Browse Source

Merge remote-tracking branch 'upstream/master' into tiff-codec

pull/119/head
James Jackson-South 8 years ago
parent
commit
bc7bafe968
  1. 13
      .github/ISSUE_TEMPLATE/ask-question.md
  2. 6
      .github/ISSUE_TEMPLATE/bug-report.md
  3. 13
      .github/ISSUE_TEMPLATE/feature-request.md
  4. 4
      ImageSharp.sln
  5. 25
      README.md
  6. 2
      appveyor.yml
  7. 2
      build.ps1
  8. 4
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  9. 210
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  10. 189
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  11. 223
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  12. 143
      src/ImageSharp/ColorSpaces/CieLab.cs
  13. 151
      src/ImageSharp/ColorSpaces/CieLch.cs
  14. 149
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  15. 146
      src/ImageSharp/ColorSpaces/CieLuv.cs
  16. 117
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  17. 132
      src/ImageSharp/ColorSpaces/CieXyy.cs
  18. 132
      src/ImageSharp/ColorSpaces/CieXyz.cs
  19. 144
      src/ImageSharp/ColorSpaces/Cmyk.cs
  20. 36
      src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
  21. 38
      src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
  22. 36
      src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
  23. 35
      src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
  24. 89
      src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
  25. 105
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  26. 324
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  27. 317
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  28. 321
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  29. 294
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  30. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  31. 305
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  32. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  33. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  34. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  35. 285
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  36. 290
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  37. 280
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  38. 280
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  39. 260
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  40. 114
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
  41. 55
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
  42. 21
      src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs
  43. 22
      src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs
  44. 49
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs
  45. 12
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
  46. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
  47. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
  48. 12
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
  49. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
  50. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
  51. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
  52. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
  53. 45
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
  54. 19
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
  55. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
  56. 21
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
  57. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
  58. 51
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
  59. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
  60. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
  61. 16
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  62. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
  63. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
  64. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
  65. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
  66. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
  67. 97
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
  68. 21
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  69. 46
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs
  70. 35
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs
  71. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs
  72. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs
  73. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs
  74. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs
  75. 98
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  76. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs
  77. 66
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
  78. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
  79. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
  80. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
  81. 83
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
  82. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
  83. 56
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  84. 125
      src/ImageSharp/ColorSpaces/Hsl.cs
  85. 181
      src/ImageSharp/ColorSpaces/Hsv.cs
  86. 142
      src/ImageSharp/ColorSpaces/HunterLab.cs
  87. 28
      src/ImageSharp/ColorSpaces/IAlmostEquatable.cs
  88. 18
      src/ImageSharp/ColorSpaces/IColorVector.cs
  89. 37
      src/ImageSharp/ColorSpaces/ICompanding.cs
  90. 2
      src/ImageSharp/ColorSpaces/Illuminants.cs
  91. 163
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  92. 134
      src/ImageSharp/ColorSpaces/Lms.cs
  93. 172
      src/ImageSharp/ColorSpaces/Rgb.cs
  94. 43
      src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
  95. 127
      src/ImageSharp/ColorSpaces/YCbCr.cs
  96. 45
      src/ImageSharp/Common/Extensions/ComparableExtensions.cs
  97. 30
      src/ImageSharp/Common/Extensions/EncoderExtensions.cs
  98. 113
      src/ImageSharp/Common/Extensions/Vector4Extensions.cs
  99. 124
      src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs
  100. 44
      src/ImageSharp/Common/Helpers/Guard.cs

13
.github/ISSUE_TEMPLATE/ask-question.md

@ -0,0 +1,13 @@
---
name: Ask question
about: Ask a question about this project.
---
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General

6
.github/ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE/bug-report.md

@ -1,3 +1,9 @@
---
name: Bug report
about: Create a report to help us improve
---
### Prerequisites ### Prerequisites
- [ ] I have written a descriptive issue title - [ ] I have written a descriptive issue title

13
.github/ISSUE_TEMPLATE/feature-request.md

@ -0,0 +1,13 @@
---
name: Feature request
about: Suggest an idea for this project
---
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General

4
ImageSharp.sln

@ -7,13 +7,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.editorconfig = .editorconfig .editorconfig = .editorconfig
.travis.yml = .travis.yml .travis.yml = .travis.yml
appveyor.yml = appveyor.yml appveyor.yml = appveyor.yml
.github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md
.github\ISSUE_TEMPLATE\bug-report.md = .github\ISSUE_TEMPLATE\bug-report.md
codecov.yml = codecov.yml codecov.yml = codecov.yml
CodeCoverage.runsettings = CodeCoverage.runsettings CodeCoverage.runsettings = CodeCoverage.runsettings
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md .github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md features.md = features.md
ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
.github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
NuGet.config = NuGet.config NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md README.md = README.md

25
README.md

@ -22,7 +22,7 @@ Designed to democratize image processing, ImageSharp brings you an incredibly po
Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments.
Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. Built against .NET Standard 1.3 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
### Documentation ### Documentation
For all SixLabors projects, including ImageSharp: For all SixLabors projects, including ImageSharp:
@ -70,6 +70,9 @@ Our API is designed to be simple to consume. Here's an example of the code requi
On platforms supporting netstandard 1.3+ On platforms supporting netstandard 1.3+
```csharp ```csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
// Image.Load(string path) is a shortcut for our default type. // Image.Load(string path) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(string path)) // Other pixel formats use Image.Load<TPixel>(string path))
using (Image<Rgba32> image = Image.Load("foo.jpg")) using (Image<Rgba32> image = Image.Load("foo.jpg"))
@ -80,25 +83,13 @@ using (Image<Rgba32> image = Image.Load("foo.jpg"))
image.Save("bar.jpg"); // Automatic encoder selected based on extension. image.Save("bar.jpg"); // Automatic encoder selected based on extension.
} }
``` ```
On netstandard 1.1 - 1.2
```csharp
// Image.Load(Stream stream) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(Stream stream))
using (FileStream stream = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
using (Image<Rgba32> image = Image.Load(stream))
{
image.Mutate(x => x
.Resize(image.Width / 2, image.Height / 2)
.Grayscale());
image.Save(output);
}
```
Setting individual pixel values can be performed as follows: Setting individual pixel values can be performed as follows:
```csharp ```csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
// Individual pixels // Individual pixels
using (Image<Rgba32> image = new Image<Rgba32>(400, 400)) using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
{ {
@ -124,7 +115,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!)
Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**: Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:
- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) - [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp)
- [.Net Core](https://www.microsoft.com/net/core#linuxubuntu) - [.NET Core](https://www.microsoft.com/net/core#linuxubuntu)
To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands. To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands.

2
appveyor.yml

@ -1,5 +1,5 @@
version: 1.0.0.{build} version: 1.0.0.{build}
image: Previous Visual Studio 2017 image: Visual Studio 2017
# prevent the double build when a branch has an active PR # prevent the double build when a branch has an active PR
skip_branch_with_pr: true skip_branch_with_pr: true

2
build.ps1

@ -94,7 +94,7 @@ if("$env:APPVEYOR_API_URL" -ne ""){
} }
Write-Host "Building version '${version}'" Write-Host "Building version '${version}'"
dotnet restore /p:packageversion=$version dotnet restore /p:packageversion=$version /p:DisableImplicitNuGetFallbackFolder=true
Write-Host "Building projects" Write-Host "Building projects"
dotnet build -c Release /p:packageversion=$version dotnet build -c Release /p:packageversion=$version

4
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -5,8 +5,8 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix> <VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix> <VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>SixLabors and contributors</Authors> <Authors>SixLabors and contributors</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<LangVersion>7.2</LangVersion> <LangVersion>7.3</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName> <AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>

210
src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -1,121 +1,137 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary> using SixLabors.ImageSharp.PixelFormats;
/// Draws the given image together with the current one by blending their pixels. using SixLabors.ImageSharp.Processing.Processors.Drawing;
/// </summary> using SixLabors.Primitives;
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> namespace SixLabors.ImageSharp.Processing
/// <param name="image">The image to blend with the currently processing image.</param> {
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param> /// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param> /// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, alphaComposition, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param> /// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, GraphicsOptions options) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param> /// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param> /// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param> /// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, alphaComposition, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, alphaComposition, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param> /// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, GraphicsOptions options) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); where TPixelSrc : struct, IPixel<TPixelSrc>
} => source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
}
} }

189
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -1,98 +1,101 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> {
/// Combines two images together by blending the pixels. /// <summary>
/// </summary> /// Combines two images together by blending the pixels.
/// <typeparam name="TPixel">The pixel format.</typeparam> /// </summary>
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel> /// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
where TPixel : struct, IPixel<TPixel> /// <typeparam name="TPixelSrc">The pixel format of source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// </summary> /// </summary>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param> /// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param> /// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixel> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image; this.Image = image;
this.Opacity = opacity; this.Opacity = opacity;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location; this.Location = location;
} }
/// <summary> /// <summary>
/// Gets the image to blend /// Gets the image to blend
/// </summary> /// </summary>
public Image<TPixel> Image { get; } public Image<TPixelSrc> Image { get; }
/// <summary> /// <summary>
/// Gets the opacity of the image to blend /// Gets the opacity of the image to blend
/// </summary> /// </summary>
public float Opacity { get; } public float Opacity { get; }
/// <summary> /// <summary>
/// Gets the pixel blender /// Gets the pixel blender
/// </summary> /// </summary>
public PixelBlender<TPixel> Blender { get; } public PixelBlender<TPixelDst> Blender { get; }
/// <summary> /// <summary>
/// Gets the location to draw the blended image /// Gets the location to draw the blended image
/// </summary> /// </summary>
public Point Location { get; } public Point Location { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{ {
Image<TPixel> targetImage = this.Image; Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixel> blender = this.Blender; PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y; int locationY = this.Location.Y;
// Align start/end positions. // Align start/end positions.
Rectangle bounds = targetImage.Bounds(); Rectangle bounds = targetImage.Bounds();
int minX = Math.Max(this.Location.X, sourceRectangle.X); int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
int targetX = minX - this.Location.X; int targetX = minX - this.Location.X;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX; int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
using (IMemoryOwner<float> amount = memoryAllocator.Allocate<float>(width)) var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
{
amount.GetSpan().Fill(this.Opacity); ParallelHelper.IterateRows(
workingRect,
ParallelFor.WithConfiguration( configuration,
minY, rows =>
maxY, {
configuration, for (int y = rows.Min; y < rows.Max; y++)
y => {
{ Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width); Span<TPixelSrc> foreground =
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend(memoryAllocator, background, background, foreground, amount.GetSpan()); blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
}); }
} });
} }
} }
} }

223
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -1,107 +1,116 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> {
/// Using the brush as a source of pixels colors blends the brush color with source. /// <summary>
/// </summary> /// Using the brush as a source of pixels colors blends the brush color with source.
/// <typeparam name="TPixel">The pixel format.</typeparam> /// </summary>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel> /// <typeparam name="TPixel">The pixel format.</typeparam>
where TPixel : struct, IPixel<TPixel> internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
{ where TPixel : struct, IPixel<TPixel>
/// <summary> {
/// The brush. /// <summary>
/// </summary> /// The brush.
private readonly IBrush<TPixel> brush; /// </summary>
private readonly GraphicsOptions options; private readonly IBrush<TPixel> brush;
private readonly GraphicsOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class. /// <summary>
/// </summary> /// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class.
/// <param name="brush">The brush to source pixel colors from.</param> /// </summary>
/// <param name="options">The options</param> /// <param name="brush">The brush to source pixel colors from.</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options) /// <param name="options">The options</param>
{ public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
this.brush = brush; {
this.options = options; this.brush = brush;
} this.options = options;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) /// <inheritdoc/>
{ protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
int startX = sourceRectangle.X; {
int endX = sourceRectangle.Right; int startX = sourceRectangle.X;
int startY = sourceRectangle.Y; int endX = sourceRectangle.Right;
int endY = sourceRectangle.Bottom; int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
// Align start/end positions.
int minX = Math.Max(0, startX); // Align start/end positions.
int maxX = Math.Min(source.Width, endX); int minX = Math.Max(0, startX);
int minY = Math.Max(0, startY); int maxX = Math.Min(source.Width, endX);
int maxY = Math.Min(source.Height, endY); int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
int width = maxX - minX;
int width = maxX - minX;
// If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)) var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
{
ParallelFor.WithConfiguration( // If there's no reason for blending, then avoid it.
minY, if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
maxY, {
configuration, ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
y =>
{ ParallelHelper.IterateRows(
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); workingRect,
}); parallelSettings,
} rows =>
else {
{ for (int y = rows.Min; y < rows.Max; y++)
// Reset offset if necessary. {
if (minX > 0) source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
{ }
startX = 0; });
} }
else
if (minY > 0) {
{ // Reset offset if necessary.
startY = 0; if (minX > 0)
} {
startX = 0;
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width)) }
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source, if (minY > 0)
sourceRectangle, {
this.options)) startY = 0;
{ }
amount.GetSpan().Fill(1f);
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
ParallelFor.WithConfiguration( using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
minY, source,
maxY, sourceRectangle,
configuration, this.options))
y => {
{ amount.GetSpan().Fill(1f);
int offsetY = y - startY;
int offsetX = minX - startX; ParallelHelper.IterateRows(
workingRect,
applicator.Apply(amount.GetSpan(), offsetX, offsetY); configuration,
}); rows =>
} {
} for (int y = rows.Min; y < rows.Max; y++)
} {
int offsetY = y - startY;
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush) int offsetX = minX - startX;
{
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
}
});
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{
solidBrush = this.brush as SolidBrush<TPixel>; solidBrush = this.brush as SolidBrush<TPixel>;
if (solidBrush == null) if (solidBrush == null)
@ -109,7 +118,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
return false; return false;
} }
return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color); return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color);
} }
} }
} }

