Browse Source

Merge remote-tracking branch 'upstream/master' into feature/adaptiveHistogramEqualization

# Conflicts:
#	src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
pull/673/head
popow 8 years ago
parent
commit
d8fd6f03b3
  1. 5
      .github/CONTRIBUTING.md
  2. 22
      README.md
  3. 4
      appveyor.yml
  4. 8
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  5. 2
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  6. 2
      src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
  7. 7
      src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
  8. 7
      src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
  9. 22
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  10. 39
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  11. 36
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  12. 52
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  13. 11
      src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
  14. 22
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  15. 6
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  16. 103
      src/ImageSharp/Advanced/AotCompilerTools.cs
  17. 143
      src/ImageSharp/ColorSpaces/CieLab.cs
  18. 151
      src/ImageSharp/ColorSpaces/CieLch.cs
  19. 149
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  20. 146
      src/ImageSharp/ColorSpaces/CieLuv.cs
  21. 117
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  22. 132
      src/ImageSharp/ColorSpaces/CieXyy.cs
  23. 132
      src/ImageSharp/ColorSpaces/CieXyz.cs
  24. 144
      src/ImageSharp/ColorSpaces/Cmyk.cs
  25. 36
      src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
  26. 38
      src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
  27. 36
      src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
  28. 35
      src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
  29. 89
      src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
  30. 105
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  31. 324
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  32. 318
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  33. 320
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  34. 295
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  35. 282
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  36. 309
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  37. 282
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  38. 282
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  39. 282
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  40. 285
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  41. 303
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  42. 284
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  43. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  44. 273
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  45. 114
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
  46. 55
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
  47. 24
      src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs
  48. 22
      src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs
  49. 49
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs
  50. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
  51. 16
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
  52. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
  53. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
  54. 16
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
  55. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
  56. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
  57. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
  58. 45
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
  59. 19
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
  60. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
  61. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
  62. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
  63. 51
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
  64. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
  65. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
  66. 39
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  67. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
  68. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
  69. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
  70. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
  71. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
  72. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs
  73. 97
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
  74. 21
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  75. 46
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs
  76. 35
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs
  77. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs
  78. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs
  79. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs
  80. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs
  81. 98
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  82. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs
  83. 66
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
  84. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
  85. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
  86. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
  87. 83
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
  88. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
  89. 61
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  90. 125
      src/ImageSharp/ColorSpaces/Hsl.cs
  91. 181
      src/ImageSharp/ColorSpaces/Hsv.cs
  92. 142
      src/ImageSharp/ColorSpaces/HunterLab.cs
  93. 28
      src/ImageSharp/ColorSpaces/IAlmostEquatable.cs
  94. 18
      src/ImageSharp/ColorSpaces/IColorVector.cs
  95. 37
      src/ImageSharp/ColorSpaces/ICompanding.cs
  96. 2
      src/ImageSharp/ColorSpaces/Illuminants.cs
  97. 163
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  98. 134
      src/ImageSharp/ColorSpaces/Lms.cs
  99. 172
      src/ImageSharp/ColorSpaces/Rgb.cs
  100. 43
      src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs

5
.github/CONTRIBUTING.md

@ -20,6 +20,11 @@
* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes. * Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
#### **Running tests and Debugging**
* Expected test output is pulled in as a submodule from the [ImageSharp.Tests.Images repository](https://github.com/SixLabors/Imagesharp.Tests.Images/tree/master/ReferenceOutput). To succesfully run tests, make sure that you have updated the submodules!
* Debugging (running tests in Debug mode) is only supported on .NET Core 2.1, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657)
#### **Do you have questions about consuming the library or the source code?** #### **Do you have questions about consuming the library or the source code?**
* Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General). * Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General).

22
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:
@ -83,24 +83,6 @@ 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
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
// 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:
@ -133,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.

4
appveyor.yml

@ -12,10 +12,10 @@ environment:
- target_framework: netcoreapp2.1 - target_framework: netcoreapp2.1
is_32bit: True is_32bit: True
- target_framework: net471 - target_framework: net472
is_32bit: False is_32bit: False
- target_framework: net471 - target_framework: net472
is_32bit: True is_32bit: True
- target_framework: net462 - target_framework: net462

