Browse Source

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

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

13
.github/ISSUE_TEMPLATE/ask-question.md

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

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

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

13
.github/ISSUE_TEMPLATE/feature-request.md

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

4
ImageSharp.sln

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

25
README.md

@ -22,7 +22,7 @@ Designed to democratize image processing, ImageSharp brings you an incredibly po
Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments.
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
For all SixLabors projects, including ImageSharp:
@ -70,6 +70,9 @@ Our API is designed to be simple to consume. Here's an example of the code requi
On platforms supporting netstandard 1.3+
```csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
// Image.Load(string path) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(string path))
using (Image<Rgba32> image = Image.Load("foo.jpg"))
@ -80,25 +83,13 @@ using (Image<Rgba32> image = Image.Load("foo.jpg"))
image.Save("bar.jpg"); // Automatic encoder selected based on extension.
}
```
On netstandard 1.1 - 1.2
```csharp
// Image.Load(Stream stream) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(Stream stream))
using (FileStream stream = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
using (Image<Rgba32> image = Image.Load(stream))
{
image.Mutate(x => x
.Resize(image.Width / 2, image.Height / 2)
.Grayscale());
image.Save(output);
}
```
Setting individual pixel values can be performed as follows:
```csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
// Individual pixels
using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
{
@ -124,7 +115,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!)
Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:
- [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.

2
appveyor.yml

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

2
build.ps1

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

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

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

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

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

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

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

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

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

143
src/ImageSharp/ColorSpaces/CieLab.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents a CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal readonly struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float>
public readonly struct CieLab : IEquatable<CieLab>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <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>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// 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="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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="b">The b (blue - yellow) component.</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)
: this(new Vector3(l, a, b), whitePoint)
{
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,126 +84,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(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;
}
/// <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>
/// Compares two <see cref="CieLab"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLab left, CieLab right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLab left, CieLab right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLab left, CieLab right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
/// <inheritdoc/>
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLab [Empty]"
: $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLab other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLab other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
/// <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.
using System;
using System.ComponentModel;
using System.Numerics;
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.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </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>
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <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>
private readonly Vector3 backingVector;
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// 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="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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="h">The hue in degrees.</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)
: this(new Vector3(l, c, h), whitePoint)
{
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,129 +87,64 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
}
/// <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>
/// Compares two <see cref="CieLch"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLch"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLch"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLch left, CieLch right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLch left, CieLch right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLch"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLch"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLch"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLch left, CieLch right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right);
/// <inheritdoc/>
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLch [Empty]"
: $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]";
}
public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj is CieLch other && this.Equals(other);
}
[MethodImpl(InliningOptions.ShortMethod)]
public override bool Equals(object obj) => obj is CieLch other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
/// <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>
/// Computes the saturation of the color (chroma normalized by lightness)
/// </summary>
@ -197,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100.
/// </remarks>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public float Saturation()
{
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.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -10,10 +9,13 @@ namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// 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>
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>
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <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>
private readonly Vector3 backingVector;
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// 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="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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="h">The hue in degrees.</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)
: this(new Vector3(l, c, h), whitePoint)
: this(new Vector3(l, c, h), whitePoint)
{
}
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,127 +87,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(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;
}
/// <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>
/// Compares two <see cref="CieLchuv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLchuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLchuv"/> 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 ==(CieLchuv left, CieLchuv right)
{
return left.Equals(right);
}
public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLchuv"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLchuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLchuv"/> 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 !=(CieLchuv left, CieLchuv right)
{
return !left.Equals(right);
}
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLchuv [Empty]"
: $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}";
}
public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLchuv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLchuv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
/// <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>
/// Computes the saturation of the color (chroma normalized by lightness)
/// </summary>
@ -195,7 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100.
/// </remarks>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public float Saturation()
{
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.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// attempted perceptual uniformity
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
/// </summary>
internal readonly struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float>
public readonly struct CieLuv : IEquatable<CieLuv>
{
/// <summary>
/// D65 standard illuminant.
@ -23,9 +22,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <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>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// 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="v">The red-green chromaticity coordinate of the given whitepoint.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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="v">The red-green chromaticity coordinate of the given whitepoint.</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)
: this(new Vector3(l, u, v), whitePoint)
{
@ -58,7 +75,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -69,126 +86,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
}
/// <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>
/// Compares two <see cref="CieLuv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLuv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLuv left, CieLuv right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLuv"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLuv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLuv left, CieLuv right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
/// <inheritdoc/>
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLuv [ Empty ]"
: $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLuv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLuv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLuv other)
{
return this.backingVector.Equals(other.backingVector)
&& 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;
return this.L.Equals(other.L)
&& this.U.Equals(other.U)
&& this.V.Equals(other.V)
&& this.WhitePoint.Equals(other.WhitePoint);
}
}
}

117
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
// ReSharper disable CompareOfFloatsByEqualityOperator
@ -12,45 +10,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents the coordinates of CIEXY chromaticity space
/// </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>
/// Gets the chromaticity X-coordinate.
/// </summary>
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
public readonly float X;
/// <summary>
/// Gets the chromaticity Y-coordinate
@ -58,85 +26,54 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>
/// Ranges usually from 0 to 1.
/// </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)]
get => this.backingVector.Y;
this.X = x;
this.Y = y;
}
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
}
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
/// <inheritdoc/>
public override string ToString()
{
return this.Equals(default)
? "CieXyChromaticityCoordinates [Empty]"
: $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]";
}
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyChromaticityCoordinates other)
{
// 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;
}
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
}
}

132
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// 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"/>
/// </summary>
internal readonly struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float>
public readonly struct CieXyy : IEquatable<CieXyy>
{
/// <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>
private readonly Vector3 backingVector;
public readonly float Yl;
/// <summary>
/// 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="y">The y chroma component.</param>
/// <param name="yl">The y luminance component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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>
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
/// </summary>
/// <param name="vector">The vector representing the x, y, Y components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy(Vector3 vector)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
// Not clamping as documentation about this space only indicates "usual" ranges
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>
/// Compares two <see cref="CieXyy"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyy"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyy"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyy left, CieXyy right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyy"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyy"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyy"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyy left, CieXyy right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieXyy [ Empty ]"
: $"CieXyy [ X={this.X:#0.##}, Y={this.Y:#0.##}, Yl={this.Yl:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyy other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieXyy other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyy other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Yl.Equals(other.Yl);
}
}
}

132
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE XYZ 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#Definition_of_the_CIE_XYZ_color_space"/>
/// </summary>
internal readonly struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float>
public readonly struct CieXyz : IEquatable<CieXyz>
{
/// <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>
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>
/// 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="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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz(float x, float y, float z)
: this(new Vector3(x, y, z))
{
@ -38,116 +50,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieXyz(Vector3 vector)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
}
/// <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;
// Not clamping as documentation about this space only indicates "usual" ranges
this.X = vector.X;
this.Y = vector.Y;
this.Z = vector.Z;
}
/// <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>
/// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyz"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyz left, CieXyz right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyz"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyz left, CieXyz right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyz left, CieXyz right) => !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/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieXyz [ Empty ]"
: $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyz other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieXyz other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyz other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Z.Equals(other.Z);
}
}
}

144
src/ImageSharp/ColorSpaces/Cmyk.cs

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

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.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Performs chromatic adaptation on the various color spaces.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// 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>
/// <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>
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>
/// 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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLab Adapt(in CieLab color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLch Adapt(in CieLch color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieLab labColor = this.ToCieLab(color);
var labColor = this.ToCieLab(color);
return this.ToCieLch(labColor);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLchuv Adapt(in CieLchuv color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieLuv luvColor = this.ToCieLuv(color);
var luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(luvColor);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLuv Adapt(in CieLuv color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public HunterLab Adapt(in HunterLab color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public LinearRgb Adapt(in LinearRgb color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace))
if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace))
{
return color;
}
@ -155,21 +134,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converterToXYZ.Convert(color);
// 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
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace);
return converterToRGB.Convert(adapted);
return this.cieXyzToLinearRgbConverter.Convert(adapted);
}
/// <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>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
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);
return this.ToRgb(linearOutput);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -2,100 +2,60 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <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>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// The default whitepoint used for converting to CieLab
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
private Matrix4x4 transformationMatrix;
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter;
// Options.
private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions();
private readonly Matrix4x4 lmsAdaptationMatrix;
private readonly CieXyz whitePoint;
private readonly CieXyz targetLuvWhitePoint;
private readonly CieXyz targetLabWhitePoint;
private readonly CieXyz targetHunterLabWhitePoint;
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>
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary>
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>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
/// 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.
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public Matrix4x4 LmsAdaptationMatrix
/// <param name="options">The configuration options.</param>
public ColorSpaceConverter(ColorSpaceConverterOptions options)
{
get => this.transformationMatrix;
set
{
this.transformationMatrix = value;
if (this.cachedCieXyzAndLmsConverter == null)
{
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value);
}
else
{
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value;
}
}
Guard.NotNull(options, nameof(options));
this.whitePoint = options.WhitePoint;
this.targetLuvWhitePoint = options.TargetLuvWhitePoint;
this.targetLabWhitePoint = options.TargetLabWhitePoint;
this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint;
this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace;
this.chromaticAdaptation = options.ChromaticAdaptation;
this.performChromaticAdaptation = this.chromaticAdaptation != null;
this.lmsAdaptationMatrix = options.LmsAdaptationMatrix;
this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix);
this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint);
this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint);
this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint);
this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace);
}
/// <summary>
/// Gets a value indicating whether chromatic adaptation has been performed.
/// </summary>
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null;
}
}

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

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

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

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

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

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

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

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

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

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieLchToCieLabConverter : IColorConversion<CieLch, CieLab>
internal sealed class CieLchToCieLabConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// Conversion algorithm described here:

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

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal class CieLabToCieLchConverter : IColorConversion<CieLab, CieLch>
internal sealed class CieLabToCieLchConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees = hDegrees % 360;
hDegrees %= 360;
// Make sure it's not negative.
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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
/// </summary>
internal class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz>
internal sealed class CieLabToCieXyzConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// 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 fz = fy - (b / 200F);
float fx3 = MathF.Pow(fx, 3F);
float fz3 = MathF.Pow(fz, 3F);
float fx3 = ImageMaths.Pow3(fx);
float fz3 = ImageMaths.Pow3(fz);
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 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)
xr = xr.Clamp(0, 1F);
yr = yr.Clamp(0, 1F);
zr = zr.Clamp(0, 1F);
var xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One);
float x = xr * wx;
float y = yr * wy;
float z = zr * wz;
return new CieXyz(x, y, z);
Vector3 xyz = xyzr * wxyz;
return new CieXyz(xyz);
}
}
}

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

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieLchuvToCieLuvConverter : IColorConversion<CieLchuv, CieLuv>
internal sealed class CieLchuvToCieLuvConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// Conversion algorithm described here:

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

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal class CieLuvToCieLchuvConverter : IColorConversion<CieLuv, CieLchuv>
internal sealed class CieLuvToCieLchuvConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees = hDegrees % 360;
hDegrees %= 360;
// Make sure it's not negative.
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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
/// </summary>
internal class CieLuvToCieXyzConverter : IColorConversion<CieLuv, CieXyz>
internal sealed class CieLuvToCieXyzConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// 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 y = l > CieConstants.Kappa * CieConstants.Epsilon
? MathF.Pow((l + 16) / 116, 3)
? ImageMaths.Pow3((l + 16) / 116)
: l / CieConstants.Kappa;
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3;
float b = -5 * y;
float c = -0.3333333F;
const float c = -0.3333333F;
float d = y * ((39 * l / (v + (13 * l * v0))) - 5);
float x = (d - b) / (a - c);
@ -56,21 +58,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeU0(in CieXyz input)
{
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
/// <summary>
/// Calculates the red-green chromacity based on the given whitepoint.
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeV0(in CieXyz input)
{
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CIE XYZ and CIE xyY
/// <see href="http://www.brucelindbloom.com/"/> for formulas.
/// </summary>
internal class CieXyzAndCieXyyConverter : IColorConversion<CieXyz, CieXyy>, IColorConversion<CieXyy, CieXyz>
internal sealed class CieXyzAndCieXyyConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
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;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// 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>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKa(CieXyz whitePoint)
{
if (whitePoint.Equals(Illuminants.C))
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// </summary>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKb(CieXyz whitePoint)
{
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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CIE XYZ and LMS
/// Color converter between <see cref="CieXyz"/> and <see cref="Lms"/>
/// </summary>
internal class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz>
internal sealed class CieXyzAndLmsConverter
{
/// <summary>
/// Default transformation matrix used, when no other is set. (Bradford)
@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter()
: this(DefaultTransformationMatrix)
{
@ -36,41 +35,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
/// if not set <see cref="DefaultTransformationMatrix"/> will be used.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix)
{
this.TransformationMatrix = transformationMatrix;
this.transformationMatrix = transformationMatrix;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
/// <summary>
/// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain).
/// <see cref="LmsAdaptationMatrix"/>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="Lms"/> type.
/// </summary>
public Matrix4x4 TransformationMatrix
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.transformationMatrix;
set
{
this.transformationMatrix = value;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab>
internal sealed class CieXyzToCieLabConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLabConverter()
: this(CieLab.DefaultWhitePoint)
{
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary>
/// <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>
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz LabWhitePoint { get; }
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// 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.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>.
/// </summary>
internal class CieXyzToCieLuvConverter : IColorConversion<CieXyz, CieLuv>
internal sealed class CieXyzToCieLuvConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLuvConverter()
: this(CieLuv.DefaultWhitePoint)
{
@ -25,19 +23,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary>
/// <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>
/// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz LuvWhitePoint { get; }
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ComputeUp(in CieXyz input)
{
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
/// <summary>
/// Calculates the red-green chromacity based on the given whitepoint.
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeVp(in CieXyz input)
{
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
}

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

@ -4,17 +4,16 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CieXyz and HunterLab
/// Color converter between <see cref="CieXyz"/> and <see cref="HunterLab"/>
/// </summary>
internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion<CieXyz, HunterLab>
internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToHunterLabConverter()
: this(HunterLab.DefaultWhitePoint)
{
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
/// <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>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz HunterLabWhitePoint { get; }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab

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

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CieXyz and LinearRgb
/// Color converter between <see cref="CieXyz"/> and <see cref="LinearRgb"/>
/// </summary>
internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<CieXyz, LinearRgb>
internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase
{
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.
/// </summary>
/// <param name="workingSpace">The target working space.</param>
public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace)
public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace)
{
this.TargetWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,13 +34,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary>
/// Gets the target working space
/// </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)
{
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);
}
}

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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HSL and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal class HslAndRgbConverter : IColorConversion<Hsl, Rgb>, IColorConversion<Rgb, Hsl>
internal sealed class HslAndRgbConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
float rangedH = input.H / 360F;
@ -43,8 +47,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
float r = input.R;
@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float GetColorComponent(float first, float second, float third)
{
third = MoveIntoRange(third);
@ -134,16 +142,16 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float MoveIntoRange(float value)
{
if (value < 0F)
{
value += 1F;
value++;
}
else if (value > 1F)
{
value -= 1F;
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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HSV and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal class HsvAndRgbConverter : IColorConversion<Hsv, Rgb>, IColorConversion<Rgb, Hsv>
internal sealed class HsvAndRgbConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
float s = input.S;
@ -75,8 +79,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
float r = input.R;

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

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HunterLab and CieXyz
/// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary>
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz>
internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <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
@ -22,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
float y = MathF.Pow(l / 100F, 2) * yn;
float y = ImageMaths.Pow2(l / 100F) * yn;
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);

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

@ -3,10 +3,10 @@
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <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>
internal abstract class LinearRgbAndCieXyzConverterBase
{
@ -15,10 +15,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// </summary>
/// <param name="workingSpace">The Rgb working space.</param>
/// <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));
RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates;
float xr = chromaticity.R.X;
@ -42,23 +41,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
var xyzMatrix = new Matrix4x4
{
M11 = mXr, M21 = mXg, M31 = mXb,
M12 = Yr, M22 = Yg, M32 = Yb,
M13 = mZr, M23 = mZg, M33 = mZb,
M11 = mXr,
M21 = mXg,
M31 = mXb,
M12 = Yr,
M22 = Yg,
M32 = Yb,
M13 = mZr,
M23 = mZg,
M33 = mZb,
M44 = 1F
};
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
// TODO: Is there a built in method for this multiplication?
return new Matrix4x4
{
M11 = vector.X * mXr, M21 = vector.Y * mXg, 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,
M11 = vector.X * mXr,
M21 = vector.Y * mXg,
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
};
}

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.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between LinearRgb and CieXyz
/// Color converter between <see cref="LinearRgb"/> and <see cref="CieXyz"/>
/// </summary>
internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<LinearRgb, CieXyz>
internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase
{
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.
/// </summary>
/// <param name="workingSpace">The target working space.</param>
public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace)
public LinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace)
{
this.SourceWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,14 +34,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary>
/// Gets the source working space
/// </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)
{
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);
}
}

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.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <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.
/// </summary>
internal class YCbCrAndRgbConverter : IColorConversion<YCbCr, Rgb>, IColorConversion<Rgb, YCbCr>
internal sealed class YCbCrAndRgbConverter
{
private static readonly Vector3 MaxBytes = new Vector3(255F);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
float y = input.Y;
@ -30,11 +34,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
return new Rgb(new Vector3(r, g, b) / MaxBytes);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// 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)
{
Vector3 rgb = input.Vector * MaxBytes;
Vector3 rgb = input.ToVector3() * MaxBytes;
float r = rgb.X;
float g = rgb.Y;
float b = rgb.Z;

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

@ -3,11 +3,10 @@
using System.Numerics;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <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"/>
/// </summary>
/// <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
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf
/// </remarks>
internal static class LmsAdaptationMatrix
public static class LmsAdaptationMatrix
{
/// <summary>
/// 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
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F,
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F,
M31 = 0, M32 = 0, M33 = 0.91822F,
M11 = 0.40024F,
M12 = 0.7076F,
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.
});
@ -37,9 +42,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 VonKriesHPE
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F,
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F,
M31 = 0, M32 = 0, M33 = 1F,
M11 = 0.3897F,
M12 = 0.6890F,
M13 = -0.0787F,
M21 = -0.2298F,
M22 = 1.1834F,
M23 = 0.0464F,
M31 = 0,
M32 = 0,
M33 = 1F,
M44 = 1F
});
@ -54,9 +65,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 Bradford
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F,
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F,
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F,
M11 = 0.8951F,
M12 = 0.2664F,
M13 = -0.1614F,
M21 = -0.7502F,
M22 = 1.7135F,
M23 = 0.0367F,
M31 = 0.0389F,
M32 = -0.0685F,
M33 = 1.0296F,
M44 = 1F
});
@ -65,35 +82,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// </summary>
public static readonly Matrix4x4 BradfordSharp
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F,
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F,
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F,
M44 = 1F
});
{
M11 = 1.2694F,
M12 = -0.0988F,
M13 = -0.1706F,
M21 = -0.8364F,
M22 = 1.8006F,
M23 = 0.0357F,
M31 = 0.0297F,
M32 = -0.0315F,
M33 = 1.0018F,
M44 = 1F
});
/// <summary>
/// CMCCAT2000 (fitted from all available color data sets)
/// </summary>
public static readonly Matrix4x4 CMCCAT2000
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F,
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F,
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F,
M44 = 1F
});
{
M11 = 0.7982F,
M12 = 0.3389F,
M13 = -0.1371F,
M21 = -0.5918F,
M22 = 1.5512F,
M23 = 0.0406F,
M31 = 0.0008F,
M32 = 0.239F,
M33 = 0.9753F,
M44 = 1F
});
/// <summary>
/// CAT02 (optimized for minimizing CIELAB differences)
/// </summary>
public static readonly Matrix4x4 CAT02
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F,
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F,
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F,
M44 = 1F
});
{
M11 = 0.7328F,
M12 = 0.4296F,
M13 = -0.1624F,
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;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="RgbWorkingSpace"/>.
/// One of the specifiers of <see cref="RgbWorkingSpaceBase"/>.
/// </summary>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{
/// <summary>
/// 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; }
/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for equality.
/// </summary>
/// <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 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>
/// <returns>
/// 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>
/// Compares two <see cref="CieLab"/> objects for inequality
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for inequality
/// </summary>
/// <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 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>
/// <returns>
/// 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();
hashCode = (hashCode * 397) ^ this.G.GetHashCode();
hashCode = (hashCode * 397) ^ this.B.GetHashCode();
return hashCode;
return (hashCode * 397) ^ this.B.GetHashCode();
}
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

125
src/ImageSharp/ColorSpaces/Hsl.cs

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

181
src/ImageSharp/ColorSpaces/Hsv.cs

@ -2,28 +2,36 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
/// </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>
/// Max range used for clamping.
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
public readonly float H;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </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>
/// 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="s">The s saturation 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)
: this(new Vector3(h, s, v))
{
@ -41,168 +49,59 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Hsv"/> struct.
/// </summary>
/// <param name="vector">The vector representing the h, s, v components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
}
/// <summary>
/// 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);
vector = Vector3.Clamp(vector, Min, Max);
this.H = vector.X;
this.S = vector.Y;
this.V = vector.Z;
}
/// <summary>
/// Compares two <see cref="Hsv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Hsv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Hsv left, Hsv right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Hsv left, Hsv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Hsv"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Hsv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsv left, Hsv right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Hsv [ Empty ]"
: $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Hsv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Hsv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsv other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.V.Equals(other.V);
}
}
}

142
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an Hunter LAB color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal readonly struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float>
public readonly struct HunterLab : IEquatable<HunterLab>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.C;
/// <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>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// 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="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(float l, float a, float b)
: 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="b">The b (blue - yellow) component.</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)
: this(new Vector3(l, a, b), whitePoint)
{
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,126 +84,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l a b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
}
/// <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>
/// Compares two <see cref="HunterLab"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HunterLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="HunterLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="HunterLab"/> 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 ==(HunterLab left, HunterLab right)
{
return left.Equals(right);
}
public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="HunterLab"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="HunterLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="HunterLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="HunterLab"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(HunterLab left, HunterLab right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "HunterLab [Empty]"
: $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is HunterLab other && this.Equals(other);
}
public override bool Equals(object obj) => obj is HunterLab other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
/// <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:
/// http://en.wikipedia.org/wiki/Standard_illuminant
/// </remarks>
internal static class Illuminants
public static class Illuminants
{
/// <summary>
/// Incandescent / Tungsten

163
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -4,24 +4,45 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <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>
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>
/// The default LinearRgb working space.
/// </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>
/// The backing vector for SIMD support.
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
private readonly Vector3 backingVector;
public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary>
/// 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="g">The green 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)
: 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="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace)
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(float r, float g, float b, RgbWorkingSpaceBase 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.
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector)
: this(vector, DefaultWorkingSpace)
{
@ -63,126 +84,68 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The LinearRgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
{
// 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;
}
/// <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>
/// Compares two <see cref="LinearRgb"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="LinearRgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="LinearRgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(LinearRgb left, LinearRgb right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(LinearRgb left, LinearRgb right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="LinearRgb"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="LinearRgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="LinearRgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(LinearRgb left, LinearRgb right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(LinearRgb left, LinearRgb right) => !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/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "LinearRgb [ Empty ]"
: $"LinearRgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is LinearRgb other && this.Equals(other);
}
public override bool Equals(object obj) => obj is LinearRgb other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(LinearRgb other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}
}

134
src/ImageSharp/ColorSpaces/Lms.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -13,12 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
/// </summary>
internal readonly struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float>
public readonly struct Lms : IEquatable<Lms>
{
/// <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>
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>
/// 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="m">M represents the responsivity at medium 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)
: this(new Vector3(l, m, s))
{
@ -36,119 +48,65 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Lms"/> struct.
/// </summary>
/// <param name="vector">The vector representing the l, m, s components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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)]
get => this.backingVector.Y;
// Not clamping as documentation about this space only indicates "usual" ranges
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>
/// Compares two <see cref="Lms"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Lms"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Lms"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Lms left, Lms right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Lms left, Lms right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Lms"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Lms"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Lms"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Lms left, Lms right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Lms left, Lms right) => !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/>
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/>
public override string ToString()
{
return this.Equals(default)
? "Lms [ Empty ]"
: $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Lms other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Lms other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Lms other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.L.Equals(other.L)
&& this.M.Equals(other.M)
&& this.S.Equals(other.S);
}
}
}

172
src/ImageSharp/ColorSpaces/Rgb.cs

@ -4,25 +4,46 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents an RGB color with specified <see cref="RgbWorkingSpace"/> working space
/// Represents an RGB color with specified <see cref="RgbWorkingSpaceBase"/> working space.
/// </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>
/// The default rgb working space
/// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <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>
private readonly Vector3 backingVector;
public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary>
/// 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="g">The green 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)
: 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="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace)
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(float r, float g, float b, RgbWorkingSpaceBase 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.
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector)
: this(vector, DefaultWorkingSpace)
{
@ -64,68 +85,33 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
{
// 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;
}
/// <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 Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a
/// <see cref="Rgb"/>.
/// </summary>
public RgbWorkingSpace WorkingSpace { get; }
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgb24 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Rgb"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Rgba32"/> to convert.
/// </param>
/// <returns>
/// 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);
}
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgba32 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Compares two <see cref="Rgb"/> objects for equality.
@ -139,66 +125,48 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rgb left, Rgb right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(Rgb left, Rgb right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Rgb"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Rgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgb left, Rgb right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Rgb left, Rgb right) => !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/>
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/>
public override string ToString()
{
return this.Equals(default)
? "Rgb [ Empty ]"
: $"Rgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Rgb other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Rgb other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Rgb other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <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;
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}
}

43
src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// 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
namespace SixLabors.ImageSharp.ColorSpaces
@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Chromaticity coordinates taken from:
/// <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/>
/// </summary>
internal static class RgbWorkingSpaces
public static class RgbWorkingSpaces
{
/// <summary>
/// sRgb working space.
@ -19,97 +20,97 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Uses proper companding function, according to:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/>
/// </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>
/// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>).
/// See also <see cref="SRgb"/>.
/// </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>
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
/// </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>
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
/// </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>
/// ECI Rgb v2 working space.
/// </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>
/// Adobe Rgb (1998) working space.
/// </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>
/// Apple sRgb working space.
/// </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>
/// Best Rgb working space.
/// </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>
/// Beta Rgb working space.
/// </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>
/// Bruce Rgb working space.
/// </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>
/// CIE Rgb working space.
/// </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>
/// ColorMatch Rgb working space.
/// </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>
/// Don Rgb 4 working space.
/// </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>
/// Ekta Space PS5 working space.
/// </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>
/// NTSC Rgb working space.
/// </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>
/// PAL/SECAM Rgb working space.
/// </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>
/// ProPhoto Rgb working space.
/// </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>
/// SMPTE-C Rgb working space.
/// </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>
/// Wide Gamut Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
public static readonly RgbWorkingSpaceBase WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
}
}

127
src/ImageSharp/ColorSpaces/YCbCr.cs

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save