143
src/ImageSharp/ColorSpaces/CieLab.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents a CIE L*a*b* 1976 color. /// Represents a CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/> /// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary> /// </summary>
internal readonly struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float> public readonly struct CieLab : IEquatable<CieLab>
{ {
/// <summary> /// <summary>
/// D50 standard illuminant. /// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly CieXyz WhitePoint;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct. /// Initializes a new instance of the <see cref="CieLab"/> struct.
@ -32,9 +49,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param> /// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param> /// <param name="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLab(float l, float a, float b) public CieLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint) : this(l, a, b, DefaultWhitePoint)
{ {
} }
@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param> /// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param> /// <param name="b">The b (blue - yellow) component.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLab(float l, float a, float b, CieXyz whitePoint) public CieLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint) : this(new Vector3(l, a, b), whitePoint)
{ {
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param> /// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLab(Vector3 vector) public CieLab(Vector3 vector)
: this(vector, DefaultWhitePoint) : this(vector, DefaultWhitePoint)
{ {
@ -67,126 +84,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param> /// <param name="vector">The vector representing the l, a, b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLab(Vector3 vector, CieXyz whitePoint) public CieLab(Vector3 vector, CieXyz whitePoint)
: this() : this()
{ {
this.backingVector = vector; // Not clamping as documentation about this space only indicates "usual" ranges
this.L = vector.X;
this.A = vector.Y;
this.B = vector.Z;
this.WhitePoint = whitePoint; this.WhitePoint = whitePoint;
} }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a color component.
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public float A
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the b color component.
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieLab"/> objects for equality. /// Compares two <see cref="CieLab"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// The <see cref="CieLab"/> on the left side of the operand. /// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLab left, CieLab right) public static bool operator ==(CieLab left, CieLab right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality /// Compares two <see cref="CieLab"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// The <see cref="CieLab"/> on the left side of the operand. /// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLab left, CieLab right) public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
{
return this.Equals(default)
? "CieLab [Empty]"
: $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieLab other && this.Equals(other);
{
return obj is CieLab other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLab other) public bool Equals(CieLab other)
{ {
return this.backingVector.Equals(other.backingVector) return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint); && this.WhitePoint.Equals(other.WhitePoint);
} }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLab other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
} }
} }

151
src/ImageSharp/ColorSpaces/CieLch.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -12,8 +11,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/> /// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </summary> /// </summary>
internal readonly struct CieLch : IColorVector, IEquatable<CieLch>, IAlmostEquatable<CieLch, float> public readonly struct CieLch : IEquatable<CieLch>
{ {
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
/// <summary> /// <summary>
/// D50 standard illuminant. /// D50 standard illuminant.
/// Used when reference white is not specified explicitly. /// Used when reference white is not specified explicitly.
@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieLch"/> struct. /// Initializes a new instance of the <see cref="CieLch"/> struct.
@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param> /// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param> /// <param name="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLch(float l, float c, float h) public CieLch(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint) : this(l, c, h, DefaultWhitePoint)
{ {
} }
@ -45,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param> /// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param> /// <param name="h">The hue in degrees.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLch(float l, float c, float h, CieXyz whitePoint) public CieLch(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint) : this(new Vector3(l, c, h), whitePoint)
{ {
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param> /// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLch(Vector3 vector) public CieLch(Vector3 vector)
: this(vector, DefaultWhitePoint) : this(vector, DefaultWhitePoint)
{ {
@ -67,129 +87,64 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param> /// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLch(Vector3 vector, CieXyz whitePoint) public CieLch(Vector3 vector, CieXyz whitePoint)
: this()
{ {
this.backingVector = vector; vector = Vector3.Clamp(vector, Min, Max);
this.L = vector.X;
this.C = vector.Y;
this.H = vector.Z;
this.WhitePoint = whitePoint; this.WhitePoint = whitePoint;
} }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieLch"/> objects for equality. /// Compares two <see cref="CieLch"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// The <see cref="CieLch"/> on the left side of the operand. /// <param name="right">The <see cref="CieLch"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLch left, CieLch right) public static bool operator ==(CieLch left, CieLch right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieLch"/> objects for inequality /// Compares two <see cref="CieLch"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// The <see cref="CieLch"/> on the left side of the operand. /// <param name="right">The <see cref="CieLch"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLch left, CieLch right) public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
{
return this.Equals(default)
? "CieLch [Empty]"
: $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]";
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieLch other && this.Equals(other);
{
return obj is CieLch other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLch other) public bool Equals(CieLch other)
{ {
return this.backingVector.Equals(other.backingVector) return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint); && this.WhitePoint.Equals(other.WhitePoint);
} }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLch other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
/// <summary> /// <summary>
/// Computes the saturation of the color (chroma normalized by lightness) /// Computes the saturation of the color (chroma normalized by lightness)
/// </summary> /// </summary>
@ -197,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100. /// A value ranging from 0 to 100.
/// </remarks> /// </remarks>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public float Saturation() public float Saturation()
{ {
float result = 100 * (this.C / this.L); float result = 100 * (this.C / this.L);

149
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -10,10 +9,13 @@ namespace SixLabors.ImageSharp.ColorSpaces
{ {
/// <summary> /// <summary>
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CieLchuv_or_CIEHLC"/> /// <see href="https://en.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </summary> /// </summary>
internal readonly struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float> public readonly struct CieLchuv : IEquatable<CieLchuv>
{ {
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
/// <summary> /// <summary>
/// D50 standard illuminant. /// D50 standard illuminant.
/// Used when reference white is not specified explicitly. /// Used when reference white is not specified explicitly.
@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieLchuv"/> struct. /// Initializes a new instance of the <see cref="CieLchuv"/> struct.
@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param> /// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param> /// <param name="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(float l, float c, float h) public CieLchuv(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint) : this(l, c, h, DefaultWhitePoint)
{ {
} }
@ -45,9 +65,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param> /// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param> /// <param name="h">The hue in degrees.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(float l, float c, float h, CieXyz whitePoint) public CieLchuv(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint) : this(new Vector3(l, c, h), whitePoint)
{ {
} }
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param> /// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(Vector3 vector) public CieLchuv(Vector3 vector)
: this(vector, DefaultWhitePoint) : this(vector, DefaultWhitePoint)
{ {
@ -67,127 +87,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param> /// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(Vector3 vector, CieXyz whitePoint) public CieLchuv(Vector3 vector, CieXyz whitePoint)
: this() : this()
{ {
this.backingVector = vector; vector = Vector3.Clamp(vector, Min, Max);
this.L = vector.X;
this.C = vector.Y;
this.H = vector.Z;
this.WhitePoint = whitePoint; this.WhitePoint = whitePoint;
} }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieLchuv"/> objects for equality. /// Compares two <see cref="CieLchuv"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// The <see cref="CieLchuv"/> on the left side of the operand. /// <param name="right">The <see cref="CieLchuv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator ==(CieLchuv left, CieLchuv right) public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieLchuv"/> objects for inequality /// Compares two <see cref="CieLchuv"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// The <see cref="CieLchuv"/> on the left side of the operand. /// <param name="right">The <see cref="CieLchuv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator !=(CieLchuv left, CieLchuv right) public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
{
return this.Equals(default)
? "CieLchuv [Empty]"
: $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieLchuv other && this.Equals(other);
{
return obj is CieLchuv other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLchuv other) public bool Equals(CieLchuv other)
{ {
return this.backingVector.Equals(other.backingVector) return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint); && this.WhitePoint.Equals(other.WhitePoint);
} }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLchuv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
/// <summary> /// <summary>
/// Computes the saturation of the color (chroma normalized by lightness) /// Computes the saturation of the color (chroma normalized by lightness)
/// </summary> /// </summary>
@ -195,7 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100. /// A value ranging from 0 to 100.
/// </remarks> /// </remarks>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public float Saturation() public float Saturation()
{ {
float result = 100 * (this.C / this.L); float result = 100 * (this.C / this.L);

146
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// attempted perceptual uniformity /// attempted perceptual uniformity
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/> /// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
/// </summary> /// </summary>
internal readonly struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float> public readonly struct CieLuv : IEquatable<CieLuv>
{ {
/// <summary> /// <summary>
/// D65 standard illuminant. /// D65 standard illuminant.
@ -23,9 +22,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the lightness dimension
/// <remarks>A value usually ranging between 0 and 100.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float U;
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float V;
/// <summary>
/// Gets the reference white point of this color
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly CieXyz WhitePoint;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieLuv"/> struct. /// Initializes a new instance of the <see cref="CieLuv"/> struct.
@ -34,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param> /// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param> /// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(float l, float u, float v) public CieLuv(float l, float u, float v)
: this(new Vector3(l, u, v), DefaultWhitePoint) : this(l, u, v, DefaultWhitePoint)
{ {
} }
@ -47,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param> /// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param> /// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(float l, float u, float v, CieXyz whitePoint) public CieLuv(float l, float u, float v, CieXyz whitePoint)
: this(new Vector3(l, u, v), whitePoint) : this(new Vector3(l, u, v), whitePoint)
{ {
@ -58,7 +75,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param> /// <param name="vector">The vector representing the l, u, v components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(Vector3 vector) public CieLuv(Vector3 vector)
: this(vector, DefaultWhitePoint) : this(vector, DefaultWhitePoint)
{ {
@ -69,126 +86,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param> /// <param name="vector">The vector representing the l, u, v components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(Vector3 vector, CieXyz whitePoint) public CieLuv(Vector3 vector, CieXyz whitePoint)
: this()
{ {
this.backingVector = vector; // Not clamping as documentation about this space only indicates "usual" ranges
this.L = vector.X;
this.U = vector.Y;
this.V = vector.Z;
this.WhitePoint = whitePoint; this.WhitePoint = whitePoint;
} }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension
/// <remarks>A value usually ranging between 0 and 100.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public float U
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public float V
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieLuv"/> objects for equality. /// Compares two <see cref="CieLuv"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// The <see cref="CieLuv"/> on the left side of the operand. /// <param name="right">The <see cref="CieLuv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLuv left, CieLuv right) public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieLuv"/> objects for inequality. /// Compares two <see cref="CieLuv"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// The <see cref="CieLuv"/> on the left side of the operand. /// <param name="right">The <see cref="CieLuv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLuv left, CieLuv right) public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.U.GetHashCode());
hash = HashHelpers.Combine(hash, this.V.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");
{
return this.Equals(default)
? "CieLuv [ Empty ]"
: $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieLuv other && this.Equals(other);
{
return obj is CieLuv other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLuv other) public bool Equals(CieLuv other)
{ {
return this.backingVector.Equals(other.backingVector) return this.L.Equals(other.L)
&& this.WhitePoint.Equals(other.WhitePoint); && this.U.Equals(other.U)
} && this.V.Equals(other.V)
&& this.WhitePoint.Equals(other.WhitePoint);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLuv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

117
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
// ReSharper disable CompareOfFloatsByEqualityOperator // ReSharper disable CompareOfFloatsByEqualityOperator
@ -12,45 +10,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary> /// <summary>
/// Represents the coordinates of CIEXY chromaticity space /// Represents the coordinates of CIEXY chromaticity space
/// </summary> /// </summary>
internal readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>, IAlmostEquatable<CieXyChromaticityCoordinates, float> public readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>
{ {
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector2 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyChromaticityCoordinates(float x, float y)
: this(new Vector2(x, y))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="vector">The vector containing the XY Chromaticity coordinates</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyChromaticityCoordinates(Vector2 vector)
{
this.backingVector = vector;
}
/// <summary> /// <summary>
/// Gets the chromaticity X-coordinate. /// Gets the chromaticity X-coordinate.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Ranges usually from 0 to 1. /// Ranges usually from 0 to 1.
/// </remarks> /// </remarks>
public float X public readonly float X;
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary> /// <summary>
/// Gets the chromaticity Y-coordinate /// Gets the chromaticity Y-coordinate
@ -58,85 +26,54 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks> /// <remarks>
/// Ranges usually from 0 to 1. /// Ranges usually from 0 to 1.
/// </remarks> /// </remarks>
public float Y public readonly float Y;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyChromaticityCoordinates(float x, float y)
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] this.X = x;
get => this.backingVector.Y; this.Y = y;
} }
/// <summary> /// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality. /// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality /// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
{
return this.backingVector.GetHashCode();
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
{
return this.Equals(default)
? "CieXyChromaticityCoordinates [Empty]"
: $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other);
{
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyChromaticityCoordinates other) public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
{
// The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443
return this.X == other.X && this.Y == other.Y;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyChromaticityCoordinates other, float precision)
{
var result = Vector2.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision;
}
} }
} }

132
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE xyY 1931 color /// Represents an CIE xyY 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space"/> /// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space"/>
/// </summary> /// </summary>
internal readonly struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float> public readonly struct CieXyy : IEquatable<CieXyy>
{ {
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float Yl;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct. /// Initializes a new instance of the <see cref="CieXyy"/> struct.
@ -25,130 +37,72 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="x">The x chroma component.</param> /// <param name="x">The x chroma component.</param>
/// <param name="y">The y chroma component.</param> /// <param name="y">The y chroma component.</param>
/// <param name="yl">The y luminance component.</param> /// <param name="yl">The y luminance component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieXyy(float x, float y, float yl) public CieXyy(float x, float y, float yl)
: this(new Vector3(x, y, yl))
{ {
// Not clamping as documentation about this space only indicates "usual" ranges
this.X = x;
this.Y = y;
this.Yl = yl;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct. /// Initializes a new instance of the <see cref="CieXyy"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the x, y, Y components.</param> /// <param name="vector">The vector representing the x, y, Y components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieXyy(Vector3 vector) public CieXyy(Vector3 vector)
: this() : this()
{ {
// Not clamping as documentation about this space seems to indicate "usual" ranges // Not clamping as documentation about this space only indicates "usual" ranges
this.backingVector = vector; this.X = vector.X;
this.Y = vector.Y;
this.Yl = vector.Z;
} }
/// <summary>
/// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Yl
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieXyy"/> objects for equality. /// Compares two <see cref="CieXyy"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// The <see cref="CieXyy"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyy"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyy left, CieXyy right) public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieXyy"/> objects for inequality. /// Compares two <see cref="CieXyy"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// The <see cref="CieXyy"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyy"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyy left, CieXyy right) public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Yl.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})");
{
return this.Equals(default)
? "CieXyy [ Empty ]"
: $"CieXyy [ X={this.X:#0.##}, Y={this.Y:#0.##}, Yl={this.Yl:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieXyy other && this.Equals(other);
{
return obj is CieXyy other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyy other) public bool Equals(CieXyy other)
{ {
return this.backingVector.Equals(other.backingVector); return this.X.Equals(other.X)
} && this.Y.Equals(other.Y)
&& this.Yl.Equals(other.Yl);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyy other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

132
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE XYZ 1931 color /// Represents an CIE XYZ 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#Definition_of_the_CIE_XYZ_color_space"/> /// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#Definition_of_the_CIE_XYZ_color_space"/>
/// </summary> /// </summary>
internal readonly struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float> public readonly struct CieXyz : IEquatable<CieXyz>
{ {
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float Y;
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Z;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct. /// Initializes a new instance of the <see cref="CieXyz"/> struct.
@ -25,7 +37,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param> /// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
/// <param name="y">The y luminance component.</param> /// <param name="y">The y luminance component.</param>
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param> /// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public CieXyz(float x, float y, float z) public CieXyz(float x, float y, float z)
: this(new Vector3(x, y, z)) : this(new Vector3(x, y, z))
{ {
@ -38,116 +50,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieXyz(Vector3 vector) public CieXyz(Vector3 vector)
: this() : this()
{ {
// Not clamping as documentation about this space seems to indicate "usual" ranges // Not clamping as documentation about this space only indicates "usual" ranges
this.backingVector = vector; this.X = vector.X;
} this.Y = vector.Y;
this.Z = vector.Z;
/// <summary>
/// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
} }
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Z
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality. /// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// The <see cref="CieXyz"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyz"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyz left, CieXyz right) public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="CieXyz"/> objects for inequality. /// Compares two <see cref="CieXyz"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// The <see cref="CieXyz"/> on the left side of the operand. /// <param name="right">The <see cref="CieXyz"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyz left, CieXyz right) public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right);
{
return !left.Equals(right); /// <summary>
} /// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Z.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})");
{
return this.Equals(default)
? "CieXyz [ Empty ]"
: $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is CieXyz other && this.Equals(other);
{
return obj is CieXyz other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyz other) public bool Equals(CieXyz other)
{ {
return this.backingVector.Equals(other.backingVector); return this.X.Equals(other.X)
} && this.Y.Equals(other.Y)
&& this.Z.Equals(other.Z);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyz other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

144
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -11,151 +10,108 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary> /// <summary>
/// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// Represents an CMYK (cyan, magenta, yellow, keyline) color.
/// </summary> /// </summary>
internal readonly struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float> public readonly struct Cmyk : IEquatable<Cmyk>
{ {
/// <summary> private static readonly Vector4 Min = Vector4.Zero;
/// The backing vector for SIMD support. private static readonly Vector4 Max = Vector4.One;
/// </summary>
private readonly Vector4 backingVector;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct. /// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
/// <param name="c">The cyan component.</param> public readonly float C;
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The keyline black component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk(float c, float m, float y, float k)
: this(new Vector4(c, m, y, k))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct. /// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
/// <param name="vector">The vector representing the c, m, y, k components.</param> public readonly float M;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk(Vector4 vector)
: this()
{
this.backingVector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
}
/// <summary> /// <summary>
/// Gets the cyan color component. /// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks> /// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float C public readonly float Y;
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary> /// <summary>
/// Gets the magenta color component. /// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks> /// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float M public readonly float K;
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary> /// <summary>
/// Gets the yellow color component. /// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float Y /// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The keyline black component.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(float c, float m, float y, float k)
: this(new Vector4(c, m, y, k))
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
} }
/// <summary> /// <summary>
/// Gets the keyline black color component. /// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float K /// <param name="vector">The vector representing the c, m, y, k components.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(Vector4 vector)
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] vector = Vector4.Clamp(vector, Min, Max);
get => this.backingVector.W; this.C = vector.X;
this.M = vector.Y;
this.Y = vector.Z;
this.K = vector.W;
} }
/// <summary> /// <summary>
/// Compares two <see cref="Cmyk"/> objects for equality. /// Compares two <see cref="Cmyk"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Cmyk"/> on the left side of the operand.</param>
/// The <see cref="Cmyk"/> on the left side of the operand. /// <param name="right">The <see cref="Cmyk"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Cmyk left, Cmyk right) public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="Cmyk"/> objects for inequality /// Compares two <see cref="Cmyk"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Cmyk"/> on the left side of the operand.</param>
/// The <see cref="Cmyk"/> on the left side of the operand. /// <param name="right">The <see cref="Cmyk"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Cmyk left, Cmyk right) public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.C.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.K.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})");
{
return this.Equals(default)
? "Cmyk [Empty]"
: $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is Cmyk other && this.Equals(other);
{
return obj is Cmyk other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Cmyk other) public bool Equals(Cmyk other)
{ {
return this.backingVector.Equals(other.backingVector); return this.C.Equals(other.C)
} && this.M.Equals(other.M)
&& this.Y.Equals(other.Y)
/// <inheritdoc/> && this.K.Equals(other.K);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Cmyk other, float precision)
{
var result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision
&& result.W <= precision;
} }
} }
} }

36
src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
/// <summary>
/// Implements gamma companding
/// </summary>
/// <remarks>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public static class GammaCompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <param name="gamma">The gamma value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel, float gamma) => MathF.Pow(channel, gamma);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <param name="gamma">The gamma value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel, float gamma) => MathF.Pow(channel, 1 / gamma);
}
}

38
src/ImageSharp/ColorSpaces/Companding/LCompanding.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
/// <summary>
/// Implements L* companding
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public static class LCompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
}
}

36
src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
/// <summary>
/// Implements Rec. 2020 companding function (for 12-bits).
/// </summary>
/// <remarks>
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
/// </remarks>
public static class Rec2020Companding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F;
}
}

35
src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
/// <summary>
/// Implements the Rec. 709 companding function.
/// </summary>
/// <remarks>
/// http://en.wikipedia.org/wiki/Rec._709
/// </remarks>
public static class Rec709Companding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F;
}
}

89
src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs

@ -0,0 +1,89 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
/// <summary>
/// Implements sRGB companding
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public static class SRgbCompanding
{
/// <summary>
/// Expands the companded vectors to their linear equivalents with respect to the energy.
/// </summary>
/// <param name="vectors">The span of vectors.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Expand(Span<Vector4> vectors)
{
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Expand(v.X);
v.Y = Expand(v.Y);
v.Z = Expand(v.Z);
}
}
/// <summary>
/// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy.
/// </summary>
/// <param name="vectors">The span of vectors.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Compress(Span<Vector4> vectors)
{
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Compress(v.X);
v.Y = Compress(v.Y);
v.Z = Compress(v.Z);
}
}
/// <summary>
/// Expands a companded vector to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the linear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W);
/// <summary>
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the nonlinear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W);
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
}
}

105
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -1,151 +1,130 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Performs chromatic adaptation on the various color spaces. /// Performs chromatic adaptation on the various color spaces.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color. /// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="WhitePoint"/>. /// Target white point is <see cref="ColorSpaceConverterOptions.WhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <param name="sourceWhitePoint">The white point to adapt for</param> /// <param name="sourceWhitePoint">The source white point.</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) => this.Adapt(color, sourceWhitePoint, this.whitePoint);
/// <summary>
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="ColorSpaceConverterOptions.WhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="targetWhitePoint">The target white point.</param>
/// <returns>The adapted color</returns>
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint))
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); return color;
} }
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); return this.chromaticAdaptation.Transform(color, sourceWhitePoint, targetWhitePoint);
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieLab Adapt(in CieLab color) public CieLab Adapt(in CieLab color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
{ {
return color; return color;
} }
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieLch Adapt(in CieLch color) public CieLch Adapt(in CieLch color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
{ {
return color; return color;
} }
CieLab labColor = this.ToCieLab(color); var labColor = this.ToCieLab(color);
return this.ToCieLch(labColor); return this.ToCieLch(labColor);
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieLchuv Adapt(in CieLchuv color) public CieLchuv Adapt(in CieLchuv color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
{ {
return color; return color;
} }
CieLuv luvColor = this.ToCieLuv(color); var luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(luvColor); return this.ToCieLchuv(luvColor);
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="TargetLuvWhitePoint"/>. /// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLuvWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieLuv Adapt(in CieLuv color) public CieLuv Adapt(in CieLuv color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint))
{ {
return color; return color;
} }
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary> /// <summary>
/// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="TargetHunterLabWhitePoint"/>. /// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetHunterLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public HunterLab Adapt(in HunterLab color) public HunterLab Adapt(in HunterLab color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint))
{ {
return color; return color;
} }
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>. /// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public LinearRgb Adapt(in LinearRgb color) public LinearRgb Adapt(in LinearRgb color)
{ {
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace))
{ {
return color; return color;
} }
@ -155,21 +134,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converterToXYZ.Convert(color); CieXyz unadapted = converterToXYZ.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); CieXyz adapted = this.chromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion back to RGB // Conversion back to RGB
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); return this.cieXyzToLinearRgbConverter.Convert(adapted);
return converterToRGB.Convert(adapted);
} }
/// <summary> /// <summary>
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>. /// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public Rgb Adapt(in Rgb color) public Rgb Adapt(in Rgb color)
{ {
LinearRgb linearInput = this.ToLinearRgb(color); if (!this.performChromaticAdaptation)
{
return color;
}
var linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput); LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput); return this.ToRgb(linearOutput);
} }