8
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>
@ -38,8 +38,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta0007" /> <PackageReference Include="SixLabors.Fonts" Version="1.0.0-dev000119" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0007" /> <PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000102" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>

2
src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan);
} }
} }
} }

2
src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs

@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing
onLocalGradient); onLocalGradient);
TPixel resultColor = default; TPixel resultColor = default;
resultColor.PackFromVector4(result); resultColor.FromVector4(result);
return resultColor; return resultColor;
} }
} }

7
src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs

@ -140,7 +140,12 @@ namespace SixLabors.ImageSharp.Processing
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(this.source.MemoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); this.Blender.Blend(
this.source.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
} }
} }
} }

7
src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs

@ -170,7 +170,12 @@ namespace SixLabors.ImageSharp.Processing
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); this.Blender.Blend(
this.Target.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
} }
} }
} }

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

@ -6,6 +6,7 @@ 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.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// Combines two images together by blending the pixels. /// Combines two images together by blending the pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam> /// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam> /// <typeparam name="TPixelSrc">The pixel format of source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst> internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst> where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
@ -78,17 +79,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
minY, workingRect,
maxY,
configuration, configuration,
y => rows =>
{ {
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width); for (int y = rows.Min; y < rows.Max; y++)
Span<TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); {
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity); Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(configuration, background, background, foreground, this.Opacity);
}
}); });
} }
} }

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

@ -6,6 +6,7 @@ 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.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -52,17 +53,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
// If there's no reason for blending, then avoid it. // If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)) if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
{ {
ParallelFor.WithConfiguration( ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
minY,
maxY, ParallelHelper.IterateRows(
configuration, workingRect,
y => parallelSettings,
{ rows =>
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); {
}); for (int y = rows.Min; y < rows.Max; y++)
{
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
}
});
} }
else else
{ {
@ -85,16 +92,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ {
amount.GetSpan().Fill(1f); amount.GetSpan().Fill(1f);
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
minY, workingRect,
maxY,
configuration, configuration,
y => rows =>
{ {
int offsetY = y - startY; for (int y = rows.Min; y < rows.Max; y++)
int offsetX = minX - startX; {
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount.GetSpan(), offsetX, offsetY); applicator.Apply(amount.GetSpan(), offsetX, offsetY);
}
}); });
} }
} }

36
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -3,7 +3,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -82,11 +82,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
// we need to offset the pixel grid to account for when we outline a path. // we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to alline with the pixel grid. // region to align with the pixel grid.
float offset = 0.5f; float offset = 0.5f;
if (this.Options.Antialias) if (this.Options.Antialias)
{ {
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth; subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4) if (subpixelCount < 4)
{ {
@ -107,6 +107,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span<float> buffer = bBuffer.GetSpan(); Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan(); Span<float> scanline = bScanline.GetSpan();
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush);
for (int y = minY; y < maxY; y++) for (int y = minY; y < maxY; y++)
{ {
if (scanlineDirty) if (scanlineDirty)
@ -121,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int pointsFound = region.Scan(subPixel + offset, buffer, configuration); int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0) if (pointsFound == 0)
{ {
// nothing on this line skip // nothing on this line, skip
continue; continue;
} }
@ -168,16 +170,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ {
if (!this.Options.Antialias) if (!this.Options.Antialias)
{ {
bool hasOnes = false;
bool hasZeros = false;
for (int x = 0; x < scanlineWidth; x++) for (int x = 0; x < scanlineWidth; x++)
{ {
if (scanline[x] >= 0.5) if (scanline[x] >= 0.5)
{ {
scanline[x] = 1; scanline[x] = 1;
hasOnes = true;
} }
else else
{ {
scanline[x] = 0; scanline[x] = 0;
hasZeros = true;
}
}
if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
{
if (hasOnes)
{
source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrush.Color);
} }
continue;
} }
} }
@ -187,5 +203,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
} }
} }
} }
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{
solidBrush = this.Brush as SolidBrush<TPixel>;
if (solidBrush == null)
{
return false;
}
return this.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
} }
} }