324
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -1,15 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieLab"/>. /// Allows conversion to <see cref="CieLab"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// The converter for converting between CieLch to CieLab. /// The converter for converting between CieLch to CieLab.
@ -26,15 +28,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point) // Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color); CieLab unadapted = CieLchToCieLabConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed)
{
return unadapted;
}
// Adaptation // Adaptation
return this.Adapt(unadapted); return this.Adapt(unadapted);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLab"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -42,10 +60,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLchuv color) public CieLab ToCieLab(in CieLchuv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLab"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -53,10 +93,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLuv color) public CieLab ToCieLab(in CieLuv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLab"/> /// Converts a <see cref="CieXyy"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -64,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieXyy color) public CieLab ToCieLab(in CieXyy color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/> /// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -76,13 +160,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLab ToCieLab(in CieXyz color) public CieLab ToCieLab(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint);
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
: color;
// Conversion // Conversion
var converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); return this.cieXyzToCieLabConverter.Convert(adapted);
return converter.Convert(adapted); }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
} }
/// <summary> /// <summary>
@ -92,10 +194,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Cmyk color) public CieLab ToCieLab(in Cmyk color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLab"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -103,10 +226,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsl color) public CieLab ToCieLab(in Hsl color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLab"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -114,10 +259,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsv color) public CieLab ToCieLab(in Hsv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLab"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -125,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in HunterLab color) public CieLab ToCieLab(in HunterLab color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLab"/> /// Converts a <see cref="Lms"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -136,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Lms color) public CieLab ToCieLab(in Lms color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLab"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -147,10 +357,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in LinearRgb color) public CieLab ToCieLab(in LinearRgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLab"/> /// Converts a <see cref="Rgb"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -158,10 +390,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Rgb color) public CieLab ToCieLab(in Rgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLab"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieLab"/>
/// </summary> /// </summary>
@ -169,8 +423,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in YCbCr color) public CieLab ToCieLab(in YCbCr color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
} }
} }

317
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieLch"/>. /// Allows conversion to <see cref="CieLch"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// The converter for converting between CieLab to CieLch. /// The converter for converting between CieLab to CieLch.
@ -23,12 +26,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLch ToCieLch(in CieLab color) public CieLch ToCieLch(in CieLab color)
{ {
// Adaptation // Adaptation
CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; CieLab adapted = this.Adapt(color);
// Conversion // Conversion
return CieLabToCieLchConverter.Convert(adapted); return CieLabToCieLchConverter.Convert(adapted);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLch"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -36,10 +60,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieLchuv color) public CieLch ToCieLch(in CieLchuv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLch"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -47,10 +93,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieLuv color) public CieLch ToCieLch(in CieLuv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLch"/> /// Converts a <see cref="CieXyy"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -58,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieXyy color) public CieLch ToCieLch(in CieXyy color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLch"/> /// Converts a <see cref="CieXyz"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -69,10 +159,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieXyz color) public CieLch ToCieLch(in CieXyz color)
{ {
CieLab labColor = this.ToCieLab(color); var labColor = this.ToCieLab(color);
return this.ToCieLch(labColor); return this.ToCieLch(labColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLch"/> /// Converts a <see cref="Cmyk"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -80,10 +192,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Cmyk color) public CieLch ToCieLch(in Cmyk color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLch"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -91,10 +224,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Hsl color) public CieLch ToCieLch(in Hsl color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLch"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -102,10 +257,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Hsv color) public CieLch ToCieLch(in Hsv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLch"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -113,10 +290,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in HunterLab color) public CieLch ToCieLch(in HunterLab color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLch"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -124,10 +323,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in LinearRgb color) public CieLch ToCieLch(in LinearRgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLch"/> /// Converts a <see cref="Lms"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -135,10 +356,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Lms color) public CieLch ToCieLch(in Lms color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLch"/> /// Converts a <see cref="Rgb"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -146,10 +389,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Rgb color) public CieLch ToCieLch(in Rgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLch"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieLch"/>
/// </summary> /// </summary>
@ -157,8 +422,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in YCbCr color) public CieLch ToCieLch(in YCbCr color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); return this.ToCieLch(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
} }
} }

321
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieLchuv"/>. /// Allows conversion to <see cref="CieLchuv"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// The converter for converting between CieLab to CieLchuv. /// The converter for converting between CieLab to CieLchuv.
@ -22,10 +25,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLab color) public CieLchuv ToCieLchuv(in CieLab color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="CieLch"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -33,10 +58,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLch color) public CieLchuv ToCieLchuv(in CieLch color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -45,12 +92,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLchuv ToCieLchuv(in CieLuv color) public CieLchuv ToCieLchuv(in CieLuv color)
{ {
// Adaptation // Adaptation
CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; CieLuv adapted = this.Adapt(color);
// Conversion // Conversion
return CieLuvToCieLchuvConverter.Convert(adapted); return CieLuvToCieLchuvConverter.Convert(adapted);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="CieXyy"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -58,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyy color) public CieLchuv ToCieLchuv(in CieXyy color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="CieXyz"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -69,8 +159,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyz color) public CieLchuv ToCieLchuv(in CieXyz color)
{ {
CieLab labColor = this.ToCieLab(color); var luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(labColor);
return this.ToCieLchuv(luvColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
} }
/// <summary> /// <summary>
@ -80,10 +192,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Cmyk color) public CieLchuv ToCieLchuv(in Cmyk color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -91,10 +225,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsl color) public CieLchuv ToCieLchuv(in Hsl color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -102,10 +258,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsv color) public CieLchuv ToCieLchuv(in Hsv color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -113,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in HunterLab color) public CieLchuv ToCieLchuv(in HunterLab color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -124,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in LinearRgb color) public CieLchuv ToCieLchuv(in LinearRgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="Lms"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -135,21 +357,65 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Lms color) public CieLchuv ToCieLchuv(in Lms color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="Rgb"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Rgb color) public CieLchuv ToCieLchuv(in Rgb color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLchuv"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieLchuv"/>
/// </summary> /// </summary>
@ -157,8 +423,29 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in YCbCr color) public CieLchuv ToCieLchuv(in YCbCr color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
} }
} }

294
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -1,16 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces; using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieLuv"/>. /// Allows conversion to <see cref="CieLuv"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter();
@ -25,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieLuv"/> /// Converts a <see cref="CieLch"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -36,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLuv"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -46,15 +89,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point) // Conversion (perserving white point)
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed)
{
return unadapted;
}
// Adaptation // Adaptation
return this.Adapt(unadapted); return this.Adapt(unadapted);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLuv"/> /// Converts a <see cref="CieXyy"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -66,6 +125,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLuv"/> /// Converts a <see cref="CieXyz"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -74,13 +154,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLuv ToCieLuv(in CieXyz color) public CieLuv ToCieLuv(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLuvWhitePoint);
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
: color;
// Conversion // Conversion
var converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint); return this.cieXyzToCieLuvConverter.Convert(adapted);
return converter.Convert(adapted); }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
} }
/// <summary> /// <summary>
@ -94,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLuv"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -105,6 +224,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLuv"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -116,6 +256,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLuv"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -127,6 +288,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLuv"/> /// Converts a <see cref="Lms"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -138,6 +320,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLuv"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -149,6 +352,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLuv"/> /// Converts a <see cref="Rgb"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -160,6 +384,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLuv"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieLuv"/>
/// </summary> /// </summary>
@ -170,5 +415,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor); return this.ToCieLuv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
} }
} }

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieXyy"/>. /// Allows conversion to <see cref="CieXyy"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieXyy"/> /// Converts a <see cref="CieLch"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieXyy"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyy"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -60,14 +126,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieXyy"/> /// Converts a <see cref="CieXyz"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieXyz color) public CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination, int count)
{ {
return CieXyzAndCieXyyConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
} }
/// <summary> /// <summary>
@ -82,6 +187,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyy"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -94,6 +220,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyy"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -106,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyy"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -118,6 +286,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieXyy"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -130,6 +319,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieXyy"/> /// Converts a <see cref="Lms"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -142,6 +352,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieXyy"/> /// Converts a <see cref="Rgb"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -154,6 +385,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyy"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieXyy"/>
/// </summary> /// </summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
} }
} }

305
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs

@ -1,17 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="CieXyz"/>. /// Allows conversion to <see cref="CieXyz"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter();
@ -32,11 +32,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); CieXyz unadapted = CieLabToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed return this.Adapt(unadapted, color.WhitePoint);
? unadapted }
: this.Adapt(unadapted, color.WhitePoint);
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
return adapted; for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
} }
/// <summary> /// <summary>
@ -53,6 +70,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(labColor); return this.ToCieXyz(labColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -67,6 +105,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(luvColor); return this.ToCieXyz(luvColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/> /// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -78,11 +137,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed return this.Adapt(unadapted, color.WhitePoint);
? unadapted }
: this.Adapt(unadapted, color.WhitePoint);
return adapted; /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
} }
/// <summary> /// <summary>
@ -96,6 +172,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CieXyzAndCieXyyConverter.Convert(color); return CieXyzAndCieXyyConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="CieXyz"/> /// Converts a <see cref="Cmyk"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -109,6 +206,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb); return this.ToCieXyz(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyz"/> /// Converts a <see cref="Hsl"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -122,6 +240,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb); return this.ToCieXyz(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyz"/> /// Converts a <see cref="Hsv"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -135,6 +274,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb); return this.ToCieXyz(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyz"/> /// Converts a <see cref="HunterLab"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -146,11 +306,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed return this.Adapt(unadapted, color.WhitePoint);
? unadapted }
: this.Adapt(unadapted, color.WhitePoint);
return adapted; /// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
} }
/// <summary> /// <summary>
@ -165,9 +342,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converter.Convert(color); CieXyz unadapted = converter.Convert(color);
// Adaptation // Adaptation
return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed return this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
? unadapted }
: this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
} }
/// <summary> /// <summary>
@ -178,7 +374,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieXyz ToCieXyz(in Lms color) public CieXyz ToCieXyz(in Lms color)
{ {
// Conversion // Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color); return this.cieXyzAndLmsConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
} }
/// <summary> /// <summary>
@ -193,6 +410,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(linear); return this.ToCieXyz(linear);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyz"/> /// Converts a <see cref="YCbCr"/> into a <see cref="CieXyz"/>
/// </summary> /// </summary>
@ -206,14 +444,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb); return this.ToCieXyz(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary> /// <summary>
/// Gets the correct converter for the given rgb working space. /// Gets the correct converter for the given rgb working space.
/// </summary> /// </summary>
/// <param name="workingSpace">The source working space</param> /// <param name="workingSpace">The source working space</param>
/// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns> /// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns>
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace)
{ {
if (this.linearRgbToCieXyzConverter != null && this.linearRgbToCieXyzConverter.SourceWorkingSpace.Equals(workingSpace)) if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true)
{ {
return this.linearRgbToCieXyzConverter; return this.linearRgbToCieXyzConverter;
} }

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="Cmyk"/>. /// Allows conversion to <see cref="Cmyk"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Cmyk"/> /// Converts a <see cref="CieLch"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Cmyk"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Cmyk"/> /// Converts a <see cref="CieLuv"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Cmyk"/> /// Converts a <see cref="CieXyy"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Cmyk"/> /// Converts a <see cref="CieXyz"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb); return CmykAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Cmyk"/> /// Converts a <see cref="Hsl"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb); return CmykAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Cmyk"/> /// Converts a <see cref="Hsv"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb); return CmykAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Cmyk"/> /// Converts a <see cref="HunterLab"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Cmyk"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb); return CmykAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Cmyk"/> /// Converts a <see cref="Lms"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor); return this.ToCmyk(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Cmyk"/> /// Converts a <see cref="Rgb"/> into a <see cref="Cmyk"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns> /// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Rgb color) public Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination, int count)
{ {
return CmykAndRgbConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
} }
/// <summary> /// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb); return CmykAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
} }
} }

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="Hsl"/>. /// Allows conversion to <see cref="Hsl"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Hsl"/> /// Converts a <see cref="CieLch"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsl"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsl"/> /// Converts a <see cref="CieLuv"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsl"/> /// Converts a <see cref="CieXyy"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsl"/> /// Converts a <see cref="CieXyz"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb); return HslAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsl"/> /// Converts a <see cref="Cmyk"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb); return HslAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Hsl"/> /// Converts a <see cref="Hsv"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb); return HslAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsl"/> /// Converts a <see cref="HunterLab"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsl"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb); return HslAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Hsl"/> /// Converts a <see cref="Lms"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor); return this.ToHsl(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Hsl"/> /// Converts a <see cref="Rgb"/> into a <see cref="Hsl"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns> /// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in Rgb color) public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination, int count)
{ {
return HslAndRgbConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
} }
/// <summary> /// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb); return HslAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
} }
} }

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="Hsv"/>. /// Allows conversion to <see cref="Hsv"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter(); private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Hsv"/> /// Converts a <see cref="CieLch"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsv"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsv"/> /// Converts a <see cref="CieLuv"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsv"/> /// Converts a <see cref="CieXyy"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsv"/> /// Converts a <see cref="CieXyz"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsv"/> /// Converts a <see cref="Cmyk"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Hsv"/> /// Converts a <see cref="Hsl"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsv"/> /// Converts a <see cref="HunterLab"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsv"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Hsv"/> /// Converts a <see cref="Lms"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Hsv"/> /// Converts a <see cref="Rgb"/> into a <see cref="Hsv"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in Rgb color) public Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination, int count)
{ {
return HsvAndRgbConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
} }
/// <summary> /// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
} }
} }

285
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -1,14 +1,16 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="HunterLab"/>. /// Allows conversion to <see cref="HunterLab"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
@ -21,6 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -32,6 +55,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -43,6 +87,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -54,6 +119,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -65,6 +151,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -73,12 +180,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public HunterLab ToHunterLab(in CieXyz color) public HunterLab ToHunterLab(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint);
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint)
: color;
// Conversion // Conversion
return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); return this.cieXyzToHunterLabConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
} }
/// <summary> /// <summary>
@ -92,6 +218,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/> /// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -103,6 +250,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/> /// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -114,6 +282,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -125,6 +314,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/> /// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -136,6 +346,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/> /// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -147,6 +378,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/> /// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -157,5 +409,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
} }
} }

290
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -1,19 +1,20 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="LinearRgb"/>. /// Allows conversion to <see cref="LinearRgb"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter();
private CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter;
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -25,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -36,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -47,6 +90,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -58,6 +122,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -69,6 +154,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -77,13 +183,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public LinearRgb ToLinearRgb(in CieXyz color) public LinearRgb ToLinearRgb(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint);
? color
: this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint);
// Conversion // Conversion
CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); return this.cieXyzToLinearRgbConverter.Convert(adapted);
return xyzConverter.Convert(adapted); }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
} }
/// <summary> /// <summary>
@ -97,6 +221,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb); return this.ToLinearRgb(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -108,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb); return this.ToLinearRgb(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -119,6 +285,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb); return this.ToLinearRgb(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -130,6 +317,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -141,6 +349,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor); return this.ToLinearRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -152,6 +381,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return RgbToLinearRgbConverter.Convert(color); return RgbToLinearRgbConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>
/// </summary> /// </summary>
@ -164,18 +414,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Gets the correct converter for the given rgb working space. /// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="workingSpace">The target working space</param> /// <param name="source">The span to the source colors</param>
/// <returns>The <see cref="CieXyzToLinearRgbConverter"/></returns> /// <param name="destination">The span to the destination colors</param>
private CieXyzToLinearRgbConverter GetCieXyxToLinearRgbConverter(RgbWorkingSpace workingSpace) /// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination, int count)
{ {
if (this.cieXyzToLinearRgbConverter != null && this.cieXyzToLinearRgbConverter.TargetWorkingSpace.Equals(workingSpace)) Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{ {
return this.cieXyzToLinearRgbConverter; ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
} }
return this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(workingSpace);
} }
} }
} }

280
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs

@ -1,14 +1,16 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="Lms"/>. /// Allows conversion to <see cref="Lms"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/> /// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
@ -21,6 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Lms"/> /// Converts a <see cref="CieLch"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -32,6 +55,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -43,6 +87,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/> /// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -54,6 +119,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/> /// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -65,14 +151,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/> /// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns> /// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyz color) public Lms ToLms(in CieXyz color) => this.cieXyzAndLmsConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination, int count)
{ {
return this.cachedCieXyzAndLmsConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
} }
/// <summary> /// <summary>
@ -86,6 +211,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Lms"/> /// Converts a <see cref="Hsl"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -97,6 +243,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Lms"/> /// Converts a <see cref="Hsv"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -108,6 +275,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/> /// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -119,6 +307,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -130,6 +339,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/> /// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -141,6 +371,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/> /// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -151,5 +402,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor); return this.ToLms(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
} }
} }

280
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="Rgb"/>. /// Allows conversion to <see cref="Rgb"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter();
@ -23,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -34,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -45,6 +90,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -56,6 +122,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -67,6 +154,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -81,6 +189,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(linear); return this.ToRgb(linear);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/> /// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -92,6 +221,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(color); return CmykAndRgbConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/> /// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -103,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(color); return HsvAndRgbConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/> /// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -114,6 +285,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(color); return HslAndRgbConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/> /// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -125,6 +317,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -136,6 +349,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return LinearRgbToRgbConverter.Convert(color); return LinearRgbToRgbConverter.Convert(color);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/> /// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -147,6 +381,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor); return this.ToRgb(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/> /// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>
@ -160,5 +415,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Adaptation // Adaptation
return this.Adapt(rgb); return this.Adapt(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
} }
} }

260
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <content> /// <content>
/// Allows conversion to <see cref="YCbCr"/>. /// Allows conversion to <see cref="YCbCr"/>.
/// </content> /// </content>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/> /// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -37,15 +61,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="source">The span to the source colors</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <param name="destination">The span to the destination colors</param>
public YCbCr ToYCbCr(in CieLchuv color) /// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination, int count)
{ {
var xyzColor = this.ToCieXyz(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
return this.ToYCbCr(xyzColor); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
} }
/// <summary> /// <summary>
@ -60,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/> /// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -72,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/> /// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -84,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/> /// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -96,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/> /// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -108,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/> /// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -120,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/> /// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -132,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/> /// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -144,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/> /// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
@ -156,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="YCbCr"/> /// Converts a <see cref="Rgb"/> into a <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Rgb color) public YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination, int count)
{ {
return YCbCrAndRgbConverter.Convert(color); Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
} }
} }
} }

114
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs

@ -2,100 +2,60 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <summary> /// <summary>
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. /// Provides methods to allow the conversion of color values between different color spaces.
/// </summary> /// </summary>
internal partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> // Options.
/// The default whitepoint used for converting to CieLab private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions();
/// </summary> private readonly Matrix4x4 lmsAdaptationMatrix;
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; private readonly CieXyz whitePoint;
private readonly CieXyz targetLuvWhitePoint;
private Matrix4x4 transformationMatrix; private readonly CieXyz targetLabWhitePoint;
private readonly CieXyz targetHunterLabWhitePoint;
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; private readonly RgbWorkingSpaceBase targetRgbWorkingSpace;
private readonly IChromaticAdaptation chromaticAdaptation;
private readonly bool performChromaticAdaptation;
private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter;
private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter;
private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter;
private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter;
private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class. /// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary> /// </summary>
public ColorSpaceConverter() public ColorSpaceConverter()
: this(DefaultOptions)
{ {
// Note the order here this is important.
this.WhitePoint = DefaultWhitePoint;
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix;
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter);
this.TargetLuvWhitePoint = CieLuv.DefaultWhitePoint;
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint;
this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint;
this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace;
} }
/// <summary> /// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. /// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// When null, no adaptation will be performed.
/// </summary>
public CieXyz WhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpace TargetRgbWorkingSpace { get; set; }
/// <summary>
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; }
/// <summary>
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
/// also used in the default Von Kries Chromatic Adaptation method.
/// </summary> /// </summary>
public Matrix4x4 LmsAdaptationMatrix /// <param name="options">The configuration options.</param>
public ColorSpaceConverter(ColorSpaceConverterOptions options)
{ {
get => this.transformationMatrix; Guard.NotNull(options, nameof(options));
this.whitePoint = options.WhitePoint;
set this.targetLuvWhitePoint = options.TargetLuvWhitePoint;
{ this.targetLabWhitePoint = options.TargetLabWhitePoint;
this.transformationMatrix = value; this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint;
if (this.cachedCieXyzAndLmsConverter == null) this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace;
{ this.chromaticAdaptation = options.ChromaticAdaptation;
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); this.performChromaticAdaptation = this.chromaticAdaptation != null;
} this.lmsAdaptationMatrix = options.LmsAdaptationMatrix;
else
{ this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix);
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value; this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint);
} this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint);
} this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint);
this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace);
} }
/// <summary>
/// Gets a value indicating whether chromatic adaptation has been performed.
/// </summary>
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null;
} }
} }

55
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Configuration options for the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public class ColorSpaceConverterOptions
{
/// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
/// When <value>default</value>, no adaptation will be performed.
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpaceBase TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace;
/// <summary>
/// Gets or sets the chromatic adaptation method used. When <value>null</value>, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation();
/// <summary>
/// Gets or sets transformation matrix used in conversion to and from <see cref="Lms"/>.
/// </summary>
public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix;
}
}

21
src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <summary> /// <summary>
@ -8,16 +10,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] /// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M]
/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD). /// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD).
/// </summary> /// </summary>
internal interface IChromaticAdaptation public interface IChromaticAdaptation
{ {
/// <summary> /// <summary>
/// Performs a linear transformation of a source color in to the destination color. /// Performs a linear transformation of a source color in to the destination color.
/// </summary> /// </summary>
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks> /// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
/// <param name="sourceColor">The source color.</param> /// <param name="source">The source color.</param>
/// <param name="sourceWhitePoint">The source white point.</param> /// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="targetWhitePoint">The target white point.</param> /// <param name="destinationWhitePoint">The destination white point.</param>
/// <returns>The <see cref="CieXyz"/></returns> /// <returns>The <see cref="CieXyz"/></returns>
CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint); CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint);
/// <summary>
/// Performs a bulk linear transformation of a source color in to the destination color.
/// </summary>
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="destinationWhitePoint">The destination white point.</param>
/// <param name="count">The number of colors to convert.</param>
void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count);
} }
} }

22
src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs

@ -1,22 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Converts color between two color spaces.
/// </summary>
/// <typeparam name="T">The input color type.</typeparam>
/// <typeparam name="TResult">The result color type.</typeparam>
internal interface IColorConversion<T, TResult>
where T : struct
where TResult : struct
{
/// <summary>
/// Performs the conversion from the input to an instance of the output type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
TResult Convert(in T input);
}
}

49
src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs

@ -1,49 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce
{
/// <summary>
/// Color converter between CMYK and Rgb
/// </summary>
internal class CmykAndRgbConverter : IColorConversion<Cmyk, Rgb>, IColorConversion<Rgb, Cmyk>
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(in Cmyk input)
{
float r = (1F - input.C) * (1F - input.K);
float g = (1F - input.M) * (1F - input.K);
float b = (1F - input.Y) * (1F - input.K);
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk Convert(in Rgb input)
{
// To CMYK
float c = 1F - input.R;
float m = 1F - input.G;
float y = 1F - input.B;
// To CMYK
float k = MathF.Min(c, MathF.Min(m, y));
if (MathF.Abs(k - 1F) < Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1F);
}
c = (c - k) / (1F - k);
m = (m - k) / (1F - k);
y = (y - k) / (1F - k);
return new Cmyk(c, m, y, k);
}
}
}

12
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs

@ -4,15 +4,19 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>. /// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary> /// </summary>
internal class CieLchToCieLabConverter : IColorConversion<CieLch, CieLab> internal sealed class CieLchToCieLabConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLch"/> input to an instance of <see cref="CieLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieLch input) public CieLab Convert(in CieLch input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:

14
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs

@ -4,15 +4,19 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary> /// </summary>
internal class CieLabToCieLchConverter : IColorConversion<CieLab, CieLch> internal sealed class CieLabToCieLchConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieLch"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch Convert(in CieLab input) public CieLch Convert(in CieLab input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360. // Wrap the angle round at 360.
hDegrees = hDegrees % 360; hDegrees %= 360;
// Make sure it's not negative. // Make sure it's not negative.
while (hDegrees < 0) while (hDegrees < 0)

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs

@ -1,18 +1,22 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
/// </summary> /// </summary>
internal class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz> internal sealed class CieLabToCieXyzConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieLab input) public CieXyz Convert(in CieLab input)
{ {
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
@ -21,25 +25,20 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
float fx = (a / 500F) + fy; float fx = (a / 500F) + fy;
float fz = fy - (b / 200F); float fz = fy - (b / 200F);
float fx3 = MathF.Pow(fx, 3F); float fx3 = ImageMaths.Pow3(fx);
float fz3 = MathF.Pow(fz, 3F); float fz3 = ImageMaths.Pow3(fz);
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? MathF.Pow((l + 16F) / 116F, 3F) : l / CieConstants.Kappa; float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa; float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z; var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
xr = xr.Clamp(0, 1F); var xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One);
yr = yr.Clamp(0, 1F);
zr = zr.Clamp(0, 1F);
float x = xr * wx; Vector3 xyz = xyzr * wxyz;
float y = yr * wy; return new CieXyz(xyz);
float z = zr * wz;
return new CieXyz(x, y, z);
} }
} }
} }

12
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs

@ -4,15 +4,19 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>. /// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary> /// </summary>
internal class CieLchuvToCieLuvConverter : IColorConversion<CieLchuv, CieLuv> internal sealed class CieLchuvToCieLuvConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLchuv"/> input to an instance of <see cref="CieLuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv Convert(in CieLchuv input) public CieLuv Convert(in CieLchuv input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:

14
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs

@ -4,15 +4,19 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary> /// </summary>
internal class CieLuvToCieLchuvConverter : IColorConversion<CieLuv, CieLchuv> internal sealed class CieLuvToCieLchuvConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieLchuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv Convert(in CieLuv input) public CieLchuv Convert(in CieLuv input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360. // Wrap the angle round at 360.
hDegrees = hDegrees % 360; hDegrees %= 360;
// Make sure it's not negative. // Make sure it's not negative.
while (hDegrees < 0) while (hDegrees < 0)

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs

@ -1,18 +1,20 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>. /// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
/// </summary> /// </summary>
internal class CieLuvToCieXyzConverter : IColorConversion<CieLuv, CieXyz> internal sealed class CieLuvToCieXyzConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
public CieXyz Convert(in CieLuv input) public CieXyz Convert(in CieLuv input)
{ {
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
@ -22,12 +24,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
float v0 = ComputeV0(input.WhitePoint); float v0 = ComputeV0(input.WhitePoint);
float y = l > CieConstants.Kappa * CieConstants.Epsilon float y = l > CieConstants.Kappa * CieConstants.Epsilon
? MathF.Pow((l + 16) / 116, 3) ? ImageMaths.Pow3((l + 16) / 116)
: l / CieConstants.Kappa; : l / CieConstants.Kappa;
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3;
float b = -5 * y; float b = -5 * y;
float c = -0.3333333F; const float c = -0.3333333F;
float d = y * ((39 * l / (v + (13 * l * v0))) - 5); float d = y * ((39 * l / (v + (13 * l * v0))) - 5);
float x = (d - b) / (a - c); float x = (d - b) / (a - c);
@ -56,21 +58,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// </summary> /// </summary>
/// <param name="input">The whitepoint</param> /// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeU0(in CieXyz input) private static float ComputeU0(in CieXyz input)
{ => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
/// <summary> /// <summary>
/// Calculates the red-green chromacity based on the given whitepoint. /// Calculates the red-green chromacity based on the given whitepoint.
/// </summary> /// </summary>
/// <param name="input">The whitepoint</param> /// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeV0(in CieXyz input) private static float ComputeV0(in CieXyz input)
{ => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
} }
} }

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs

@ -4,16 +4,20 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between CIE XYZ and CIE xyY /// Color converter between CIE XYZ and CIE xyY
/// <see href="http://www.brucelindbloom.com/"/> for formulas. /// <see href="http://www.brucelindbloom.com/"/> for formulas.
/// </summary> /// </summary>
internal class CieXyzAndCieXyyConverter : IColorConversion<CieXyz, CieXyy>, IColorConversion<CieXyy, CieXyz> internal sealed class CieXyzAndCieXyyConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieXyy"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy Convert(in CieXyz input) public CieXyy Convert(in CieXyz input)
{ {
float x = input.X / (input.X + input.Y + input.Z); float x = input.X / (input.X + input.Y + input.Z);
@ -27,8 +31,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColor
return new CieXyy(x, y, input.Y); return new CieXyy(x, y, input.Y);
} }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieXyy"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieXyy input) public CieXyz Convert(in CieXyy input)
{ {
if (MathF.Abs(input.Y) < Constants.Epsilon) if (MathF.Abs(input.Y) < Constants.Epsilon)

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs

@ -3,7 +3,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// The base class for converting between <see cref="HunterLab"/> and <see cref="CieXyz"/> color spaces. /// The base class for converting between <see cref="HunterLab"/> and <see cref="CieXyz"/> color spaces.
@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// </summary> /// </summary>
/// <param name="whitePoint">The whitepoint</param> /// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKa(CieXyz whitePoint) public static float ComputeKa(CieXyz whitePoint)
{ {
if (whitePoint.Equals(Illuminants.C)) if (whitePoint.Equals(Illuminants.C))
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// </summary> /// </summary>
/// <param name="whitePoint">The whitepoint</param> /// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKb(CieXyz whitePoint) public static float ComputeKb(CieXyz whitePoint)
{ {
if (whitePoint == Illuminants.C) if (whitePoint == Illuminants.C)

45
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs

@ -4,12 +4,12 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between CIE XYZ and LMS /// Color converter between <see cref="CieXyz"/> and <see cref="Lms"/>
/// </summary> /// </summary>
internal class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz> internal sealed class CieXyzAndLmsConverter
{ {
/// <summary> /// <summary>
/// Default transformation matrix used, when no other is set. (Bradford) /// Default transformation matrix used, when no other is set. (Bradford)
@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter() public CieXyzAndLmsConverter()
: this(DefaultTransformationMatrix) : this(DefaultTransformationMatrix)
{ {
@ -36,41 +35,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>), /// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
/// if not set <see cref="DefaultTransformationMatrix"/> will be used. /// if not set <see cref="DefaultTransformationMatrix"/> will be used.
/// </param> /// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix)
{ {
this.TransformationMatrix = transformationMatrix; this.transformationMatrix = transformationMatrix;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
} }
/// <summary> /// <summary>
/// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain). /// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="Lms"/> type.
/// <see cref="LmsAdaptationMatrix"/>
/// </summary> /// </summary>
public Matrix4x4 TransformationMatrix /// <param name="input">The input color instance.</param>
{ /// <returns>The converted result</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
get => this.transformationMatrix;
set
{
this.transformationMatrix = value;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lms Convert(in CieXyz input) public Lms Convert(in CieXyz input)
{ {
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix); var vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix);
return new Lms(vector); return new Lms(vector);
} }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Lms"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in Lms input) public CieXyz Convert(in Lms input)
{ {
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix); var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
return new CieXyz(vector); return new CieXyz(vector);
} }
} }

19
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs

@ -4,17 +4,16 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>. /// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
/// </summary> /// </summary>
internal class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab> internal sealed class CieXyzToCieLabConverter
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLabConverter() public CieXyzToCieLabConverter()
: this(CieLab.DefaultWhitePoint) : this(CieLab.DefaultWhitePoint)
{ {
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary> /// </summary>
/// <param name="labWhitePoint">The target reference lab white point</param> /// <param name="labWhitePoint">The target reference lab white point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToCieLabConverter(CieXyz labWhitePoint) => this.LabWhitePoint = labWhitePoint;
public CieXyzToCieLabConverter(CieXyz labWhitePoint)
{
this.LabWhitePoint = labWhitePoint;
}
/// <summary> /// <summary>
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used. /// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
/// </summary> /// </summary>
public CieXyz LabWhitePoint { get; } public CieXyz LabWhitePoint { get; }
/// <inheritdoc /> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieXyz input) public CieLab Convert(in CieXyz input)
{ {
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs

@ -3,19 +3,17 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>. /// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>.
/// </summary> /// </summary>
internal class CieXyzToCieLuvConverter : IColorConversion<CieXyz, CieLuv> internal sealed class CieXyzToCieLuvConverter
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLuvConverter() public CieXyzToCieLuvConverter()
: this(CieLuv.DefaultWhitePoint) : this(CieLuv.DefaultWhitePoint)
{ {
@ -25,19 +23,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary> /// </summary>
/// <param name="luvWhitePoint">The target reference luv white point</param> /// <param name="luvWhitePoint">The target reference luv white point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) => this.LuvWhitePoint = luvWhitePoint;
public CieXyzToCieLuvConverter(CieXyz luvWhitePoint)
{
this.LuvWhitePoint = luvWhitePoint;
}
/// <summary> /// <summary>
/// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used. /// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used.
/// </summary> /// </summary>
public CieXyz LuvWhitePoint { get; } public CieXyz LuvWhitePoint { get; }
/// <inheritdoc /> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieLuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
public CieLuv Convert(in CieXyz input) public CieLuv Convert(in CieXyz input)
{ {
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
@ -77,18 +74,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ComputeUp(in CieXyz input) private static float ComputeUp(in CieXyz input)
{ => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
/// <summary> /// <summary>
/// Calculates the red-green chromacity based on the given whitepoint. /// Calculates the red-green chromacity based on the given whitepoint.
/// </summary> /// </summary>
/// <param name="input">The whitepoint</param> /// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns> /// <returns>The <see cref="float"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeVp(in CieXyz input) private static float ComputeVp(in CieXyz input)
{ => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
} }
} }

21
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs

@ -4,17 +4,16 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between CieXyz and HunterLab /// Color converter between <see cref="CieXyz"/> and <see cref="HunterLab"/>
/// </summary> /// </summary>
internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion<CieXyz, HunterLab> internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToHunterLabConverter() public CieXyzToHunterLabConverter()
: this(HunterLab.DefaultWhitePoint) : this(HunterLab.DefaultWhitePoint)
{ {
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary> /// </summary>
/// <param name="labWhitePoint">The hunter Lab white point.</param> /// <param name="labWhitePoint">The hunter Lab white point.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToHunterLabConverter(CieXyz labWhitePoint) => this.HunterLabWhitePoint = labWhitePoint;
public CieXyzToHunterLabConverter(CieXyz labWhitePoint)
{
this.HunterLabWhitePoint = labWhitePoint;
}
/// <summary> /// <summary>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used. /// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary> /// </summary>
public CieXyz HunterLabWhitePoint { get; } public CieXyz HunterLabWhitePoint { get; }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="HunterLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab Convert(in CieXyz input) public HunterLab Convert(in CieXyz input)
{ {
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between CieXyz and LinearRgb /// Color converter between <see cref="CieXyz"/> and <see cref="LinearRgb"/>
/// </summary> /// </summary>
internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<CieXyz, LinearRgb> internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase
{ {
private readonly Matrix4x4 conversionMatrix; private readonly Matrix4x4 conversionMatrix;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Initializes a new instance of the <see cref="CieXyzToLinearRgbConverter"/> class. /// Initializes a new instance of the <see cref="CieXyzToLinearRgbConverter"/> class.
/// </summary> /// </summary>
/// <param name="workingSpace">The target working space.</param> /// <param name="workingSpace">The target working space.</param>
public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace)
{ {
this.TargetWorkingSpace = workingSpace; this.TargetWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,13 +34,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary> /// <summary>
/// Gets the target working space /// Gets the target working space
/// </summary> /// </summary>
public RgbWorkingSpace TargetWorkingSpace { get; } public RgbWorkingSpaceBase TargetWorkingSpace { get; }
/// <inheritdoc/> /// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="LinearRgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in CieXyz input) public LinearRgb Convert(in CieXyz input)
{ {
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
Vector3 vector = Vector3.Transform(input.Vector, inverted); var vector = Vector3.Transform(input.ToVector3(), inverted);
return new LinearRgb(vector, this.TargetWorkingSpace); return new LinearRgb(vector, this.TargetWorkingSpace);
} }
} }

51
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between <see cref="Cmyk"/> and <see cref="Rgb"/>
/// </summary>
internal sealed class CmykAndRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Cmyk"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Cmyk input)
{
Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K));
return new Rgb(rgb);
}
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Cmyk"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk Convert(in Rgb input)
{
// To CMY
Vector3 cmy = Vector3.One - input.ToVector3();
// To CMYK
var k = new Vector3(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z)));
if (MathF.Abs(k.X - 1F) < Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1F);
}
cmy = (cmy - k) / (Vector3.One - k);
return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X);
}
}
}

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs

@ -4,16 +4,20 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between HSL and Rgb /// Color converter between HSL and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas. /// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary> /// </summary>
internal class HslAndRgbConverter : IColorConversion<Hsl, Rgb>, IColorConversion<Rgb, Hsl> internal sealed class HslAndRgbConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Hsl"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsl input) public Rgb Convert(in Hsl input)
{ {
float rangedH = input.H / 360F; float rangedH = input.H / 360F;
@ -43,8 +47,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
return new Rgb(r, g, b); return new Rgb(r, g, b);
} }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Hsl"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsl Convert(in Rgb input) public Hsl Convert(in Rgb input)
{ {
float r = input.R; float r = input.R;
@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns> /// <returns>
/// The <see cref="float"/>. /// The <see cref="float"/>.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static float GetColorComponent(float first, float second, float third) private static float GetColorComponent(float first, float second, float third)
{ {
third = MoveIntoRange(third); third = MoveIntoRange(third);
@ -134,16 +142,16 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns> /// <returns>
/// The <see cref="float"/>. /// The <see cref="float"/>.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static float MoveIntoRange(float value) private static float MoveIntoRange(float value)
{ {
if (value < 0F) if (value < 0F)
{ {
value += 1F; value++;
} }
else if (value > 1F) else if (value > 1F)
{ {
value -= 1F; value--;
} }
return value; return value;

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs

@ -4,16 +4,20 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between HSV and Rgb /// Color converter between HSV and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas. /// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary> /// </summary>
internal class HsvAndRgbConverter : IColorConversion<Hsv, Rgb>, IColorConversion<Rgb, Hsv> internal sealed class HsvAndRgbConverter
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Hsv"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsv input) public Rgb Convert(in Hsv input)
{ {
float s = input.S; float s = input.S;
@ -75,8 +79,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
return new Rgb(r, g, b); return new Rgb(r, g, b);
} }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Hsv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv Convert(in Rgb input) public Hsv Convert(in Rgb input)
{ {
float r = input.R; float r = input.R;

16
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs

@ -4,15 +4,19 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between HunterLab and CieXyz /// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary> /// </summary>
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz> internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{ {
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="HunterLab"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in HunterLab input) public CieXyz Convert(in HunterLab input)
{ {
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
@ -22,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
float ka = ComputeKa(input.WhitePoint); float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint); float kb = ComputeKb(input.WhitePoint);
float y = MathF.Pow(l / 100F, 2) * yn; float y = ImageMaths.Pow2(l / 100F) * yn;
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs

@ -3,10 +3,10 @@
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Provides base methods for converting between Rgb and CieXyz color spaces. /// Provides base methods for converting between <see cref="LinearRgb"/> and <see cref="CieXyz"/> color spaces.
/// </summary> /// </summary>
internal abstract class LinearRgbAndCieXyzConverterBase internal abstract class LinearRgbAndCieXyzConverterBase
{ {
@ -15,10 +15,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// </summary> /// </summary>
/// <param name="workingSpace">The Rgb working space.</param> /// <param name="workingSpace">The Rgb working space.</param>
/// <returns>The <see cref="Matrix4x4"/> based on the chromaticity and working space.</returns> /// <returns>The <see cref="Matrix4x4"/> based on the chromaticity and working space.</returns>
public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpaceBase workingSpace)
{ {
DebugGuard.NotNull(workingSpace, nameof(workingSpace)); DebugGuard.NotNull(workingSpace, nameof(workingSpace));
RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates;
float xr = chromaticity.R.X; float xr = chromaticity.R.X;
@ -42,23 +41,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
var xyzMatrix = new Matrix4x4 var xyzMatrix = new Matrix4x4
{ {
M11 = mXr, M21 = mXg, M31 = mXb, M11 = mXr,
M12 = Yr, M22 = Yg, M32 = Yb, M21 = mXg,
M13 = mZr, M23 = mZg, M33 = mZb, M31 = mXb,
M12 = Yr,
M22 = Yg,
M32 = Yb,
M13 = mZr,
M23 = mZg,
M33 = mZb,
M44 = 1F M44 = 1F
}; };
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix); var vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix);
// Use transposed Rows/Columns // Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication? // TODO: Is there a built in method for this multiplication?
return new Matrix4x4 return new Matrix4x4
{ {
M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb, M11 = vector.X * mXr,
M12 = vector.X * Yr, M22 = vector.Y * Yg, M32 = vector.Z * Yb, M21 = vector.Y * mXg,
M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb, M31 = vector.Z * mXb,
M12 = vector.X * Yr,
M22 = vector.Y * Yg,
M32 = vector.Z * Yb,
M13 = vector.X * mZr,
M23 = vector.Y * mZg,
M33 = vector.Z * mZb,
M44 = 1F M44 = 1F
}; };
} }

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between LinearRgb and CieXyz /// Color converter between <see cref="LinearRgb"/> and <see cref="CieXyz"/>
/// </summary> /// </summary>
internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<LinearRgb, CieXyz> internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase
{ {
private readonly Matrix4x4 conversionMatrix; private readonly Matrix4x4 conversionMatrix;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Initializes a new instance of the <see cref="LinearRgbToCieXyzConverter"/> class. /// Initializes a new instance of the <see cref="LinearRgbToCieXyzConverter"/> class.
/// </summary> /// </summary>
/// <param name="workingSpace">The target working space.</param> /// <param name="workingSpace">The target working space.</param>
public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) public LinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace)
{ {
this.SourceWorkingSpace = workingSpace; this.SourceWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,14 +34,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary> /// <summary>
/// Gets the source working space /// Gets the source working space
/// </summary> /// </summary>
public RgbWorkingSpace SourceWorkingSpace { get; } public RgbWorkingSpaceBase SourceWorkingSpace { get; }
/// <inheritdoc/> /// <summary>
/// Performs the conversion from the <see cref="LinearRgb"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in LinearRgb input) public CieXyz Convert(in LinearRgb input)
{ {
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix); var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
return new CieXyz(vector); return new CieXyz(vector);
} }
} }

29
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between <see cref="LinearRgb"/> and <see cref="Rgb"/>
/// </summary>
internal sealed class LinearRgbToRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="LinearRgb"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in LinearRgb input)
{
var vector = input.ToVector3();
vector.X = input.WorkingSpace.Compress(vector.X);
vector.Y = input.WorkingSpace.Compress(vector.Y);
vector.Z = input.WorkingSpace.Compress(vector.Z);
return new Rgb(vector, input.WorkingSpace);
}
}
}

29
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between Rgb and LinearRgb
/// </summary>
internal class RgbToLinearRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="LinearRgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in Rgb input)
{
var vector = input.ToVector3();
vector.X = input.WorkingSpace.Expand(vector.X);
vector.Y = input.WorkingSpace.Expand(vector.Y);
vector.Z = input.WorkingSpace.Expand(vector.Z);
return new LinearRgb(vector, input.WorkingSpace);
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs

@ -5,18 +5,22 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Color converter between YCbCr and Rgb /// Color converter between <see cref="YCbCr"/> and <see cref="Rgb"/>
/// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas. /// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas.
/// </summary> /// </summary>
internal class YCbCrAndRgbConverter : IColorConversion<YCbCr, Rgb>, IColorConversion<Rgb, YCbCr> internal sealed class YCbCrAndRgbConverter
{ {
private static readonly Vector3 MaxBytes = new Vector3(255F); private static readonly Vector3 MaxBytes = new Vector3(255F);
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="YCbCr"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in YCbCr input) public Rgb Convert(in YCbCr input)
{ {
float y = input.Y; float y = input.Y;
@ -30,11 +34,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
return new Rgb(new Vector3(r, g, b) / MaxBytes); return new Rgb(new Vector3(r, g, b) / MaxBytes);
} }
/// <inheritdoc/> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="YCbCr"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public YCbCr Convert(in Rgb input) public YCbCr Convert(in Rgb input)
{ {
Vector3 rgb = input.Vector * MaxBytes; Vector3 rgb = input.ToVector3() * MaxBytes;
float r = rgb.X; float r = rgb.X;
float g = rgb.Y; float g = rgb.Y;
float b = rgb.Z; float b = rgb.Z;

97
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs

@ -3,11 +3,10 @@
using System.Numerics; using System.Numerics;
// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
{ {
/// <summary> /// <summary>
/// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain. /// Matrices used for transformation from <see cref="CieXyz"/> to <see cref="Lms"/>, defining the cone response domain.
/// Used in <see cref="IChromaticAdaptation"/> /// Used in <see cref="IChromaticAdaptation"/>
/// </summary> /// </summary>
/// <remarks> /// <remarks>
@ -17,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy /// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf /// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf
/// </remarks> /// </remarks>
internal static class LmsAdaptationMatrix public static class LmsAdaptationMatrix
{ {
/// <summary> /// <summary>
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)
@ -25,9 +24,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 VonKriesHPEAdjusted public static readonly Matrix4x4 VonKriesHPEAdjusted
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F, M11 = 0.40024F,
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F, M12 = 0.7076F,
M31 = 0, M32 = 0, M33 = 0.91822F, M13 = -0.08081F,
M21 = -0.2263F,
M22 = 1.16532F,
M23 = 0.0457F,
M31 = 0,
M32 = 0,
M33 = 0.91822F,
M44 = 1F // Important for inverse transforms. M44 = 1F // Important for inverse transforms.
}); });
@ -37,9 +42,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 VonKriesHPE public static readonly Matrix4x4 VonKriesHPE
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F, M11 = 0.3897F,
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F, M12 = 0.6890F,
M31 = 0, M32 = 0, M33 = 1F, M13 = -0.0787F,
M21 = -0.2298F,
M22 = 1.1834F,
M23 = 0.0464F,
M31 = 0,
M32 = 0,
M33 = 1F,
M44 = 1F M44 = 1F
}); });
@ -54,9 +65,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 Bradford public static readonly Matrix4x4 Bradford
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F, M11 = 0.8951F,
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F, M12 = 0.2664F,
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F, M13 = -0.1614F,
M21 = -0.7502F,
M22 = 1.7135F,
M23 = 0.0367F,
M31 = 0.0389F,
M32 = -0.0685F,
M33 = 1.0296F,
M44 = 1F M44 = 1F
}); });
@ -65,35 +82,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// </summary> /// </summary>
public static readonly Matrix4x4 BradfordSharp public static readonly Matrix4x4 BradfordSharp
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F, M11 = 1.2694F,
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F, M12 = -0.0988F,
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F, M13 = -0.1706F,
M44 = 1F M21 = -0.8364F,
}); M22 = 1.8006F,
M23 = 0.0357F,
M31 = 0.0297F,
M32 = -0.0315F,
M33 = 1.0018F,
M44 = 1F
});
/// <summary> /// <summary>
/// CMCCAT2000 (fitted from all available color data sets) /// CMCCAT2000 (fitted from all available color data sets)
/// </summary> /// </summary>
public static readonly Matrix4x4 CMCCAT2000 public static readonly Matrix4x4 CMCCAT2000
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F, M11 = 0.7982F,
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F, M12 = 0.3389F,
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F, M13 = -0.1371F,
M44 = 1F M21 = -0.5918F,
}); M22 = 1.5512F,
M23 = 0.0406F,
M31 = 0.0008F,
M32 = 0.239F,
M33 = 0.9753F,
M44 = 1F
});
/// <summary> /// <summary>
/// CAT02 (optimized for minimizing CIELAB differences) /// CAT02 (optimized for minimizing CIELAB differences)
/// </summary> /// </summary>
public static readonly Matrix4x4 CAT02 public static readonly Matrix4x4 CAT02
= Matrix4x4.Transpose(new Matrix4x4 = Matrix4x4.Transpose(new Matrix4x4
{ {
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F, M11 = 0.7328F,
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F, M12 = 0.4296F,
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F, M13 = -0.1624F,
M44 = 1F M21 = -0.7036F,
}); M22 = 1.6975F,
M23 = 0.0061F,
M31 = 0.0030F,
M32 = 0.0136F,
M33 = 0.9834F,
M44 = 1F
});
} }
} }