52
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// <param name="font">The font we want to render with</param> /// <param name="font">The font we want to render with</param>
/// <param name="brush">The brush to source pixel colors from.</param> /// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param> /// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawign the text from.</param> /// <param name="location">The location on the image to start drawing the text from.</param>
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location) public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
{ {
Guard.NotNull(text, nameof(text)); Guard.NotNull(text, nameof(text));
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{ {
base.BeforeImageApply(source, sourceRectangle); base.BeforeImageApply(source, sourceRectangle);
// do everythign at the image level as we are deligating the processing down to other processors // do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{ {
ApplyKerning = this.Options.ApplyKerning, ApplyKerning = this.Options.ApplyKerning,
@ -97,7 +97,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null);
this.textRenderer.Options = (GraphicsOptions)this.Options; this.textRenderer.Options = (GraphicsOptions)this.Options;
TextRenderer.RenderTextTo(this.textRenderer, this.Text, style); var renderer = new TextRenderer(this.textRenderer);
renderer.RenderText(this.Text, style);
} }
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle) protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
@ -138,10 +139,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
fistRow = -startY; fistRow = -startY;
} }
int end = operation.Map.Height;
int maxHeight = source.Height - startY; int maxHeight = source.Height - startY;
end = Math.Min(end, maxHeight); int end = Math.Min(operation.Map.Height, maxHeight);
for (int row = fistRow; row < end; row++) for (int row = fistRow; row < end; row++)
{ {
@ -164,18 +163,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private class CachingGlyphRenderer : IGlyphRenderer, IDisposable private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
{ {
private PathBuilder builder; // just enough accuracy to allow for 1/8 pixel differences which
// later are accumulated while rendering, but do not grow into full pixel offsets
// The value 8 is benchmarked to:
// - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant)
// - Cache hit ratio above 60%
private const float AccuracyMultiple = 8;
private readonly PathBuilder builder;
private Point currentRenderPosition = default; private Point currentRenderPosition = default;
private GlyphRendererParameters currentGlyphRenderParams = default; private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
private int offset = 0; private readonly int offset = 0;
private PointF currentPoint = default(PointF); private PointF currentPoint = default(PointF);
private readonly Dictionary<GlyphRendererParameters, GlyphRenderData> glyphData = new Dictionary<GlyphRendererParameters, GlyphRenderData>(); private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>
glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>();
private bool renderOutline = false; private readonly bool renderOutline = false;
private bool renderFill = false; private readonly bool renderFill = false;
private bool raterizationRequired = false; private bool rasterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill) public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{ {
@ -213,17 +220,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
this.builder.StartFigure(); this.builder.StartFigure();
} }
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters paramters) public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters)
{ {
this.currentRenderPosition = Point.Truncate(bounds.Location); this.currentRenderPosition = Point.Truncate(bounds.Location);
PointF subPixelOffset = bounds.Location - this.currentRenderPosition;
subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple;
subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple;
// we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate // we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset); this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
this.currentGlyphRenderParams = paramters; this.currentGlyphRenderParams = (parameters, subPixelOffset);
if (this.glyphData.ContainsKey(paramters))
if (this.glyphData.ContainsKey(this.currentGlyphRenderParams))
{ {
// we have already drawn the glyph vectors skip trying again // we have already drawn the glyph vectors skip trying again
this.raterizationRequired = false; this.rasterizationRequired = false;
return false; return false;
} }
@ -233,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset)); this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
this.raterizationRequired = true; this.rasterizationRequired = true;
return true; return true;
} }
@ -252,7 +264,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
public void Dispose() public void Dispose()
{ {
foreach (KeyValuePair<GlyphRendererParameters, GlyphRenderData> kv in this.glyphData) foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData)
{ {
kv.Value.Dispose(); kv.Value.Dispose();
} }
@ -270,7 +282,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
GlyphRenderData renderData = default; GlyphRenderData renderData = default;
// has the glyoh been rendedered already???? // has the glyoh been rendedered already????
if (this.raterizationRequired) if (this.rasterizationRequired)
{ {
IPath path = this.builder.Build(); IPath path = this.builder.Build();

11
src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs

@ -95,9 +95,9 @@ namespace SixLabors.ImageSharp.Processing
// Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :) // Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :)
var maxColor = default(TPixel); var maxColor = default(TPixel);
maxColor.PackFromVector4(new Vector4(float.MaxValue)); maxColor.FromVector4(new Vector4(float.MaxValue));
var minColor = default(TPixel); var minColor = default(TPixel);
minColor.PackFromVector4(new Vector4(float.MinValue)); minColor.FromVector4(new Vector4(float.MinValue));
this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold;
} }
@ -158,7 +158,12 @@ namespace SixLabors.ImageSharp.Processing
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); this.Blender.Blend(
this.Target.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
} }
} }
} }

22
src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs

@ -89,13 +89,24 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x);
// constrain the spans to each other
if (destinationRow.Length > scanline.Length)
{
destinationRow = destinationRow.Slice(0, scanline.Length);
}
else
{
scanline = scanline.Slice(0, destinationRow.Length);
}
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
Configuration configuration = this.Target.Configuration;
if (this.Options.BlendPercentage == 1f) if (this.Options.BlendPercentage == 1f)
{ {
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline);
} }
else else
{ {
@ -108,7 +119,12 @@ namespace SixLabors.ImageSharp.Processing
amountSpan[i] = scanline[i] * this.Options.BlendPercentage; amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
} }
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan); this.Blender.Blend(
configuration,
destinationRow,
destinationRow,
this.Colors.GetSpan(),
amountSpan);
} }
} }
} }

6
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <returns>The span retuned from Pixel source</returns> /// <returns>The span returned from Pixel source</returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source) private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetSpan(); => source.PixelBuffer.GetSpan();
@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="row">The row.</param> /// <param name="row">The row.</param>
/// <returns> /// <returns>
/// The span retuned from Pixel source /// The span returned from Pixel source
/// </returns> /// </returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row) private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="row">The row.</param> /// <param name="row">The row.</param>
/// <returns> /// <returns>
/// The span retuned from Pixel source /// The span returned from Pixel source
/// </returns> /// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row) private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>