21
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs

@ -3,13 +3,13 @@
using System; using System;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{ {
/// <summary> /// <summary>
/// Represents the chromaticity coordinates of RGB primaries. /// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="RgbWorkingSpace"/>. /// One of the specifiers of <see cref="RgbWorkingSpaceBase"/>.
/// </summary> /// </summary>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates> public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct. /// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -40,13 +40,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
public CieXyChromaticityCoordinates B { get; } public CieXyChromaticityCoordinates B { get; }
/// <summary> /// <summary>
/// Compares two <see cref="CieLab"/> objects for equality. /// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand. /// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
/// </param> /// </param>
/// <param name="right"> /// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand. /// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
/// </param> /// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
@ -57,13 +57,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
} }
/// <summary> /// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality /// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand. /// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
/// </param> /// </param>
/// <param name="right"> /// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand. /// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
/// </param> /// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
@ -92,8 +92,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
{ {
int hashCode = this.R.GetHashCode(); int hashCode = this.R.GetHashCode();
hashCode = (hashCode * 397) ^ this.G.GetHashCode(); hashCode = (hashCode * 397) ^ this.G.GetHashCode();
hashCode = (hashCode * 397) ^ this.B.GetHashCode(); return (hashCode * 397) ^ this.B.GetHashCode();
return hashCode;
} }
} }
} }

46
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs

@ -1,46 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements gamma companding
/// </summary>
/// <remarks>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
internal class GammaCompanding : ICompanding
{
/// <summary>
/// Initializes a new instance of the <see cref="GammaCompanding"/> class.
/// </summary>
/// <param name="gamma">The gamma value.</param>
public GammaCompanding(float gamma)
{
this.Gamma = gamma;
}
/// <summary>
/// Gets the gamma value
/// </summary>
public float Gamma { get; }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return MathF.Pow(channel, this.Gamma);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return MathF.Pow(channel, 1 / this.Gamma);
}
}
}

35
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements L* companding
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
internal class LCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel <= CieConstants.Epsilon
? channel * CieConstants.Kappa / 100F
: MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs

@ -1,24 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Color converter between LinearRgb and Rgb
/// </summary>
internal class LinearRgbToRgbConverter : IColorConversion<LinearRgb, Rgb>
{
/// <inheritdoc/>
public Rgb Convert(in LinearRgb input)
{
Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Compress(vector.X);
vector.Y = input.WorkingSpace.Companding.Compress(vector.Y);
vector.Z = input.WorkingSpace.Companding.Compress(vector.Z);
return new Rgb(vector, input.WorkingSpace);
}
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements Rec. 2020 companding function (for 12-bits).
/// </summary>
/// <remarks>
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
/// </remarks>
internal class Rec2020Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F;
}
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements the Rec. 709 companding function
/// </summary>
/// <remarks>
/// http://en.wikipedia.org/wiki/Rec._709
/// </remarks>
internal class Rec709Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F;
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs

@ -1,24 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Color converter between Rgb and LinearRgb
/// </summary>
internal class RgbToLinearRgbConverter : IColorConversion<Rgb, LinearRgb>
{
/// <inheritdoc/>
public LinearRgb Convert(in Rgb input)
{
Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Expand(vector.X);
vector.Y = input.WorkingSpace.Companding.Expand(vector.Y);
vector.Z = input.WorkingSpace.Companding.Expand(vector.Z);
return new LinearRgb(vector, input.WorkingSpace);
}
}
}

98
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs

@ -1,98 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Trivial implementation of <see cref="RgbWorkingSpace"/>
/// </summary>
internal class RgbWorkingSpace
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbWorkingSpace"/> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="companding">The function pair for converting to <see cref="CieXyz"/> and back.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
{
this.WhitePoint = referenceWhite;
this.Companding = companding;
this.ChromaticityCoordinates = chromaticityCoordinates;
}
/// <summary>
/// Gets the reference white point
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the function pair for converting to <see cref="CieXyz"/> and back.
/// </summary>
public ICompanding Companding { get; }
/// <summary>
/// Gets the chromaticity of the rgb primaries.
/// </summary>
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Compares two <see cref="RgbWorkingSpace"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RgbWorkingSpace"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(RgbWorkingSpace left, RgbWorkingSpace right)
{
return Equals(left, right);
}
/// <summary>
/// Compares two <see cref="RgbWorkingSpace"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RgbWorkingSpace"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(RgbWorkingSpace left, RgbWorkingSpace right)
{
return !Equals(left, right);
}
public override bool Equals(object obj)
{
return obj is RgbWorkingSpace other && this.Equals(other);
}
public bool Equals(RgbWorkingSpace other)
{
// TODO: Object.Equals for ICompanding will be slow.
return this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates)
&& Equals(this.Companding, other.Companding);
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = this.WhitePoint.GetHashCode();
hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode();
hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0);
return hashCode;
}
}
}
}

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs

@ -1,33 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements sRGB companding
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
internal class SRgbCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
}
}
}

66
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs

@ -0,0 +1,66 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// The gamma working space.
/// </summary>
public class GammaWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="GammaWorkingSpace" /> class.
/// </summary>
/// <param name="gamma">The gamma value.</param>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma;
/// <summary>
/// Gets the gamma value.
/// </summary>
public float Gamma { get; }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => GammaCompanding.Compress(channel, this.Gamma);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => GammaCompanding.Expand(channel, this.Gamma);
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is GammaWorkingSpace other)
{
return this.Gamma.Equals(other.Gamma)
&& this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = base.GetHashCode();
return HashHelpers.Combine(hash, this.Gamma.GetHashCode());
}
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// L* working space.
/// </summary>
public sealed class LWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="LWorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => LCompanding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => LCompanding.Expand(channel);
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
/// </summary>
public sealed class Rec2020WorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="Rec2020WorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => Rec2020Companding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => Rec2020Companding.Expand(channel);
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
/// </summary>
public sealed class Rec709WorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="Rec709WorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => Rec709Companding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => Rec709Companding.Expand(channel);
}
}

83
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Base class for all implementations of <see cref="RgbWorkingSpaceBase"/>.
/// </summary>
public abstract class RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbWorkingSpaceBase"/> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
protected RgbWorkingSpaceBase(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
{
this.WhitePoint = referenceWhite;
this.ChromaticityCoordinates = chromaticityCoordinates;
}
/// <summary>
/// Gets the reference white point
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the chromaticity of the rgb primaries.
/// </summary>
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// </remarks>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
public abstract float Expand(float channel);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system).
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
public abstract float Compress(float channel);
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is RgbWorkingSpaceBase other)
{
return this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.WhitePoint.GetHashCode();
return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode());
}
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// The sRgb working space.
/// </summary>
public sealed class SRgbWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="SRgbWorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => SRgbCompanding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => SRgbCompanding.Expand(channel);
}
}

56
src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs

@ -1,8 +1,11 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
@ -13,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Transformation described here: /// Transformation described here:
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
/// </remarks> /// </remarks>
internal class VonKriesChromaticAdaptation : IChromaticAdaptation public class VonKriesChromaticAdaptation : IChromaticAdaptation
{ {
private readonly CieXyzAndLmsConverter converter; private readonly CieXyzAndLmsConverter converter;
@ -41,27 +44,54 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class. /// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
/// </summary> /// </summary>
/// <param name="converter">The color converter</param> /// <param name="converter">The color converter</param>
public VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) => this.converter = converter;
{
this.converter = converter;
}
/// <inheritdoc/> /// <inheritdoc/>
public CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint)
{ {
if (sourceWhitePoint.Equals(targetWhitePoint)) if (sourceWhitePoint.Equals(destinationWhitePoint))
{ {
return sourceColor; return source;
} }
Lms sourceColorLms = this.converter.Convert(sourceColor); Lms sourceColorLms = this.converter.Convert(source);
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(targetWhitePoint); Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint);
var vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S); Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3();
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector)); var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3()));
return this.converter.Convert(targetColorLms); return this.converter.Convert(targetColorLms);
} }
/// <inheritdoc/>
public void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
if (sourceWhitePoint.Equals(destinationWhitePoint))
{
source.CopyTo(destination.Slice(0, count));
return;
}
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
Lms sourceColorLms = this.converter.Convert(sp);
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint);
Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3();
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3()));
dp = this.converter.Convert(targetColorLms);
}
}
} }
} }