103
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -0,0 +1,103 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Unlike traditional Mono/.NET, code on the iPhone is statically compiled ahead of time instead of being
/// compiled on demand by a JIT compiler. This means there are a few limitations with respect to generics,
/// these are caused because not every possible generic instantiation can be determined up front at compile time.
/// The Aot Compiler is designed to overcome the limitations of this compiler.
/// </summary>
public static class AotCompilerTools
{
/// <summary>
/// Seeds the compiler using the given pixel format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public static void Seed<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
// This is we actually call all the individual methods you need to seed.
AotCompileOctreeQuantizer<TPixel>();
AotCompileWuQuantizer<TPixel>();
AotCompileDithering<TPixel>();
// TODO: Do the discovery work to figure out what works and what doesn't.
}
/// <summary>
/// Seeds the compiler using the given pixel formats.
/// </summary>
/// <typeparam name="TPixel">The first pixel format.</typeparam>
/// <typeparam name="TPixel2">The second pixel format.</typeparam>
public static void Seed<TPixel, TPixel2>()
where TPixel : struct, IPixel<TPixel>
where TPixel2 : struct, IPixel<TPixel2>
{
Seed<TPixel>();
Seed<TPixel2>();
}
/// <summary>
/// Seeds the compiler using the given pixel formats.
/// </summary>
/// <typeparam name="TPixel">The first pixel format.</typeparam>
/// <typeparam name="TPixel2">The second pixel format.</typeparam>
/// <typeparam name="TPixel3">The third pixel format.</typeparam>
public static void Seed<TPixel, TPixel2, TPixel3>()
where TPixel : struct, IPixel<TPixel>
where TPixel2 : struct, IPixel<TPixel2>
where TPixel3 : struct, IPixel<TPixel3>
{
Seed<TPixel, TPixel2>();
Seed<TPixel3>();
}
/// <summary>
/// This method doesn't actually do anything but serves an important purpose...
/// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an excepion:
/// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode."
/// The reason this happens is the SaveAsGif method makes haevy use of generics, which are too confusing for the AoT
/// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on
/// iOS so it bombs out.
/// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the
/// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!!
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileOctreeQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new OctreeFrameQuantizer<TPixel>(new OctreeQuantizer(false));
test.AotGetPalette();
}
/// <summary>
/// This method pre-seeds the WuQuantizer in the AoT compiler for iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileWuQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new WuFrameQuantizer<TPixel>(new WuQuantizer(false));
test.QuantizeFrame(new ImageFrame<TPixel>(Configuration.Default, 1, 1));
test.AotGetPalette();
}
/// <summary>
/// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileDithering<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new FloydSteinbergDiffuser();
TPixel pixel = default;
test.Dither<TPixel>(new ImageFrame<TPixel>(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
}
}
}

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>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,14 +1,18 @@
// 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 +27,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +61,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +94,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +160,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +225,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +258,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +291,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +324,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +357,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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +390,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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +423,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,14 +1,18 @@
// 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 +26,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +59,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +93,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +160,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +226,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +259,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +292,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +325,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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,10 +358,32 @@ 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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
@ -146,10 +391,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in 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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +424,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,16 +1,18 @@
// 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 +27,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +59,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +90,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +126,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +155,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +225,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +257,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +289,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +321,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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +353,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +385,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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +416,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,14 +1,18 @@
// 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 +28,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +61,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +94,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination)
{ {
return CieXyzAndCieXyyConverter.Convert(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +188,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +221,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +254,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +287,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +320,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +353,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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +386,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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +418,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,23 +1,25 @@
// 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();
private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter(); private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter();
private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); private static readonly HunterLabToCieXyzConverter
HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter();
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter;
@ -32,11 +34,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
return adapted; ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
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 +72,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +107,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +139,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);
/// <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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
return adapted; 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 +174,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +208,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +242,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +276,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +308,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +344,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +376,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>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +412,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>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +446,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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;
} }

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

@ -1,14 +1,18 @@
// 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 +28,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +61,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +94,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +160,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +226,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +259,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +292,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +325,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +358,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>
public void Convert(ReadOnlySpan<Lms> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination)
{ {
return CmykAndRgbConverter.Convert(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +418,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,14 +1,18 @@
// 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 +28,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +61,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +94,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +160,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +226,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +259,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>
public void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +292,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +325,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +358,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>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination)
{ {
return HslAndRgbConverter.Convert(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +418,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
} }
} }

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

@ -1,14 +1,18 @@
// 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 +28,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>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +61,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>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +94,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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +127,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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +160,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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +193,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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +226,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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +259,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>
public void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +292,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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +325,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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +358,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>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination)
{ {
return HsvAndRgbConverter.Convert(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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 +418,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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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,15 +1,290 @@
// 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>
/// 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>
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
@ -73,12 +348,10 @@ 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> /// <summary>

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

@ -1,18 +1,293 @@
// 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>
/// 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>
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// Performs the bulk conversion from <see cref="YCbCr"/> 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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr 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="CieLab"/> into a <see cref="LinearRgb"/> /// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
@ -77,13 +352,10 @@ 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> /// <summary>
@ -162,20 +434,5 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb); return this.ToLinearRgb(rgb);
} }
/// <summary>
/// Gets the correct converter for the given rgb working space.
/// </summary>
/// <param name="workingSpace">The target working space</param>
/// <returns>The <see cref="CieXyzToLinearRgbConverter"/></returns>
private CieXyzToLinearRgbConverter GetCieXyxToLinearRgbConverter(RgbWorkingSpace workingSpace)
{
if (this.cieXyzToLinearRgbConverter != null && this.cieXyzToLinearRgbConverter.TargetWorkingSpace.Equals(workingSpace))
{
return this.cieXyzToLinearRgbConverter;
}
return this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(workingSpace);
}
} }
} }

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

@ -1,15 +1,290 @@
// 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>
/// 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>
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/> /// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
/// </summary> /// </summary>
@ -70,10 +345,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </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);
{
return this.cachedCieXyzAndLmsConverter.Convert(color);
}
/// <summary> /// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Lms"/> /// Converts a <see cref="Cmyk"/> into a <see cref="Lms"/>

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

@ -1,17 +1,294 @@
// 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();
/// <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>
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/> /// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/>
/// </summary> /// </summary>

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

@ -1,35 +1,279 @@
// 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();
/// <summary> /// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieLab"/> 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 CieLab color) public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination)
{ {
var xyzColor = this.ToCieXyz(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
return this.ToYCbCr(xyzColor); 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"/> /// Performs the bulk conversion from <see cref="CieLch"/> 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>
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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>
/// 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>
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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);
}
}
/// <summary>
/// Converts a <see cref="CieLab"/> 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 CieLch color) public YCbCr ToYCbCr(in CieLab color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
@ -37,11 +281,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="YCbCr"/> /// Converts a <see cref="CieLch"/> 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 CieLchuv color) public YCbCr ToYCbCr(in CieLch color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
@ -161,9 +405,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </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);
{
return YCbCrAndRgbConverter.Convert(color);
}
} }
} }

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;
}
}

24
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,30 @@ 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>
void Transform(
ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
CieXyz sourceWhitePoint,
in CieXyz destinationWhitePoint);
} }
} }

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);
}
}
}

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

@ -4,21 +4,25 @@
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:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;
float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float a = c * MathF.Cos(hRadians); float a = c * MathF.Cos(hRadians);
float b = c * MathF.Sin(hRadians); float b = c * MathF.Sin(hRadians);

16
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:
@ -20,10 +24,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
float l = input.L, a = input.A, b = input.B; float l = input.L, a = input.A, b = input.B;
float c = MathF.Sqrt((a * a) + (b * b)); float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a); float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = GeometryUtilities.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);
} }
} }
} }

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

@ -4,21 +4,25 @@
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:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;
float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float u = c * MathF.Cos(hRadians); float u = c * MathF.Cos(hRadians);
float v = c * MathF.Sin(hRadians); float v = c * MathF.Sin(hRadians);

16
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:
@ -20,10 +24,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
float l = input.L, a = input.U, b = input.V; float l = input.L, a = input.U, b = input.V;
float c = MathF.Sqrt((a * a) + (b * b)); float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a); float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = GeometryUtilities.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));
}
} }
} }

29
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
@ -46,9 +45,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
float ka = ComputeKa(this.HunterLabWhitePoint); float ka = ComputeKa(this.HunterLabWhitePoint);
float kb = ComputeKb(this.HunterLabWhitePoint); float kb = ComputeKb(this.HunterLabWhitePoint);
float l = 100 * MathF.Sqrt(y / yn); float yByYn = y / yn;
float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn)); float sqrtYbyYn = MathF.Sqrt(yByYn);
float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn)); float l = 100 * sqrtYbyYn;
float a = ka * (((x / xn) - yByYn) / sqrtYbyYn);
float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn);
if (float.IsNaN(a)) if (float.IsNaN(a))
{ {

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;

39
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs

@ -0,0 +1,39 @@
// 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
{
/// <summary>
/// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary>
internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{
/// <summary>
/// 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)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float l = input.L, a = input.A, b = input.B;
float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z;
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
float pow = ImageMaths.Pow2(l / 100F);
float sqrtPow = MathF.Sqrt(pow);
float y = pow * yn;
float x = (((a / ka) * sqrtPow) + pow) * xn;
float z = (((b / kb) * sqrtPow) - pow) * (-zn);
return new CieXyz(x, y, z);
}
}
}

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;

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.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.HunterLabColorSapce
{
/// <summary>
/// Color converter between HunterLab and CieXyz
/// </summary>
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz>
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(in HunterLab input)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float l = input.L, a = input.A, b = input.B;
float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z;
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
float y = MathF.Pow(l / 100F, 2) * yn;
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);
return new CieXyz(x, y, 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);
}
}

61
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,59 @@ 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(
ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
CieXyz sourceWhitePoint,
in CieXyz destinationWhitePoint)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
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)));
} }
} }

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

Loading…
Cancel
Save