125
src/ImageSharp/ColorSpaces/Hsl.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -11,17 +10,28 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary> /// <summary>
/// Represents a Hsl (hue, saturation, lightness) color. /// Represents a Hsl (hue, saturation, lightness) color.
/// </summary> /// </summary>
internal readonly struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float> public readonly struct Hsl : IEquatable<Hsl>
{ {
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary> /// <summary>
/// Max range used for clamping. /// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary> /// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); public readonly float H;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float S;
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float L;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct. /// Initializes a new instance of the <see cref="Hsl"/> struct.
@ -29,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="h">The h hue component.</param> /// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param> /// <param name="s">The s saturation component.</param>
/// <param name="l">The l value (lightness) component.</param> /// <param name="l">The l value (lightness) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsl(float h, float s, float l) public Hsl(float h, float s, float l)
: this(new Vector3(h, s, l)) : this(new Vector3(h, s, l))
{ {
@ -39,118 +49,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Hsl"/> struct. /// Initializes a new instance of the <see cref="Hsl"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the h, s, l components.</param> /// <param name="vector">The vector representing the h, s, l components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsl(Vector3 vector) public Hsl(Vector3 vector)
{ {
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); vector = Vector3.Clamp(vector, Min, Max);
} this.H = vector.X;
this.S = vector.Y;
/// <summary> this.L = vector.Z;
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
} }
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="Hsl"/> objects for equality. /// Compares two <see cref="Hsl"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand. /// The <see cref="Hsl"/> on the left side of the operand.
/// </param> /// </param>
/// <param name="right"> /// <param name="right">The <see cref="Hsl"/> on the right side of the operand.</param>
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Hsl left, Hsl right) public static bool operator ==(Hsl left, Hsl right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="Hsl"/> objects for inequality. /// Compares two <see cref="Hsl"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Hsl"/> on the left side of the operand.</param>
/// The <see cref="Hsl"/> on the left side of the operand. /// <param name="right">The <see cref="Hsl"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Hsl left, Hsl right) public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.L.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})");
{
return this.Equals(default)
? "Hsl [ Empty ]"
: $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is Hsl other && this.Equals(other);
{
return obj is Hsl other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsl other) public bool Equals(Hsl other)
{ {
return this.backingVector.Equals(other.backingVector); return this.H.Equals(other.H)
} && this.S.Equals(other.S)
&& this.L.Equals(other.L);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Hsl other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

181
src/ImageSharp/ColorSpaces/Hsv.cs

@ -2,28 +2,36 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.ColorSpaces namespace SixLabors.ImageSharp.ColorSpaces
{ {
/// <summary> /// <summary>
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
/// </summary> /// </summary>
internal readonly struct Hsv : IColorVector, IEquatable<Hsv>, IAlmostEquatable<Hsv, float> public readonly struct Hsv : IEquatable<Hsv>
{ {
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary> /// <summary>
/// Max range used for clamping. /// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary> /// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); public readonly float H;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float S;
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float V;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct. /// Initializes a new instance of the <see cref="Hsv"/> struct.
@ -31,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="h">The h hue component.</param> /// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param> /// <param name="s">The s saturation component.</param>
/// <param name="v">The v value (brightness) component.</param> /// <param name="v">The v value (brightness) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsv(float h, float s, float v) public Hsv(float h, float s, float v)
: this(new Vector3(h, s, v)) : this(new Vector3(h, s, v))
{ {
@ -41,168 +49,59 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Hsv"/> struct. /// Initializes a new instance of the <see cref="Hsv"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the h, s, v components.</param> /// <param name="vector">The vector representing the h, s, v components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsv(Vector3 vector) public Hsv(Vector3 vector)
{ {
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); vector = Vector3.Clamp(vector, Min, Max);
} this.H = vector.X;
this.S = vector.Y;
/// <summary> this.V = vector.Z;
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float V
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Hsv"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Hsv"/>.
/// </returns>
public static implicit operator Hsv(Rgba32 color)
{
float r = color.R / 255F;
float g = color.G / 255F;
float b = color.B / 255F;
float max = MathF.Max(r, MathF.Max(g, b));
float min = MathF.Min(r, MathF.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float v = max;
if (MathF.Abs(chroma) < Constants.Epsilon)
{
return new Hsv(0, s, v);
}
if (MathF.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
else if (MathF.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (MathF.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}
h *= 60;
if (h < 0.0)
{
h += 360;
}
s = chroma / v;
return new Hsv(h, s, v);
} }
/// <summary> /// <summary>
/// Compares two <see cref="Hsv"/> objects for equality. /// Compares two <see cref="Hsv"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// The <see cref="Hsv"/> on the left side of the operand. /// <param name="right">The <see cref="Hsv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Hsv left, Hsv right) public static bool operator ==(Hsv left, Hsv right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="Hsv"/> objects for inequality. /// Compares two <see cref="Hsv"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// The <see cref="Hsv"/> on the left side of the operand. /// <param name="right">The <see cref="Hsv"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Hsv left, Hsv right) public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.V.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})");
{
return this.Equals(default)
? "Hsv [ Empty ]"
: $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is Hsv other && this.Equals(other);
{
return obj is Hsv other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsv other) public bool Equals(Hsv other)
{ {
return this.backingVector.Equals(other.backingVector); return this.H.Equals(other.H)
} && this.S.Equals(other.S)
&& this.V.Equals(other.V);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Hsv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

142
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an Hunter LAB color. /// Represents an Hunter LAB color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/> /// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary> /// </summary>
internal readonly struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float> public readonly struct HunterLab : IEquatable<HunterLab>
{ {
/// <summary> /// <summary>
/// D50 standard illuminant. /// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.C; public static readonly CieXyz DefaultWhitePoint = Illuminants.C;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly CieXyz WhitePoint;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct. /// Initializes a new instance of the <see cref="HunterLab"/> struct.
@ -32,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param> /// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param> /// <param name="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(float l, float a, float b) public HunterLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint) : this(new Vector3(l, a, b), DefaultWhitePoint)
{ {
@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param> /// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param> /// <param name="b">The b (blue - yellow) component.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(float l, float a, float b, CieXyz whitePoint) public HunterLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint) : this(new Vector3(l, a, b), whitePoint)
{ {
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param> /// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks> /// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(Vector3 vector) public HunterLab(Vector3 vector)
: this(vector, DefaultWhitePoint) : this(vector, DefaultWhitePoint)
{ {
@ -67,126 +84,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l a b components.</param> /// <param name="vector">The vector representing the l a b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param> /// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(Vector3 vector, CieXyz whitePoint) public HunterLab(Vector3 vector, CieXyz whitePoint)
: this()
{ {
this.backingVector = vector; // Not clamping as documentation about this space only indicates "usual" ranges
this.L = vector.X;
this.A = vector.Y;
this.B = vector.Z;
this.WhitePoint = whitePoint; this.WhitePoint = whitePoint;
} }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a color component.
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public float A
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the b color component.
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="HunterLab"/> objects for equality. /// Compares two <see cref="HunterLab"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="HunterLab"/> on the left side of the operand.</param>
/// The <see cref="HunterLab"/> on the left side of the operand. /// <param name="right">The <see cref="HunterLab"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator ==(HunterLab left, HunterLab right) public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="HunterLab"/> objects for inequality /// Compares two <see cref="HunterLab"/> objects for inequality
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="HunterLab"/> on the left side of the operand.</param>
/// The <see cref="HunterLab"/> on the left side of the operand. /// <param name="right">The <see cref="HunterLab"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(HunterLab left, HunterLab right) public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
{
return this.Equals(default)
? "HunterLab [Empty]"
: $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is HunterLab other && this.Equals(other);
{
return obj is HunterLab other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(HunterLab other) public bool Equals(HunterLab other)
{ {
return this.backingVector.Equals(other.backingVector) return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint); && this.WhitePoint.Equals(other.WhitePoint);
} }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(HunterLab other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
} }
} }

28
src/ImageSharp/ColorSpaces/IAlmostEquatable.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Defines a generalized method that a value type or class implements to create
/// a type-specific method for determining approximate equality of instances.
/// </summary>
/// <typeparam name="TPixel">The type of objects to compare.</typeparam>
/// <typeparam name="TPrecision">The object specifying the type to specify precision with.</typeparam>
internal interface IAlmostEquatable<in TPixel, in TPrecision>
where TPrecision : struct, IComparable<TPrecision>
{
/// <summary>
/// Indicates whether the current object is equal to another object of the same type
/// when compared to the specified precision level.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <param name="precision">The object specifying the level of precision.</param>
/// <returns>
/// true if the current object is equal to the other parameter; otherwise, false.
/// </returns>
bool AlmostEquals(TPixel other, TPrecision precision);
}
}

18
src/ImageSharp/ColorSpaces/IColorVector.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Color represented as a vector in its color space
/// </summary>
internal interface IColorVector
{
/// <summary>
/// Gets the vector representation of the color
/// </summary>
Vector3 Vector { get; }
}
}

37
src/ImageSharp/ColorSpaces/ICompanding.cs

@ -1,37 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Pair of companding functions for <see cref="RgbWorkingSpace"/>.
/// Used for conversion to <see cref="CieXyz"/> and backwards.
/// See also: <seealso cref="RgbWorkingSpace.Companding"/>
/// </summary>
internal interface ICompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// </remarks>
/// <param name="channel">The channel value</param>
/// <returns>The linear channel value</returns>
float Expand(float channel);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system).
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
/// <param name="channel">The channel value</param>
/// <returns>The nonlinear channel value</returns>
float Compress(float channel);
}
}

2
src/ImageSharp/ColorSpaces/Illuminants.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Descriptions taken from: /// Descriptions taken from:
/// http://en.wikipedia.org/wiki/Standard_illuminant /// http://en.wikipedia.org/wiki/Standard_illuminant
/// </remarks> /// </remarks>
internal static class Illuminants public static class Illuminants
{ {
/// <summary> /// <summary>
/// Incandescent / Tungsten /// Incandescent / Tungsten

163
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -4,24 +4,45 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces namespace SixLabors.ImageSharp.ColorSpaces
{ {
/// <summary> /// <summary>
/// Represents an linear Rgb color with specified <see cref="RgbWorkingSpace"/> working space /// Represents an linear Rgb color with specified <see cref="RgbWorkingSpaceBase"/> working space
/// </summary> /// </summary>
internal readonly struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float> public readonly struct LinearRgb : IEquatable<LinearRgb>
{ {
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One;
/// <summary> /// <summary>
/// The default LinearRgb working space. /// The default LinearRgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct. /// Initializes a new instance of the <see cref="LinearRgb"/> struct.
@ -29,9 +50,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="r">The red component ranging between 0 and 1.</param> /// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param> /// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param> /// <param name="b">The blue component ranging between 0 and 1.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(float r, float g, float b) public LinearRgb(float r, float g, float b)
: this(new Vector3(r, g, b)) : this(r, g, b, DefaultWorkingSpace)
{ {
} }
@ -42,8 +63,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="g">The green component ranging between 0 and 1.</param> /// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param> /// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param> /// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) public LinearRgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace)
: this(new Vector3(r, g, b), workingSpace) : this(new Vector3(r, g, b), workingSpace)
{ {
} }
@ -52,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="LinearRgb"/> struct. /// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param> /// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector) public LinearRgb(Vector3 vector)
: this(vector, DefaultWorkingSpace) : this(vector, DefaultWorkingSpace)
{ {
@ -63,126 +84,68 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param> /// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The LinearRgb working space.</param> /// <param name="workingSpace">The LinearRgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) public LinearRgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
: this()
{ {
// Clamp to 0-1 range. // Clamp to 0-1 range.
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); vector = Vector3.Clamp(vector, Min, Max);
this.R = vector.X;
this.G = vector.Y;
this.B = vector.Z;
this.WorkingSpace = workingSpace; this.WorkingSpace = workingSpace;
} }
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float R
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float G
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public RgbWorkingSpace WorkingSpace { get; }
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="LinearRgb"/> objects for equality. /// Compares two <see cref="LinearRgb"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// The <see cref="LinearRgb"/> on the left side of the operand. /// <param name="right">The <see cref="LinearRgb"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(LinearRgb left, LinearRgb right) public static bool operator ==(LinearRgb left, LinearRgb right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="LinearRgb"/> objects for inequality. /// Compares two <see cref="LinearRgb"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// The <see cref="LinearRgb"/> on the left side of the operand. /// <param name="right">The <see cref="LinearRgb"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(LinearRgb left, LinearRgb right) public static bool operator !=(LinearRgb left, LinearRgb right) => !left.Equals(right);
{
return !left.Equals(right); /// <summary>
} /// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
{
return this.Equals(default)
? "LinearRgb [ Empty ]"
: $"LinearRgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is LinearRgb other && this.Equals(other);
{
return obj is LinearRgb other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(LinearRgb other) public bool Equals(LinearRgb other)
{ {
return this.backingVector.Equals(other.backingVector); return this.R.Equals(other.R)
} && this.G.Equals(other.G)
&& this.B.Equals(other.B);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(LinearRgb other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

134
src/ImageSharp/ColorSpaces/Lms.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -13,12 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// named after their responsivity (sensitivity) at long, medium and short wavelengths. /// named after their responsivity (sensitivity) at long, medium and short wavelengths.
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/> /// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
/// </summary> /// </summary>
internal readonly struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float> public readonly struct Lms : IEquatable<Lms>
{ {
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float M;
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Lms"/> struct. /// Initializes a new instance of the <see cref="Lms"/> struct.
@ -26,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="l">L represents the responsivity at long wavelengths.</param> /// <param name="l">L represents the responsivity at long wavelengths.</param>
/// <param name="m">M represents the responsivity at medium wavelengths.</param> /// <param name="m">M represents the responsivity at medium wavelengths.</param>
/// <param name="s">S represents the responsivity at short wavelengths.</param> /// <param name="s">S represents the responsivity at short wavelengths.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Lms(float l, float m, float s) public Lms(float l, float m, float s)
: this(new Vector3(l, m, s)) : this(new Vector3(l, m, s))
{ {
@ -36,119 +48,65 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Lms"/> struct. /// Initializes a new instance of the <see cref="Lms"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the l, m, s components.</param> /// <param name="vector">The vector representing the l, m, s components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Lms(Vector3 vector) public Lms(Vector3 vector)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
}
/// <summary>
/// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float M
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Not clamping as documentation about this space only indicates "usual" ranges
get => this.backingVector.Y; this.L = vector.X;
this.M = vector.Y;
this.S = vector.Z;
} }
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="Lms"/> objects for equality. /// Compares two <see cref="Lms"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// The <see cref="Lms"/> on the left side of the operand. /// <param name="right">The <see cref="Lms"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Lms left, Lms right) public static bool operator ==(Lms left, Lms right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="Lms"/> objects for inequality. /// Compares two <see cref="Lms"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// The <see cref="Lms"/> on the left side of the operand. /// <param name="right">The <see cref="Lms"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Lms left, Lms right) public static bool operator !=(Lms left, Lms right) => !left.Equals(right);
{
return !left.Equals(right); /// <summary>
} /// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
return HashHelpers.Combine(hash, this.S.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})");
{
return this.Equals(default)
? "Lms [ Empty ]"
: $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is Lms other && this.Equals(other);
{
return obj is Lms other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Lms other) public bool Equals(Lms other)
{ {
return this.backingVector.Equals(other.backingVector); return this.L.Equals(other.L)
} && this.M.Equals(other.M)
&& this.S.Equals(other.S);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Lms other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

172
src/ImageSharp/ColorSpaces/Rgb.cs

@ -4,25 +4,46 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.ColorSpaces namespace SixLabors.ImageSharp.ColorSpaces
{ {
/// <summary> /// <summary>
/// Represents an RGB color with specified <see cref="RgbWorkingSpace"/> working space /// Represents an RGB color with specified <see cref="RgbWorkingSpaceBase"/> working space.
/// </summary> /// </summary>
internal readonly struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float> public readonly struct Rgb : IEquatable<Rgb>
{ {
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One;
/// <summary> /// <summary>
/// The default rgb working space /// The default rgb working space
/// </summary> /// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct. /// Initializes a new instance of the <see cref="Rgb"/> struct.
@ -30,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="r">The red component ranging between 0 and 1.</param> /// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param> /// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param> /// <param name="b">The blue component ranging between 0 and 1.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb(float r, float g, float b) public Rgb(float r, float g, float b)
: this(new Vector3(r, g, b)) : this(r, g, b, DefaultWorkingSpace)
{ {
} }
@ -43,8 +64,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="g">The green component ranging between 0 and 1.</param> /// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param> /// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param> /// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) public Rgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace)
: this(new Vector3(r, g, b), workingSpace) : this(new Vector3(r, g, b), workingSpace)
{ {
} }
@ -53,7 +74,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Rgb"/> struct. /// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param> /// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector) public Rgb(Vector3 vector)
: this(vector, DefaultWorkingSpace) : this(vector, DefaultWorkingSpace)
{ {
@ -64,68 +85,33 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param> /// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The rgb working space.</param> /// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) public Rgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
: this()
{ {
// Clamp to 0-1 range. vector = Vector3.Clamp(vector, Min, Max);
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); this.R = vector.X;
this.G = vector.Y;
this.B = vector.Z;
this.WorkingSpace = workingSpace; this.WorkingSpace = workingSpace;
} }
/// <summary> /// <summary>
/// Gets the red component. /// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a
/// <remarks>A value usually ranging between 0 and 1.</remarks> /// <see cref="Rgb"/>.
/// </summary>
public float R
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float G
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary> /// </summary>
public RgbWorkingSpace WorkingSpace { get; } /// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
/// <inheritdoc /> [MethodImpl(InliningOptions.ShortMethod)]
public Vector3 Vector => this.backingVector; public static implicit operator Rgb(Rgb24 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a /// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Rgb"/>. /// <see cref="Rgb"/>.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// The instance of <see cref="Rgba32"/> to convert. /// <returns>An instance of <see cref="Rgb"/>.</returns>
/// </param> [MethodImpl(InliningOptions.ShortMethod)]
/// <returns> public static implicit operator Rgb(Rgba32 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// An instance of <see cref="Rgb"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgb(Rgba32 color)
{
return new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
}
/// <summary> /// <summary>
/// Compares two <see cref="Rgb"/> objects for equality. /// Compares two <see cref="Rgb"/> objects for equality.
@ -139,66 +125,48 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Rgb left, Rgb right) public static bool operator ==(Rgb left, Rgb right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="Rgb"/> objects for inequality. /// Compares two <see cref="Rgb"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Rgb"/> on the left side of the operand.</param>
/// The <see cref="Rgb"/> on the left side of the operand. /// <param name="right">The <see cref="Rgb"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Rgb"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Rgb left, Rgb right) public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right);
{
return !left.Equals(right); /// <summary>
} /// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
{
return this.Equals(default)
? "Rgb [ Empty ]"
: $"Rgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is Rgb other && this.Equals(other);
{
return obj is Rgb other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Rgb other) public bool Equals(Rgb other)
{ {
return this.backingVector.Equals(other.backingVector); return this.R.Equals(other.R)
} && this.G.Equals(other.G)
&& this.B.Equals(other.B);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Rgb other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

43
src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.ColorSpaces namespace SixLabors.ImageSharp.ColorSpaces
@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Chromaticity coordinates taken from: /// Chromaticity coordinates taken from:
/// <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/> /// <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/>
/// </summary> /// </summary>
internal static class RgbWorkingSpaces public static class RgbWorkingSpaces
{ {
/// <summary> /// <summary>
/// sRgb working space. /// sRgb working space.
@ -19,97 +20,97 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Uses proper companding function, according to: /// Uses proper companding function, according to:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/> /// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/>
/// </remarks> /// </remarks>
public static readonly RgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); public static readonly RgbWorkingSpaceBase SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary> /// <summary>
/// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>). /// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>).
/// See also <see cref="SRgb"/>. /// See also <see cref="SRgb"/>.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); public static readonly RgbWorkingSpaceBase SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary> /// <summary>
/// Rec. 709 (ITU-R Recommendation BT.709) working space. /// Rec. 709 (ITU-R Recommendation BT.709) working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); public static readonly RgbWorkingSpaceBase Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F)));
/// <summary> /// <summary>
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); public static readonly RgbWorkingSpaceBase Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F)));
/// <summary> /// <summary>
/// ECI Rgb v2 working space. /// ECI Rgb v2 working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); public static readonly RgbWorkingSpaceBase ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
/// <summary> /// <summary>
/// Adobe Rgb (1998) working space. /// Adobe Rgb (1998) working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); public static readonly RgbWorkingSpaceBase AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary> /// <summary>
/// Apple sRgb working space. /// Apple sRgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); public static readonly RgbWorkingSpaceBase ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
/// <summary> /// <summary>
/// Best Rgb working space. /// Best Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); public static readonly RgbWorkingSpaceBase BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
/// <summary> /// <summary>
/// Beta Rgb working space. /// Beta Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); public static readonly RgbWorkingSpaceBase BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F)));
/// <summary> /// <summary>
/// Bruce Rgb working space. /// Bruce Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); public static readonly RgbWorkingSpaceBase BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary> /// <summary>
/// CIE Rgb working space. /// CIE Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); public static readonly RgbWorkingSpaceBase CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F)));
/// <summary> /// <summary>
/// ColorMatch Rgb working space. /// ColorMatch Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); public static readonly RgbWorkingSpaceBase ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F)));
/// <summary> /// <summary>
/// Don Rgb 4 working space. /// Don Rgb 4 working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); public static readonly RgbWorkingSpaceBase DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
/// <summary> /// <summary>
/// Ekta Space PS5 working space. /// Ekta Space PS5 working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); public static readonly RgbWorkingSpaceBase EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F)));
/// <summary> /// <summary>
/// NTSC Rgb working space. /// NTSC Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); public static readonly RgbWorkingSpaceBase NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
/// <summary> /// <summary>
/// PAL/SECAM Rgb working space. /// PAL/SECAM Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); public static readonly RgbWorkingSpaceBase PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary> /// <summary>
/// ProPhoto Rgb working space. /// ProPhoto Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); public static readonly RgbWorkingSpaceBase ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F)));
/// <summary> /// <summary>
/// SMPTE-C Rgb working space. /// SMPTE-C Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); public static readonly RgbWorkingSpaceBase SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
/// <summary> /// <summary>
/// Wide Gamut Rgb working space. /// Wide Gamut Rgb working space.
/// </summary> /// </summary>
public static readonly RgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); public static readonly RgbWorkingSpaceBase WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
} }
} }

127
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -13,17 +12,28 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/> /// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// <see href="http://www.ijg.org/files/T-REC-T.871-201105-I!!PDF-E.pdf"/> /// <see href="http://www.ijg.org/files/T-REC-T.871-201105-I!!PDF-E.pdf"/>
/// </summary> /// </summary>
internal readonly struct YCbCr : IColorVector, IEquatable<YCbCr>, IAlmostEquatable<YCbCr, float> public readonly struct YCbCr : IEquatable<YCbCr>
{ {
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(255);
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Y;
/// <summary> /// <summary>
/// Vector which is used in clamping to the max value. /// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
private static readonly Vector3 VectorMax = new Vector3(255F); public readonly float Cb;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
private readonly Vector3 backingVector; public readonly float Cr;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct. /// Initializes a new instance of the <see cref="YCbCr"/> struct.
@ -31,7 +41,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="y">The y luminance component.</param> /// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param> /// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param> /// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public YCbCr(float y, float cb, float cr) public YCbCr(float y, float cb, float cr)
: this(new Vector3(y, cb, cr)) : this(new Vector3(y, cb, cr))
{ {
@ -41,117 +51,58 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="YCbCr"/> struct. /// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector representing the y, cb, cr components.</param> /// <param name="vector">The vector representing the y, cb, cr components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public YCbCr(Vector3 vector) public YCbCr(Vector3 vector)
{ {
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); vector = Vector3.Clamp(vector, Min, Max);
} this.Y = vector.X;
this.Cb = vector.Y;
/// <summary> this.Cr = vector.Z;
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Cb
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Cr
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
} }
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary> /// <summary>
/// Compares two <see cref="YCbCr"/> objects for equality. /// Compares two <see cref="YCbCr"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="YCbCr"/> on the left side of the operand.</param>
/// The <see cref="YCbCr"/> on the left side of the operand. /// <param name="right">The <see cref="YCbCr"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator ==(YCbCr left, YCbCr right) public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right);
{
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="YCbCr"/> objects for inequality. /// Compares two <see cref="YCbCr"/> objects for inequality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="YCbCr"/> on the left side of the operand.</param>
/// The <see cref="YCbCr"/> on the left side of the operand. /// <param name="right">The <see cref="YCbCr"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(YCbCr left, YCbCr right) public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right);
{
return !left.Equals(right);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() public override int GetHashCode()
{ {
return this.backingVector.GetHashCode(); int hash = this.Y.GetHashCode();
hash = HashHelpers.Combine(hash, this.Cb.GetHashCode());
return HashHelpers.Combine(hash, this.Cr.GetHashCode());
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})");
{
return this.Equals(default)
? "YCbCr [ Empty ]"
: $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object obj) => obj is YCbCr other && this.Equals(other);
{
return obj is YCbCr other && this.Equals(other);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(YCbCr other) public bool Equals(YCbCr other)
{ {
return this.backingVector.Equals(other.backingVector); return this.Y.Equals(other.Y)
} && this.Cb.Equals(other.Cb)
&& this.Cr.Equals(other.Cr);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(YCbCr other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
} }
} }
} }

45
src/ImageSharp/Common/Extensions/ComparableExtensions.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="byte"/> representing the clamped value. /// The <see cref="byte"/> representing the clamped value.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static byte Clamp(this byte value, byte min, byte max) public static byte Clamp(this byte value, byte min, byte max)
{ {
// Order is important here as someone might set min to higher than max. // Order is important here as someone might set min to higher than max.
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="int"/> representing the clamped value. /// The <see cref="int"/> representing the clamped value.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Clamp(this uint value, uint min, uint max) public static uint Clamp(this uint value, uint min, uint max)
{ {
if (value >= max) if (value >= max)
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="int"/> representing the clamped value. /// The <see cref="int"/> representing the clamped value.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Clamp(this int value, int min, int max) public static int Clamp(this int value, int min, int max)
{ {
if (value >= max) if (value >= max)
@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="float"/> representing the clamped value. /// The <see cref="float"/> representing the clamped value.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float Clamp(this float value, float min, float max) public static float Clamp(this float value, float min, float max)
{ {
if (value >= max) if (value >= max)
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="double"/> representing the clamped value. /// The <see cref="double"/> representing the clamped value.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static double Clamp(this double value, double min, double max) public static double Clamp(this double value, double min, double max)
{ {
if (value >= max) if (value >= max)
@ -136,38 +136,5 @@ namespace SixLabors.ImageSharp
return value; return value;
} }
/// <summary>
/// Converts an <see cref="int"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="int"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this int value)
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Converts an <see cref="float"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="float"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this float value)
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Converts an <see cref="double"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="double"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this double value)
{
return (byte)value.Clamp(0, 255);
}
} }
} }

30
src/ImageSharp/Common/Extensions/EncoderExtensions.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
#if !NETCOREAPP2_1
using System;
using System.Text;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Encoder"/> type.
/// </summary>
internal static unsafe class EncoderExtensions
{
/// <summary>
/// Gets a string from the provided buffer data.
/// </summary>
/// <param name="encoding">The encoding.</param>
/// <param name="buffer">The buffer.</param>
/// <returns>The string.</returns>
public static string GetString(this Encoding encoding, ReadOnlySpan<byte> buffer)
{
fixed (byte* bytes = buffer)
{
return encoding.GetString(bytes, buffer.Length);
}
}
}
}
#endif

113
src/ImageSharp/Common/Extensions/Vector4Extensions.cs

@ -1,113 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Vector4"/> struct.
/// </summary>
internal static class Vector4Extensions
{
/// <summary>
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <returns>The <see cref="Vector4"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Premultiply(this Vector4 source)
{
float w = source.W;
Vector4 premultiplied = source * w;
premultiplied.W = w;
return premultiplied;
}
/// <summary>
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(Vector4)"/>.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <returns>The <see cref="Vector4"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 UnPremultiply(this Vector4 source)
{
float w = source.W;
Vector4 unpremultiplied = source / w;
unpremultiplied.W = w;
return unpremultiplied;
}
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="linear">The <see cref="Vector4"/> whose signal to compress.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Compress(this Vector4 linear)
{
// TODO: Is there a faster way to do this?
return new Vector4(Compress(linear.X), Compress(linear.Y), Compress(linear.Z), linear.W);
}
/// <summary>
/// Expands an sRGB color signal to its linear equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="gamma">The <see cref="Rgba32"/> whose signal to expand.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Expand(this Vector4 gamma)
{
// TODO: Is there a faster way to do this?
return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W);
}
/// <summary>
/// Gets the compressed sRGB value from an linear signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to compress.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Compress(float signal)
{
if (signal <= 0.0031308F)
{
return signal * 12.92F;
}
return (1.055F * MathF.Pow(signal, 0.41666666F)) - 0.055F;
}
/// <summary>
/// Gets the expanded linear value from an sRGB signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to expand.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Expand(float signal)
{
if (signal <= 0.04045F)
{
return signal / 12.92F;
}
return MathF.Pow((signal + 0.055F) / 1.055F, 2.4F);
}
}
}

124
src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs

@ -0,0 +1,124 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for <see cref="DenseMatrix{T}"/>.
/// </summary>
internal static class DenseMatrixUtils
{
/// <summary>
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrix">The dense matrix.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRow">The target row.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="maxColumn">The maximum working area column.</param>
/// <param name="offsetColumn">The column offset to apply to source sampling.</param>
public static void Convolve<TPixel>(
in DenseMatrix<float> matrix,
Buffer2D<TPixel> sourcePixels,
Span<Vector4> targetRow,
int row,
int column,
int maxRow,
int maxColumn,
int offsetColumn)
where TPixel : struct, IPixel<TPixel>
{
Vector4 vector = default;
int matrixHeight = matrix.Rows;
int matrixWidth = matrix.Columns;
int radiusY = matrixHeight >> 1;
int radiusX = matrixWidth >> 1;
int sourceOffsetColumnBase = column + offsetColumn;
for (int y = 0; y < matrixHeight; y++)
{
int offsetY = (row + y - radiusY).Clamp(0, maxRow);
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
for (int x = 0; x < matrixWidth; x++)
{
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn);
var currentColor = sourceRowSpan[offsetX].ToVector4();
Vector4Utils.Premultiply(ref currentColor);
vector += matrix[y, x] * currentColor;
}
}
ref Vector4 target = ref targetRow[column];
vector.W = target.W;
Vector4Utils.UnPremultiply(ref vector);
target = vector;
}
/// <summary>
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the two kernel weight values.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="matrixY">The vertical dense matrix.</param>
/// <param name="matrixX">The horizontal dense matrix.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRow">The target row.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="maxColumn">The maximum working area column.</param>
/// <param name="offsetColumn">The column offset to apply to source sampling.</param>
public static void Convolve2D<TPixel>(
in DenseMatrix<float> matrixY,
in DenseMatrix<float> matrixX,
Buffer2D<TPixel> sourcePixels,
Span<Vector4> targetRow,
int row,
int column,
int maxRow,
int maxColumn,
int offsetColumn)
where TPixel : struct, IPixel<TPixel>
{
Vector4 vectorY = default;
Vector4 vectorX = default;
int matrixHeight = matrixY.Rows;
int matrixWidth = matrixY.Columns;
int radiusY = matrixHeight >> 1;
int radiusX = matrixWidth >> 1;
int sourceOffsetColumnBase = column + offsetColumn;
for (int y = 0; y < matrixHeight; y++)
{
int offsetY = (row + y - radiusY).Clamp(0, maxRow);
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
for (int x = 0; x < matrixWidth; x++)
{
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn);
var currentColor = sourceRowSpan[offsetX].ToVector4();
Vector4Utils.Premultiply(ref currentColor);
vectorX += matrixX[y, x] * currentColor;
vectorY += matrixY[y, x] * currentColor;
}
}
var vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY));
ref Vector4 target = ref targetRow[column];
vector.W = target.W;
Vector4Utils.UnPremultiply(ref vector);
target = vector;
}
}
}

44
src/ImageSharp/Common/Helpers/Guard.cs

@ -242,5 +242,49 @@ namespace SixLabors.ImageSharp
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
} }
} }
/// <summary>
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
Span<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
}
/// <summary>
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
ReadOnlySpan<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
}
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save