Browse Source

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

pull/119/head
James Jackson-South 8 years ago
parent
commit
f0ead2d02d
  1. 5
      .github/CONTRIBUTING.md
  2. 4
      appveyor.yml
  3. 4
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 2
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  5. 2
      src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
  6. 7
      src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
  7. 7
      src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
  8. 4
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  9. 36
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  10. 52
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  11. 11
      src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
  12. 22
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  13. 6
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  14. 103
      src/ImageSharp/Advanced/AotCompilerTools.cs
  15. 78
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  16. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  17. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  18. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  19. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  20. 82
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  21. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  22. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  23. 79
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  24. 368
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  25. 371
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  26. 356
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  27. 377
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  28. 351
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  29. 7
      src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs
  30. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
  31. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
  32. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
  33. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
  34. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
  35. 9
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  36. 9
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  37. 5
      src/ImageSharp/Common/Extensions/EncoderExtensions.cs
  38. 232
      src/ImageSharp/Common/Extensions/SimdUtils.cs
  39. 14
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  40. 111
      src/ImageSharp/Common/Helpers/Guard.cs
  41. 108
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  42. 2
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  43. 215
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  44. 190
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  45. 151
      src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
  46. 185
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  47. 6
      src/ImageSharp/Common/Helpers/TestHelpers.cs
  48. 101
      src/ImageSharp/Common/Helpers/TolerantMath.cs
  49. 109
      src/ImageSharp/Common/Tuples/Octet.cs
  50. 12
      src/ImageSharp/Common/Tuples/Vector4Pair.cs
  51. 45
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  52. 21
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  53. 2
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  54. 65
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  55. 72
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  56. 10
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  57. 4
      src/ImageSharp/Formats/IImageFormat.cs
  58. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  59. 181
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  60. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  61. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  62. 88
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  63. 21
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  64. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  65. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  66. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  67. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  68. 11
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  69. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  70. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  71. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  72. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
  73. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  74. 11
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  75. 66
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  76. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
  77. 8
      src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  78. 2
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  79. 23
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  80. 7
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  81. 2
      src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
  82. 2
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  83. 2
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  84. 4
      src/ImageSharp/Formats/Png/PngChunkType.cs
  85. 97
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  86. 207
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  87. 32
      src/ImageSharp/Formats/Png/PngMetaData.cs
  88. 118
      src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
  89. 2
      src/ImageSharp/IImageInfo.cs
  90. 24
      src/ImageSharp/Image.Decode.cs
  91. 5
      src/ImageSharp/Image.FromBytes.cs
  92. 24
      src/ImageSharp/Image.WrapMemory.cs
  93. 4
      src/ImageSharp/ImageExtensions.cs
  94. 32
      src/ImageSharp/ImageFrame{TPixel}.cs
  95. 95
      src/ImageSharp/ImageSharp.csproj
  96. 9
      src/ImageSharp/ImageSharp.csproj.DotSettings
  97. 2
      src/ImageSharp/Image{TPixel}.cs
  98. 13
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  99. 43
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  100. 4
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

5
.github/CONTRIBUTING.md

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

4
appveyor.yml

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

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

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

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

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

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

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

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

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

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

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

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

@ -79,8 +79,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelHelper.IterateRows( ParallelHelper.IterateRows(
@ -93,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width); Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground = Span<TPixelSrc> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity); blender.Blend<TPixelSrc>(configuration, background, background, foreground, this.Opacity);
} }
}); });
} }

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

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

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

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

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

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

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

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

6
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

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

103
src/ImageSharp/Advanced/AotCompilerTools.cs

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

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

@ -37,10 +37,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -70,10 +70,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -103,10 +103,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -136,10 +136,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -171,10 +171,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -203,10 +203,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -236,10 +236,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -268,10 +268,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -301,10 +301,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -334,10 +334,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -367,10 +367,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -400,10 +400,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -433,10 +433,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination); ref CieLab destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -37,10 +38,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -103,10 +104,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -136,10 +137,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -169,10 +170,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -201,10 +202,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -234,10 +235,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -267,10 +268,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -300,10 +301,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -333,10 +334,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -366,10 +367,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -399,10 +400,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
@ -432,10 +433,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLch> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination); ref CieLch destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -35,10 +36,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -68,10 +69,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -103,10 +104,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -136,10 +137,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -169,10 +170,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -202,10 +203,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -235,10 +236,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -268,10 +269,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -301,10 +302,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -334,10 +335,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -367,10 +368,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -400,10 +401,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
@ -432,10 +433,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLchuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -31,10 +32,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -63,10 +64,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -98,10 +99,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -130,10 +131,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -165,10 +166,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -197,10 +198,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -229,10 +230,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -261,10 +262,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -293,10 +294,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -325,10 +326,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -357,10 +358,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -389,10 +390,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
@ -421,10 +422,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLuv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -65,10 +66,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -98,10 +99,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -131,10 +132,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -159,10 +160,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -192,10 +193,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -225,10 +226,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -258,10 +259,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -291,10 +292,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -324,10 +325,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -357,10 +358,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -390,10 +391,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
@ -423,10 +424,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyy> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -17,7 +18,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter(); private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter();
private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); private static readonly HunterLabToCieXyzConverter
HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter();
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter;
@ -40,10 +42,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -75,10 +77,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -110,10 +112,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -145,10 +147,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -177,10 +179,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -211,10 +213,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -245,10 +247,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -279,10 +281,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -314,10 +316,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -350,10 +352,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -382,10 +384,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -415,10 +417,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
@ -449,10 +451,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyz> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -65,10 +66,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -98,10 +99,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -131,10 +132,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -164,10 +165,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -197,10 +198,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -230,10 +231,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -263,10 +264,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -296,10 +297,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -329,10 +330,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -362,10 +363,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -390,10 +391,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
@ -423,10 +424,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Cmyk> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -65,10 +66,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -98,10 +99,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -131,10 +132,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -164,10 +165,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -197,10 +198,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -230,10 +231,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -263,10 +264,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -296,10 +297,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -329,10 +330,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -362,10 +363,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -390,10 +391,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
@ -423,10 +424,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsl> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination); ref Hsl destRef = ref MemoryMarshal.GetReference(destination);

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -65,10 +66,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -98,10 +99,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -131,10 +132,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -164,10 +165,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -197,10 +198,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -230,10 +231,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -263,10 +264,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -296,10 +297,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -329,10 +330,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -362,10 +363,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -390,10 +391,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
@ -423,10 +424,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsv> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination); ref Hsv destRef = ref MemoryMarshal.GetReference(destination);

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

@ -12,26 +12,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content> /// </content>
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -44,26 +33,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -76,26 +54,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -108,26 +75,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -140,26 +96,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -172,29 +117,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieXyz color)
{
// Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint);
// Conversion
return this.cieXyzToHunterLabConverter.Convert(adapted);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -207,26 +138,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -240,14 +160,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="source">The span to the source colors</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <param name="destination">The span to the destination colors</param>
public HunterLab ToHunterLab(in Hsl color) public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination)
{ {
var xyzColor = this.ToCieXyz(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
return this.ToHunterLab(xyzColor); int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
} }
/// <summary> /// <summary>
@ -255,180 +185,250 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp); dp = this.ToHunterLab(sp);
} }
} }
/// <summary> /// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="source">The span to the source colors</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <param name="destination">The span to the destination colors</param>
public HunterLab ToHunterLab(in Hsv color) public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination)
{ {
var xyzColor = this.ToCieXyz(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
return this.ToHunterLab(xyzColor); int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
} }
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp); dp = this.ToHunterLab(sp);
} }
} }
/// <summary> /// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="source">The span to the source colors</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <param name="destination">The span to the destination colors</param>
public HunterLab ToHunterLab(in LinearRgb color) public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination)
{ {
var xyzColor = this.ToCieXyz(color); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
return this.ToHunterLab(xyzColor); int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
} }
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/> /// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp); dp = this.ToHunterLab(sp);
} }
} }
/// <summary> /// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Lms color) public HunterLab ToHunterLab(in CieLab color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/> /// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="color">The color to convert.</param>
/// <param name="destination">The span to the destination colors</param> /// <returns>The <see cref="HunterLab"/></returns>
/// <param name="count">The number of colors to convert.</param> public HunterLab ToHunterLab(in CieLch color)
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); /// <summary>
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); /// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
for (int i = 0; i < count; i++) /// <summary>
{ /// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/>
ref Lms sp = ref Unsafe.Add(ref sourceRef, i); /// </summary>
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); /// <param name="color">The color to convert.</param>
dp = this.ToHunterLab(sp); /// <returns>The <see cref="HunterLab"/></returns>
} public HunterLab ToHunterLab(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/> /// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Rgb color) public HunterLab ToHunterLab(in CieXyy color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/> /// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="color">The color to convert.</param>
/// <param name="destination">The span to the destination colors</param> /// <returns>The <see cref="HunterLab"/></returns>
/// <param name="count">The number of colors to convert.</param> public HunterLab ToHunterLab(in CieXyz color)
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); // Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); // Conversion
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); return this.cieXyzToHunterLabConverter.Convert(adapted);
}
for (int i = 0; i < count; i++) /// <summary>
{ /// Converts a <see cref="Cmyk"/> into a <see cref="HunterLab"/>
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); /// </summary>
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); /// <param name="color">The color to convert.</param>
dp = this.ToHunterLab(sp); /// <returns>The <see cref="HunterLab"/></returns>
} public HunterLab ToHunterLab(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/> /// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns> /// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in YCbCr color) public HunterLab ToHunterLab(in Hsl color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor); return this.ToHunterLab(xyzColor);
} }
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/> /// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="color">The color to convert.</param>
/// <param name="destination">The span to the destination colors</param> /// <returns>The <see cref="HunterLab"/></returns>
/// <param name="count">The number of colors to convert.</param> public HunterLab ToHunterLab(in Hsv color)
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); /// <summary>
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); /// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
for (int i = 0; i < count; i++) /// <summary>
{ /// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); /// </summary>
ref HunterLab dp = ref Unsafe.Add(ref destRef, i); /// <param name="color">The color to convert.</param>
dp = this.ToHunterLab(sp); /// <returns>The <see cref="HunterLab"/></returns>
} public HunterLab ToHunterLab(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
} }
} }
} }

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,26 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -47,26 +37,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -79,26 +58,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -111,26 +79,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -143,26 +100,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -175,29 +121,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieXyz color)
{
// Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion
return this.cieXyzToLinearRgbConverter.Convert(adapted);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -210,26 +142,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Cmyk color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -242,26 +163,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsl color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -274,26 +184,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsv color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -306,26 +205,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -338,26 +226,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -370,26 +247,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Rgb color)
{
// Conversion
return RgbToLinearRgbConverter.Convert(color);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -402,26 +268,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in YCbCr color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="LinearRgb"/> /// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="LinearRgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -433,5 +288,151 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
dp = this.ToLinearRgb(sp); dp = this.ToLinearRgb(sp);
} }
} }
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieXyz color)
{
// Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion
return this.cieXyzToLinearRgbConverter.Convert(adapted);
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Cmyk color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsl color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsv color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Rgb color)
{
// Conversion
return RgbToLinearRgbConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in YCbCr color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
} }
} }

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

@ -12,26 +12,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content> /// </content>
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -44,26 +33,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -76,26 +54,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -108,26 +75,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -140,26 +96,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -172,22 +117,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <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) => this.cieXyzAndLmsConverter.Convert(color);
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -200,26 +138,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -232,26 +159,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -264,26 +180,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -296,26 +201,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -328,26 +222,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -360,26 +243,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -392,26 +264,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Lms"/> /// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Lms"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination); ref Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -423,5 +284,144 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
dp = this.ToLms(sp); dp = this.ToLms(sp);
} }
} }
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <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) => this.cieXyzAndLmsConverter.Convert(color);
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
} }
} }

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,26 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -47,26 +37,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -79,26 +58,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -111,26 +79,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -143,26 +100,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -175,29 +121,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieXyz color)
{
// Conversion
var linear = this.ToLinearRgb(color);
// Compand
return this.ToRgb(linear);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -210,26 +142,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Cmyk color)
{
// Conversion
return CmykAndRgbConverter.Convert(color);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -242,26 +163,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsv color)
{
// Conversion
return HsvAndRgbConverter.Convert(color);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -274,26 +184,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsl color)
{
// Conversion
return HslAndRgbConverter.Convert(color);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -306,26 +205,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -338,26 +226,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in LinearRgb color)
{
// Conversion
return LinearRgbToRgbConverter.Convert(color);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -370,26 +247,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -402,29 +268,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in YCbCr color)
{
// Conversion
Rgb rgb = YCbCrAndRgbConverter.Convert(color);
// Adaptation
return this.Adapt(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Rgb"/> /// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Rgb"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination); ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -436,5 +288,154 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
dp = this.ToRgb(sp); dp = this.ToRgb(sp);
} }
} }
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieXyz color)
{
// Conversion
var linear = this.ToLinearRgb(color);
// Compand
return this.ToRgb(linear);
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Cmyk color)
{
// Conversion
return CmykAndRgbConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsv color)
{
// Conversion
return HsvAndRgbConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsl color)
{
// Conversion
return HslAndRgbConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in LinearRgb color)
{
// Conversion
return LinearRgbToRgbConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in YCbCr color)
{
// Conversion
Rgb rgb = YCbCrAndRgbConverter.Convert(color);
// Adaptation
return this.Adapt(rgb);
}
} }
} }

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

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,27 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -48,27 +37,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -81,27 +58,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -114,27 +79,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -147,27 +100,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyz color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -180,27 +121,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Cmyk color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -213,27 +142,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsl color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -246,27 +163,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsv color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -279,27 +184,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -312,27 +205,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in LinearRgb color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -345,27 +226,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="Lms"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -378,22 +247,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
} }
/// <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) => YCbCrAndRgbConverter.Convert(color);
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="YCbCr"/> /// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="YCbCr"/>
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param> /// <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)
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination, int count)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -405,5 +267,144 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
dp = this.ToYCbCr(sp); dp = this.ToYCbCr(sp);
} }
} }
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyz color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Cmyk color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsl color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsv color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in LinearRgb color)
{
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
/// <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) => YCbCrAndRgbConverter.Convert(color);
} }
} }

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

@ -30,7 +30,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="destination">The span to the destination colors.</param> /// <param name="destination">The span to the destination colors.</param>
/// <param name="sourceWhitePoint">The source white point.</param> /// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="destinationWhitePoint">The destination white point.</param> /// <param name="destinationWhitePoint">The destination white point.</param>
/// <param name="count">The number of colors to convert.</param> void Transform(
void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count); ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
CieXyz sourceWhitePoint,
in CieXyz destinationWhitePoint);
} }
} }

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;
float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float a = c * MathF.Cos(hRadians); float a = c * MathF.Cos(hRadians);
float b = c * MathF.Sin(hRadians); float b = c * MathF.Sin(hRadians);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float l = input.L, a = input.A, b = input.B; float l = input.L, a = input.A, b = input.B;
float c = MathF.Sqrt((a * a) + (b * b)); float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a); float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 360. // Wrap the angle round at 360.
hDegrees %= 360; hDegrees %= 360;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H; float l = input.L, c = input.C, hDegrees = input.H;
float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float u = c * MathF.Cos(hRadians); float u = c * MathF.Cos(hRadians);
float v = c * MathF.Sin(hRadians); float v = c * MathF.Sin(hRadians);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float l = input.L, a = input.U, b = input.V; float l = input.L, a = input.U, b = input.V;
float c = MathF.Sqrt((a * a) + (b * b)); float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a); float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians); float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 360. // Wrap the angle round at 360.
hDegrees %= 360; hDegrees %= 360;

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs

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

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

@ -26,9 +26,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float ka = ComputeKa(input.WhitePoint); float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint); float kb = ComputeKb(input.WhitePoint);
float y = ImageMaths.Pow2(l / 100F) * yn; float pow = ImageMaths.Pow2(l / 100F);
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; float sqrtPow = MathF.Sqrt(pow);
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); float y = pow * yn;
float x = (((a / ka) * sqrtPow) + pow) * xn;
float z = (((b / kb) * sqrtPow) - pow) * (-zn);
return new CieXyz(x, y, z); return new CieXyz(x, y, z);
} }

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

@ -65,9 +65,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count) public void Transform(
ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
CieXyz sourceWhitePoint,
in CieXyz destinationWhitePoint)
{ {
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
if (sourceWhitePoint.Equals(destinationWhitePoint)) if (sourceWhitePoint.Equals(destinationWhitePoint))
{ {

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

@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp
/// <returns>The string.</returns> /// <returns>The string.</returns>
public static string GetString(this Encoding encoding, ReadOnlySpan<byte> buffer) public static string GetString(this Encoding encoding, ReadOnlySpan<byte> buffer)
{ {
if (buffer.Length == 0)
{
return string.Empty;
}
fixed (byte* bytes = buffer) fixed (byte* bytes = buffer)
{ {
return encoding.GetString(bytes, buffer.Length); return encoding.GetString(bytes, buffer.Length);

232
src/ImageSharp/Common/Extensions/SimdUtils.cs

@ -1,232 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Various extension and utility methods for <see cref="Vector4"/> and <see cref="Vector{T}"/> utilizing SIMD capabilities
/// </summary>
internal static class SimdUtils
{
/// <summary>
/// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte.
/// </summary>
public static bool IsAvx2CompatibleArchitecture => Vector<float>.Count == 8 && Vector<int>.Count == 8;
internal static void GuardAvx2(string operation)
{
if (!IsAvx2CompatibleArchitecture)
{
throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!");
}
}
/// <summary>
/// Transform all scalars in 'v' in a way that converting them to <see cref="int"/> would have rounding semantics.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector4 PseudoRound(this Vector4 v)
{
var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1));
return v + (sign * 0.5f);
}
/// <summary>
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics.
/// Source:
/// <see>
/// <cref>https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110</cref>
/// </see>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> x)
{
Vector<int> magic0 = new Vector<int>(int.MinValue); // 0x80000000
Vector<float> sgn0 = Vector.AsVectorSingle(magic0);
Vector<float> and0 = Vector.BitwiseAnd(sgn0, x);
Vector<float> or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f));
Vector<float> add0 = Vector.Add(x, or0);
Vector<float> sub0 = Vector.Subtract(add0, or0);
return sub0;
}
/// <summary>
/// Convert 'source.Length' <see cref="float"/> values normalized into [0..1] from 'source' into 'dest' buffer of <see cref="byte"/> values.
/// The values gonna be scaled up into [0-255] and rounded.
/// Based on:
/// <see>
/// <cref>http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions</cref>
/// </see>
/// </summary>
internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan<float> source, Span<byte> dest)
{
GuardAvx2(nameof(BulkConvertNormalizedFloatToByte));
DebugGuard.IsTrue((source.Length % Vector<float>.Count) == 0, nameof(source), "source.Length should be divisable by Vector<float>.Count!");
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = (x * scale) + magick;
tempRef = x;
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
/// <summary>
/// Same as <see cref="BulkConvertNormalizedFloatToByte"/> but clamps overflown values before conversion.
/// </summary>
internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan<float> source, Span<byte> dest)
{
GuardAvx2(nameof(BulkConvertNormalizedFloatToByte));
DebugGuard.IsTrue((source.Length % Vector<float>.Count) == 0, nameof(source), "source.Length should be divisable by Vector<float>.Count!");
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = Vector.Max(x, Vector<float>.Zero);
x = Vector.Min(x, Vector<float>.One);
x = (x * scale) + magick;
tempRef = x;
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
// TODO: Replace these with T4-d library level tuples!
internal static class Octet
{
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))]
public struct OfUInt32
{
[FieldOffset(0 * sizeof(uint))]
public uint V0;
[FieldOffset(1 * sizeof(uint))]
public uint V1;
[FieldOffset(2 * sizeof(uint))]
public uint V2;
[FieldOffset(3 * sizeof(uint))]
public uint V3;
[FieldOffset(4 * sizeof(uint))]
public uint V4;
[FieldOffset(5 * sizeof(uint))]
public uint V5;
[FieldOffset(6 * sizeof(uint))]
public uint V6;
[FieldOffset(7 * sizeof(uint))]
public uint V7;
public override string ToString()
{
return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]";
}
}
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct OfByte
{
[FieldOffset(0)]
public byte V0;
[FieldOffset(1)]
public byte V1;
[FieldOffset(2)]
public byte V2;
[FieldOffset(3)]
public byte V3;
[FieldOffset(4)]
public byte V4;
[FieldOffset(5)]
public byte V5;
[FieldOffset(6)]
public byte V6;
[FieldOffset(7)]
public byte V7;
public override string ToString()
{
return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]";
}
public void LoadFrom(ref OfUInt32 i)
{
this.V0 = (byte)i.V0;
this.V1 = (byte)i.V1;
this.V2 = (byte)i.V2;
this.V3 = (byte)i.V3;
this.V4 = (byte)i.V4;
this.V5 = (byte)i.V5;
this.V6 = (byte)i.V6;
this.V7 = (byte)i.V7;
}
}
}
}
}

14
src/ImageSharp/Common/Helpers/DebugGuard.cs

@ -163,6 +163,20 @@ namespace SixLabors.ImageSharp
} }
} }
/// <summary>
/// Verifies whether a specific condition is met, throwing an exception if it's false.
/// </summary>
/// <param name="target">The condition</param>
/// <param name="message">The error message</param>
[Conditional("DEBUG")]
public static void IsTrue(bool target, string message)
{
if (!target)
{
throw new InvalidOperationException(message);
}
}
/// <summary> /// <summary>
/// Verifies, that the method parameter with specified target value is false /// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so. /// and throws an exception if it is found to be so.

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

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -19,12 +20,13 @@ namespace SixLabors.ImageSharp
/// <param name="value">The target object, which cannot be null.</param> /// <param name="value">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNull<T>(T value, string parameterName) public static void NotNull<T>(T value, string parameterName)
where T : class where T : class
{ {
if (value is null) if (value is null)
{ {
throw new ArgumentNullException(parameterName); ThrowArgumentNullException(parameterName);
} }
} }
@ -35,16 +37,17 @@ namespace SixLabors.ImageSharp
/// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterName">Name of the parameter.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty or contains only blanks.</exception> /// <exception cref="ArgumentException"><paramref name="value"/> is empty or contains only blanks.</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNullOrWhiteSpace(string value, string parameterName) public static void NotNullOrWhiteSpace(string value, string parameterName)
{ {
if (value is null) if (value is null)
{ {
throw new ArgumentNullException(parameterName); ThrowArgumentNullException(parameterName);
} }
if (string.IsNullOrWhiteSpace(value)) if (string.IsNullOrWhiteSpace(value))
{ {
throw new ArgumentException("Must not be empty or whitespace.", parameterName); ThrowArgumentException("Must not be empty or whitespace.", parameterName);
} }
} }
@ -56,16 +59,17 @@ namespace SixLabors.ImageSharp
/// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterName">Name of the parameter.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception> /// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName) public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName)
{ {
if (value is null) if (value is null)
{ {
throw new ArgumentNullException(parameterName); ThrowArgumentNullException(parameterName);
} }
if (value.Count == 0) if (value.Count == 0)
{ {
throw new ArgumentException("Must not be empty.", parameterName); ThrowArgumentException("Must not be empty.", parameterName);
} }
} }
@ -79,12 +83,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="value"/> is greater than the maximum value. /// <paramref name="value"/> is greater than the maximum value.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName) public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(max) >= 0) if (value.CompareTo(max) >= 0)
{ {
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}."); ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}.");
} }
} }
@ -99,12 +104,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="value"/> is greater than the maximum value. /// <paramref name="value"/> is greater than the maximum value.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName) public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(max) > 0) if (value.CompareTo(max) > 0)
{ {
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}."); ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}.");
} }
} }
@ -119,12 +125,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value. /// <paramref name="value"/> is less than the minimum value.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName) public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) <= 0) if (value.CompareTo(min) <= 0)
{ {
throw new ArgumentOutOfRangeException( ThrowArgumentOutOfRangeException(
parameterName, parameterName,
$"Value {value} must be greater than {min}."); $"Value {value} must be greater than {min}.");
} }
@ -141,12 +148,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value. /// <paramref name="value"/> is less than the minimum value.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName) public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) < 0) if (value.CompareTo(min) < 0)
{ {
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}."); ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}.");
} }
} }
@ -162,12 +170,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value. /// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName) public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{ {
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}.");
} }
} }
@ -181,11 +190,12 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is false /// <paramref name="target"/> is false
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IsTrue(bool target, string parameterName, string message) public static void IsTrue(bool target, string parameterName, string message)
{ {
if (!target) if (!target)
{ {
throw new ArgumentException(message, parameterName); ThrowArgumentException(message, parameterName);
} }
} }
@ -199,11 +209,12 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is true /// <paramref name="target"/> is true
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IsFalse(bool target, string parameterName, string message) public static void IsFalse(bool target, string parameterName, string message)
{ {
if (target) if (target)
{ {
throw new ArgumentException(message, parameterName); ThrowArgumentException(message, parameterName);
} }
} }
@ -217,11 +228,32 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="source"/> has less than <paramref name="minLength"/> items /// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName) public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName)
{ {
if (source.Length < minLength) if (source.Length < minLength)
{ {
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
}
}
/// <summary>
/// Verifies that the 'destination' span is not shorter than 'source'.
/// </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="destination">The destination span</param>
/// <param name="destinationParamName">The name of the argument for 'destination'</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void DestinationShouldNotBeTooShort<TSource, TDest>(
ReadOnlySpan<TSource> source,
Span<TDest> destination,
string destinationParamName)
{
if (destination.Length < source.Length)
{
ThrowArgumentException($"Destination span is too short!", destinationParamName);
} }
} }
@ -235,56 +267,31 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="source"/> has less than <paramref name="minLength"/> items /// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName) public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName)
{ {
if (source.Length < minLength) if (source.Length < minLength)
{ {
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
} }
} }
/// <summary> [MethodImpl(InliningOptions.ColdPath)]
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size. private static void ThrowArgumentException(string message, string parameterName)
/// 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); throw new ArgumentException(message, parameterName);
MustBeSizedAtLeast(dest, minLength, destParamName);
} }
/// <summary> [MethodImpl(InliningOptions.ColdPath)]
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size. private static void ThrowArgumentOutOfRangeException(string parameterName, string message)
/// Throwing an <see cref="ArgumentException"/> if the condition is not met. {
/// </summary> throw new ArgumentOutOfRangeException(parameterName, message);
/// <typeparam name="TSource">The source element type</typeparam> }
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param> [MethodImpl(InliningOptions.ColdPath)]
/// <param name="sourceParamName">The source parameter name</param> private static void ThrowArgumentNullException(string parameterName)
/// <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); throw new ArgumentNullException(parameterName);
MustBeSizedAtLeast(dest, minLength, destParamName);
} }
} }
} }

108
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -14,6 +15,75 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal static class ImageMaths internal static class ImageMaths
{ {
/// <summary>
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <returns>The <see cref="byte"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) =>
(byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5f);
/// <summary>
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <returns>The <see cref="ushort"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) =>
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
/// <summary>
/// Scales a value from a 16 bit <see cref="ushort"/> to it's 8 bit <see cref="byte"/> equivalent.
/// </summary>
/// <param name="component">The 8 bit component value.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static byte DownScaleFrom16BitTo8Bit(ushort component)
{
// To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
//
// (V * 255) / 65535
//
// This reduces to round(V / 257), or floor((V + 128.5)/257)
//
// Represent V as the two byte value vhi.vlo. Make a guess that the
// result is the top byte of V, vhi, then the correction to this value
// is:
//
// error = floor(((V-vhi.vhi) + 128.5) / 257)
// = floor(((vlo-vhi) + 128.5) / 257)
//
// This can be approximated using integer arithmetic (and a signed
// shift):
//
// error = (vlo-vhi+128) >> 8;
//
// The approximate differs from the exact answer only when (vlo-vhi) is
// 128; it then gives a correction of +1 when the exact correction is
// 0. This gives 128 errors. The exact answer (correct for all 16-bit
// input values) is:
//
// error = (vlo-vhi+128)*65535 >> 24;
//
// An alternative arithmetic calculation which also gives no errors is:
//
// (V * 255 + 32895) >> 16
return (byte)(((component * 255) + 32895) >> 16);
}
/// <summary>
/// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent.
/// </summary>
/// <param name="component">The 8 bit component value.</param>
/// <returns>The <see cref="ushort"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);
/// <summary> /// <summary>
/// Determine the Greatest CommonDivisor (GCD) of two numbers. /// Determine the Greatest CommonDivisor (GCD) of two numbers.
/// </summary> /// </summary>
@ -31,7 +101,6 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Determine the Least Common Multiple (LCM) of two numbers. /// Determine the Least Common Multiple (LCM) of two numbers.
/// TODO: This method might be useful for building a more compact <see cref="Processing.Processors.Transforms.KernelMap"/>
/// </summary> /// </summary>
public static int LeastCommonMultiple(int a, int b) public static int LeastCommonMultiple(int a, int b)
{ {
@ -39,6 +108,25 @@ namespace SixLabors.ImageSharp
return (a / GreatestCommonDivisor(a, b)) * b; return (a / GreatestCommonDivisor(a, b)) * b;
} }
/// <summary>
/// Calculates <paramref name="x"/> % 4
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public static int Modulo4(int x) => x & 3;
/// <summary>
/// Calculates <paramref name="x"/> % 8
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public static int Modulo8(int x) => x & 7;
/// <summary>
/// Fast (x mod m) calculator, with the restriction that
/// <paramref name="m"/> should be power of 2.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public static int ModuloP2(int x, int m) => x & (m - 1);
/// <summary> /// <summary>
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation. /// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
/// </summary> /// </summary>
@ -46,7 +134,7 @@ namespace SixLabors.ImageSharp
/// A number that is greater than <see cref="int.MinValue"/>, but less than or equal to <see cref="int.MaxValue"/> /// A number that is greater than <see cref="int.MinValue"/>, but less than or equal to <see cref="int.MaxValue"/>
/// </param> /// </param>
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static int FastAbs(int x) public static int FastAbs(int x)
{ {
int y = x >> 31; int y = x >> 31;
@ -58,7 +146,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <param name="x">A single-precision floating-point number</param> /// <param name="x">A single-precision floating-point number</param>
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns> /// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float Pow2(float x) => x * x; public static float Pow2(float x) => x * x;
/// <summary> /// <summary>
@ -66,7 +154,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <param name="x">A single-precision floating-point number</param> /// <param name="x">A single-precision floating-point number</param>
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns> /// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float Pow3(float x) => x * x * x; public static float Pow3(float x) => x * x * x;
/// <summary> /// <summary>
@ -77,7 +165,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="int"/> /// The <see cref="int"/>
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
/// <summary> /// <summary>
@ -85,7 +173,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <param name="bitDepth">The bit depth.</param> /// <param name="bitDepth">The bit depth.</param>
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth; public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth;
/// <summary> /// <summary>
@ -94,7 +182,7 @@ namespace SixLabors.ImageSharp
/// <param name="x">The x provided to G(x).</param> /// <param name="x">The x provided to G(x).</param>
/// <param name="sigma">The spread of the blur.</param> /// <param name="sigma">The spread of the blur.</param>
/// <returns>The Gaussian G(x)</returns> /// <returns>The Gaussian G(x)</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float Gaussian(float x, float sigma) public static float Gaussian(float x, float sigma)
{ {
const float Numerator = 1.0f; const float Numerator = 1.0f;
@ -117,7 +205,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The sine cardinal of <paramref name="f" />. /// The sine cardinal of <paramref name="f" />.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float SinC(float f) public static float SinC(float f)
{ {
if (MathF.Abs(f) > Constants.Epsilon) if (MathF.Abs(f) > Constants.Epsilon)
@ -140,7 +228,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="float"/>. /// The <see cref="float"/>.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static float GetBcValue(float x, float b, float c) public static float GetBcValue(float x, float b, float c)
{ {
if (x < 0F) if (x < 0F)
@ -176,7 +264,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The bounding <see cref="Rectangle"/>. /// The bounding <see cref="Rectangle"/>.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
/// <summary> /// <summary>

2
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results: // Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
// #define PROFILING // #define PROFILING
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

215
src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs

@ -0,0 +1,215 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
// ReSharper disable MemberHidesStaticFromOuterClass
namespace SixLabors.ImageSharp
{
internal static partial class SimdUtils
{
/// <summary>
/// Implementation with 256bit / AVX2 intrinsics NOT depending on newer API-s (Vector.Widen etc.)
/// </summary>
public static class BasicIntrinsics256
{
public static bool IsAvailable { get; } = IsAvx2CompatibleArchitecture;
/// <summary>
/// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertByteToNormalizedFloatReduce(
ref ReadOnlySpan<byte> source,
ref Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = ImageMaths.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertByteToNormalizedFloat(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// <see cref="BulkConvertNormalizedFloatToByteClampOverflows"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = ImageMaths.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertNormalizedFloatToByteClampOverflows(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// SIMD optimized implementation for <see cref="SimdUtils.BulkConvertByteToNormalizedFloat"/>.
/// Works only with span Length divisible by 8.
/// Implementation adapted from:
/// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions
/// http://stackoverflow.com/a/536278
/// </summary>
internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
VerifyIsAvx2Compatible(nameof(BulkConvertByteToNormalizedFloat));
VerifySpanInput(source, dest, 8);
var bVec = new Vector<float>(256.0f / 255.0f);
var magicFloat = new Vector<float>(32768.0f);
var magicInt = new Vector<uint>(1191182336); // reinterpreted value of 32768.0f
var mask = new Vector<uint>(255);
ref Octet.OfByte sourceBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(source));
ref Octet.OfUInt32 destBaseAsWideOctet = ref Unsafe.As<float, Octet.OfUInt32>(ref MemoryMarshal.GetReference(dest));
ref Vector<float> destBaseAsFloat = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref destBaseAsWideOctet);
int n = dest.Length / 8;
for (int i = 0; i < n; i++)
{
ref Octet.OfByte s = ref Unsafe.Add(ref sourceBase, i);
ref Octet.OfUInt32 d = ref Unsafe.Add(ref destBaseAsWideOctet, i);
d.LoadFrom(ref s);
}
for (int i = 0; i < n; i++)
{
ref Vector<float> df = ref Unsafe.Add(ref destBaseAsFloat, i);
var vi = Vector.AsVectorUInt32(df);
vi &= mask;
vi |= magicInt;
var vf = Vector.AsVectorSingle(vi);
vf = (vf - magicFloat) * bVec;
df = vf;
}
}
/// <summary>
/// Implementation of <see cref="SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows"/> which is faster on older runtimes.
/// </summary>
internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan<float> source, Span<byte> dest)
{
VerifyIsAvx2Compatible(nameof(BulkConvertNormalizedFloatToByteClampOverflows));
VerifySpanInput(source, dest, 8);
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = Vector.Max(x, Vector<float>.Zero);
x = Vector.Min(x, Vector<float>.One);
x = (x * scale) + magick;
tempRef = x;
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
/// <summary>
/// Convert all <see cref="float"/> values normalized into [0..1] from 'source'
/// into 'dest' buffer of <see cref="byte"/>. The values are scaled up into [0-255] and rounded.
/// This implementation is SIMD optimized and works only when span Length is divisible by 8.
/// Based on:
/// <see>
/// <cref>http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions</cref>
/// </see>
/// </summary>
internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan<float> source, Span<byte> dest)
{
VerifyIsAvx2Compatible(nameof(BulkConvertNormalizedFloatToByte));
VerifySpanInput(source, dest, 8);
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = (x * scale) + magick;
tempRef = x;
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
}
}
}

190
src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

@ -0,0 +1,190 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// ReSharper disable MemberHidesStaticFromOuterClass
namespace SixLabors.ImageSharp
{
internal static partial class SimdUtils
{
/// <summary>
/// Implementation methods based on newer <see cref="Vector{T}"/> API-s (Vector.Widen, Vector.Narrow, Vector.ConvertTo*).
/// Only accelerated only on RyuJIT having dotnet/coreclr#10662 merged (.NET Core 2.1+ .NET 4.7.2+)
/// See:
/// https://github.com/dotnet/coreclr/pull/10662
/// API Proposal:
/// https://github.com/dotnet/corefx/issues/15957
/// </summary>
public static class ExtendedIntrinsics
{
public static bool IsAvailable { get; } =
#if SUPPORTS_EXTENDED_INTRINSICS
Vector.IsHardwareAccelerated;
#else
false;
#endif
/// <summary>
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ConvertToSingle(
Vector<short> source,
out Vector<float> dest1,
out Vector<float> dest2)
{
Vector.Widen(source, out Vector<int> i1, out Vector<int> i2);
dest1 = Vector.ConvertToSingle(i1);
dest2 = Vector.ConvertToSingle(i2);
}
/// <summary>
/// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertByteToNormalizedFloatReduce(
ref ReadOnlySpan<byte> source,
ref Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = ImageMaths.ModuloP2(source.Length, Vector<byte>.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// <see cref="BulkConvertNormalizedFloatToByteClampOverflows"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = ImageMaths.ModuloP2(source.Length, Vector<byte>.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertNormalizedFloatToByteClampOverflows(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// Implementation <see cref="SimdUtils.BulkConvertByteToNormalizedFloat"/>, which is faster on new RyuJIT runtime.
/// </summary>
internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
VerifySpanInput(source, dest, Vector<byte>.Count);
int n = dest.Length / Vector<byte>.Count;
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dest));
for (int i = 0; i < n; i++)
{
Vector<byte> b = Unsafe.Add(ref sourceBase, i);
Vector.Widen(b, out Vector<ushort> s0, out Vector<ushort> s1);
Vector.Widen(s0, out Vector<uint> w0, out Vector<uint> w1);
Vector.Widen(s1, out Vector<uint> w2, out Vector<uint> w3);
Vector<float> f0 = ConvertToSingle(w0);
Vector<float> f1 = ConvertToSingle(w1);
Vector<float> f2 = ConvertToSingle(w2);
Vector<float> f3 = ConvertToSingle(w3);
ref Vector<float> d = ref Unsafe.Add(ref destBase, i * 4);
d = f0;
Unsafe.Add(ref d, 1) = f1;
Unsafe.Add(ref d, 2) = f2;
Unsafe.Add(ref d, 3) = f3;
}
}
/// <summary>
/// Implementation of <see cref="SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows"/>, which is faster on new .NET runtime.
/// </summary>
internal static void BulkConvertNormalizedFloatToByteClampOverflows(
ReadOnlySpan<float> source,
Span<byte> dest)
{
VerifySpanInput(source, dest, Vector<byte>.Count);
int n = dest.Length / Vector<byte>.Count;
ref Vector<float> sourceBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Vector<byte> destBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference(dest));
for (int i = 0; i < n; i++)
{
ref Vector<float> s = ref Unsafe.Add(ref sourceBase, i * 4);
Vector<float> f0 = s;
Vector<float> f1 = Unsafe.Add(ref s, 1);
Vector<float> f2 = Unsafe.Add(ref s, 2);
Vector<float> f3 = Unsafe.Add(ref s, 3);
Vector<uint> w0 = ConvertToUInt32(f0);
Vector<uint> w1 = ConvertToUInt32(f1);
Vector<uint> w2 = ConvertToUInt32(f2);
Vector<uint> w3 = ConvertToUInt32(f3);
Vector<ushort> u0 = Vector.Narrow(w0, w1);
Vector<ushort> u1 = Vector.Narrow(w2, w3);
Vector<byte> b = Vector.Narrow(u0, u1);
Unsafe.Add(ref destBase, i) = b;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<uint> ConvertToUInt32(Vector<float> vf)
{
Vector<float> maxBytes = new Vector<float>(255f);
vf *= maxBytes;
vf += new Vector<float>(0.5f);
vf = Vector.Min(Vector.Max(vf, Vector<float>.Zero), maxBytes);
Vector<int> vi = Vector.ConvertToInt32(vf);
return Vector.AsVectorUInt32(vi);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> ConvertToSingle(Vector<uint> u)
{
Vector<int> vi = Vector.AsVectorInt32(u);
Vector<float> v = Vector.ConvertToSingle(vi);
v *= new Vector<float>(1f / 255f);
return v;
}
}
}
}

151
src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs

@ -0,0 +1,151 @@
// 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;
// ReSharper disable MemberHidesStaticFromOuterClass
namespace SixLabors.ImageSharp
{
internal static partial class SimdUtils
{
/// <summary>
/// Fallback implementation based on <see cref="Vector4"/> (128bit).
/// For <see cref="Vector4"/>, efficient software fallback implementations are present,
/// and we hope that even mono's JIT is able to emit SIMD instructions for that type :P
/// </summary>
public static class FallbackIntrinsics128
{
/// <summary>
/// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertByteToNormalizedFloatReduce(
ref ReadOnlySpan<byte> source,
ref Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
int remainder = ImageMaths.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertByteToNormalizedFloat(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// <see cref="BulkConvertNormalizedFloatToByteClampOverflows"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
int remainder = ImageMaths.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
BulkConvertNormalizedFloatToByteClampOverflows(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// Implementation of <see cref="SimdUtils.BulkConvertByteToNormalizedFloat"/> using <see cref="Vector4"/>.
/// </summary>
[MethodImpl(InliningOptions.ColdPath)]
internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
VerifySpanInput(source, dest, 4);
int count = dest.Length / 4;
if (count == 0)
{
return;
}
ref ByteVector4 sBase = ref Unsafe.As<byte, ByteVector4>(ref MemoryMarshal.GetReference(source));
ref Vector4 dBase = ref Unsafe.As<float, Vector4>(ref MemoryMarshal.GetReference(dest));
const float Scale = 1f / 255f;
Vector4 d = default;
for (int i = 0; i < count; i++)
{
ref ByteVector4 s = ref Unsafe.Add(ref sBase, i);
d.X = s.X;
d.Y = s.Y;
d.Z = s.Z;
d.W = s.W;
d *= Scale;
Unsafe.Add(ref dBase, i) = d;
}
}
/// <summary>
/// Implementation of <see cref="SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows"/> using <see cref="Vector4"/>.
/// </summary>
[MethodImpl(InliningOptions.ColdPath)]
internal static void BulkConvertNormalizedFloatToByteClampOverflows(
ReadOnlySpan<float> source,
Span<byte> dest)
{
VerifySpanInput(source, dest, 4);
int count = source.Length / 4;
if (count == 0)
{
return;
}
ref Vector4 sBase = ref Unsafe.As<float, Vector4>(ref MemoryMarshal.GetReference(source));
ref ByteVector4 dBase = ref Unsafe.As<byte, ByteVector4>(ref MemoryMarshal.GetReference(dest));
var half = new Vector4(0.5f);
var maxBytes = new Vector4(255f);
for (int i = 0; i < count; i++)
{
Vector4 s = Unsafe.Add(ref sBase, i);
s *= maxBytes;
s += half;
// I'm not sure if Vector4.Clamp() is properly implemented with intrinsics.
s = Vector4.Max(Vector4.Zero, s);
s = Vector4.Min(maxBytes, s);
ref ByteVector4 d = ref Unsafe.Add(ref dBase, i);
d.X = (byte)s.X;
d.Y = (byte)s.Y;
d.Z = (byte)s.Z;
d.W = (byte)s.W;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct ByteVector4
{
public byte X;
public byte Y;
public byte Z;
public byte W;
}
}
}
}

185
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -0,0 +1,185 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Various extension and utility methods for <see cref="Vector4"/> and <see cref="Vector{T}"/> utilizing SIMD capabilities
/// </summary>
internal static partial class SimdUtils
{
/// <summary>
/// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte.
/// </summary>
public static bool IsAvx2CompatibleArchitecture { get; } =
Vector.IsHardwareAccelerated && Vector<float>.Count == 8 && Vector<int>.Count == 8;
/// <summary>
/// Transform all scalars in 'v' in a way that converting them to <see cref="int"/> would have rounding semantics.
/// </summary>
/// <param name="v">The vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector4 PseudoRound(this Vector4 v)
{
var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1));
return v + (sign * 0.5f);
}
/// <summary>
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics.
/// Source:
/// <see>
/// <cref>https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110</cref>
/// </see>
/// </summary>
/// <param name="v">The vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> v)
{
Vector<int> magic0 = new Vector<int>(int.MinValue); // 0x80000000
Vector<float> sgn0 = Vector.AsVectorSingle(magic0);
Vector<float> and0 = Vector.BitwiseAnd(sgn0, v);
Vector<float> or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f));
Vector<float> add0 = Vector.Add(v, or0);
Vector<float> sub0 = Vector.Subtract(add0, or0);
return sub0;
}
/// <summary>
/// Converts all input <see cref="byte"/>-s to <see cref="float"/>-s normalized into [0..1].
/// <paramref name="source"/> should be the of the same size as <paramref name="dest"/>,
/// but there are no restrictions on the span's length.
/// </summary>
/// <param name="source">The source span of bytes</param>
/// <param name="dest">The destination span of floats</param>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
#if SUPPORTS_EXTENDED_INTRINSICS
ExtendedIntrinsics.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest);
#else
BasicIntrinsics256.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest);
#endif
FallbackIntrinsics128.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest);
// Deal with the remainder:
if (source.Length > 0)
{
ConvertByteToNormalizedFloatRemainder(source, dest);
}
}
/// <summary>
/// Convert all <see cref="float"/> values normalized into [0..1] from 'source' into 'dest' buffer of <see cref="byte"/>.
/// The values are scaled up into [0-255] and rounded, overflows are clamped.
/// <paramref name="source"/> should be the of the same size as <paramref name="dest"/>,
/// but there are no restrictions on the span's length.
/// </summary>
/// <param name="source">The source span of floats</param>
/// <param name="dest">The destination span of bytes</param>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan<float> source, Span<byte> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
#if SUPPORTS_EXTENDED_INTRINSICS
ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest);
#else
BasicIntrinsics256.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest);
#endif
FallbackIntrinsics128.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest);
// Deal with the remainder:
if (source.Length > 0)
{
ConvertNormalizedFloatToByteRemainder(source, dest);
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan<byte> source, Span<float> dest)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref float dBase = ref MemoryMarshal.GetReference(dest);
// There are at most 3 elements at this point, having a for loop is overkill.
// Let's minimize the no. of instructions!
switch (source.Length)
{
case 3:
Unsafe.Add(ref dBase, 2) = Unsafe.Add(ref sBase, 2) / 255f;
goto case 2;
case 2:
Unsafe.Add(ref dBase, 1) = Unsafe.Add(ref sBase, 1) / 255f;
goto case 1;
case 1:
dBase = sBase / 255f;
break;
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan<float> source, Span<byte> dest)
{
ref float sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
switch (source.Length)
{
case 3:
Unsafe.Add(ref dBase, 2) = ConvertToByte(Unsafe.Add(ref sBase, 2));
goto case 2;
case 2:
Unsafe.Add(ref dBase, 1) = ConvertToByte(Unsafe.Add(ref sBase, 1));
goto case 1;
case 1:
dBase = ConvertToByte(sBase);
break;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private static byte ConvertToByte(float f) => (byte)ComparableExtensions.Clamp((f * 255f) + 0.5f, 0, 255f);
[Conditional("DEBUG")]
private static void VerifyIsAvx2Compatible(string operation)
{
if (!IsAvx2CompatibleArchitecture)
{
throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!");
}
}
[Conditional("DEBUG")]
private static void VerifySpanInput(ReadOnlySpan<byte> source, Span<float> dest, int shouldBeDivisibleBy)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisible by {shouldBeDivisibleBy}!");
}
[Conditional("DEBUG")]
private static void VerifySpanInput(ReadOnlySpan<float> source, Span<byte> dest, int shouldBeDivisibleBy)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisible by {shouldBeDivisibleBy}!");
}
}
}

6
src/ImageSharp/Common/Helpers/TestHelpers.cs

@ -13,8 +13,12 @@ namespace SixLabors.ImageSharp.Common.Helpers
/// Only intended to be used in tests! /// Only intended to be used in tests!
/// </summary> /// </summary>
internal const string ImageSharpBuiltAgainst = internal const string ImageSharpBuiltAgainst =
#if NETCOREAPP2_1 #if NET472
"netfx4.7.2";
#elif NETCOREAPP2_1
"netcoreapp2.1"; "netcoreapp2.1";
#elif NETSTANDARD1_3
"netstandard1.3";
#else #else
"netstandard2.0"; "netstandard2.0";
#endif #endif

101
src/ImageSharp/Common/Helpers/TolerantMath.cs

@ -0,0 +1,101 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Implements basic math operations using tolerant comparison
/// whenever an equality check is needed.
/// </summary>
internal readonly struct TolerantMath
{
private readonly double epsilon;
private readonly double negEpsilon;
public TolerantMath(double epsilon)
{
DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon));
this.epsilon = epsilon;
this.negEpsilon = -epsilon;
}
public static TolerantMath Default { get; } = new TolerantMath(1e-8);
/// <summary>
/// <paramref name="a"/> == 0
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsZero(double a) => a > this.negEpsilon && a < this.epsilon;
/// <summary>
/// <paramref name="a"/> &gt; 0
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsPositive(double a) => a > this.epsilon;
/// <summary>
/// <paramref name="a"/> &lt; 0
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsNegative(double a) => a < this.negEpsilon;
/// <summary>
/// <paramref name="a"/> == <paramref name="b"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool AreEqual(double a, double b) => this.IsZero(a - b);
/// <summary>
/// <paramref name="a"/> &gt; <paramref name="b"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsGreater(double a, double b) => a > b + this.epsilon;
/// <summary>
/// <paramref name="a"/> &lt; <paramref name="b"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsLess(double a, double b) => a < b - this.epsilon;
/// <summary>
/// <paramref name="a"/> &gt;= <paramref name="b"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsGreaterOrEqual(double a, double b) => a >= b - this.epsilon;
/// <summary>
/// <paramref name="a"/> &lt;= <paramref name="b"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsLessOrEqual(double a, double b) => b >= a - this.epsilon;
[MethodImpl(InliningOptions.ShortMethod)]
public double Ceiling(double a)
{
double rem = Math.IEEERemainder(a, 1);
if (this.IsZero(rem))
{
return Math.Round(a);
}
return Math.Ceiling(a);
}
[MethodImpl(InliningOptions.ShortMethod)]
public double Floor(double a)
{
double rem = Math.IEEERemainder(a, 1);
if (this.IsZero(rem))
{
return Math.Round(a);
}
return Math.Floor(a);
}
}
}

109
src/ImageSharp/Common/Tuples/Octet.cs

@ -0,0 +1,109 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Tuples
{
/// <summary>
/// Contains 8 element value tuples of various types.
/// </summary>
internal static class Octet
{
/// <summary>
/// Value tuple of <see cref="uint"/>-s
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))]
public struct OfUInt32
{
[FieldOffset(0 * sizeof(uint))]
public uint V0;
[FieldOffset(1 * sizeof(uint))]
public uint V1;
[FieldOffset(2 * sizeof(uint))]
public uint V2;
[FieldOffset(3 * sizeof(uint))]
public uint V3;
[FieldOffset(4 * sizeof(uint))]
public uint V4;
[FieldOffset(5 * sizeof(uint))]
public uint V5;
[FieldOffset(6 * sizeof(uint))]
public uint V6;
[FieldOffset(7 * sizeof(uint))]
public uint V7;
public override string ToString()
{
return $"{nameof(Octet)}.{nameof(OfUInt32)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})";
}
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref OfByte src)
{
this.V0 = src.V0;
this.V1 = src.V1;
this.V2 = src.V2;
this.V3 = src.V3;
this.V4 = src.V4;
this.V5 = src.V5;
this.V6 = src.V6;
this.V7 = src.V7;
}
}
/// <summary>
/// Value tuple of <see cref="byte"/>-s
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct OfByte
{
[FieldOffset(0)]
public byte V0;
[FieldOffset(1)]
public byte V1;
[FieldOffset(2)]
public byte V2;
[FieldOffset(3)]
public byte V3;
[FieldOffset(4)]
public byte V4;
[FieldOffset(5)]
public byte V5;
[FieldOffset(6)]
public byte V6;
[FieldOffset(7)]
public byte V7;
public override string ToString()
{
return $"{nameof(Octet)}.{nameof(OfByte)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})";
}
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref OfUInt32 src)
{
this.V0 = (byte)src.V0;
this.V1 = (byte)src.V1;
this.V2 = (byte)src.V2;
this.V3 = (byte)src.V3;
this.V4 = (byte)src.V4;
this.V5 = (byte)src.V5;
this.V6 = (byte)src.V6;
this.V7 = (byte)src.V7;
}
}
}
}

12
src/ImageSharp/Common/Tuples/Vector4Pair.cs

@ -2,11 +2,12 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Common.Tuples namespace SixLabors.ImageSharp.Tuples
{ {
/// <summary> /// <summary>
/// Its faster to process multiple Vector4-s together, so let's pair them! /// Its faster to process multiple Vector4-s together, so let's pair them!
/// On AVX2 this pair should be convertible to <see cref="Vector{T}"/> of <see cref="float"/>! /// On AVX2 this pair should be convertible to <see cref="Vector{T}"/> of <see cref="float"/>!
/// TODO: Investigate defining this as union with an Octet.OfSingle type.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct Vector4Pair internal struct Vector4Pair
@ -15,8 +16,6 @@ namespace SixLabors.ImageSharp.Common.Tuples
public Vector4 B; public Vector4 B;
private static readonly Vector4 Scale = new Vector4(1 / 255f);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MultiplyInplace(float value) public void MultiplyInplace(float value)
{ {
@ -52,8 +51,9 @@ namespace SixLabors.ImageSharp.Common.Tuples
b = b.FastRound(); b = b.FastRound();
// Downscale by 1/255 // Downscale by 1/255
this.A *= Scale; var scale = new Vector4(1 / 255f);
this.B *= Scale; this.A *= scale;
this.B *= scale;
} }
/// <summary> /// <summary>
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Common.Tuples
public override string ToString() public override string ToString()
{ {
return $"{this.A}, {this.B}"; return $"{nameof(Vector4Pair)}({this.A}, {this.B})";
} }
} }
} }

45
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 data. /// Looks up color values and builds the image from de-compressed RLE8 data.
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/> /// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
@ -219,8 +219,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (Buffer2D<byte> buffer = this.memoryAllocator.Allocate2D<byte>(width, height, AllocationOptions.Clean)) using (Buffer2D<byte> buffer = this.memoryAllocator.Allocate2D<byte>(width, height, AllocationOptions.Clean))
{ {
this.UncompressRle8(width, buffer.GetSpan()); this.UncompressRle8(width, buffer.GetSpan());
@ -233,8 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]));
color.PackFromRgba32(rgba);
pixelRow[x] = color; pixelRow[x] = color;
} }
} }
@ -313,9 +310,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
else else
{ {
for (int i = 0; i < cmd[0]; i++) int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0]
byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop
for (; count < max; count++)
{ {
buffer[count++] = cmd[1]; buffer[count] = cmd1;
} }
} }
} }
@ -352,8 +352,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(arrayWidth + padding, AllocationOptions.Clean)) using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(arrayWidth + padding, AllocationOptions.Clean))
{ {
TPixel color = default; TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -363,7 +361,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int offset = 0; int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(newY); Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
// TODO: Could use PixelOperations here!
for (int x = 0; x < arrayWidth; x++) for (int x = 0; x < arrayWidth; x++)
{ {
int colOffset = x * ppb; int colOffset = x * ppb;
@ -371,9 +368,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4; int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
// Stored in b-> g-> r order. color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIndex]));
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[colorIndex]);
color.PackFromRgba32(rgba);
pixelRow[newX] = color; pixelRow[newX] = color;
} }
@ -397,7 +392,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int padding = CalculatePadding(width, 2); int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding; int stride = (width * 2) + padding;
TPixel color = default; TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{ {
@ -412,11 +406,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
short temp = BitConverter.ToInt16(buffer.Array, offset); short temp = BitConverter.ToInt16(buffer.Array, offset);
rgba.R = GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10); var rgb = new Rgb24(
rgba.G = GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5); GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10),
rgba.B = GetBytesFrom5BitValue(temp & Rgb16BMask); GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5),
GetBytesFrom5BitValue(temp & Rgb16BMask));
color.PackFromRgba32(rgba); color.FromRgb24(rgb);
pixelRow[x] = color; pixelRow[x] = color;
offset += 2; offset += 2;
} }
@ -444,7 +439,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row); this.stream.Read(row);
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY); Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.GetSpan(), pixelSpan, width); PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.configuration,
row.GetSpan(),
pixelSpan,
width);
} }
} }
} }
@ -469,7 +468,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row); this.stream.Read(row);
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY); Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.GetSpan(), pixelSpan, width); PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
row.GetSpan(),
pixelSpan,
width);
} }
} }
} }
@ -537,7 +540,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.metaData = meta; this.metaData = meta;
short bitsPerPixel = this.infoHeader.BitsPerPixel; short bitsPerPixel = this.infoHeader.BitsPerPixel;
var bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
// We can only encode at these bit rates so far. // We can only encode at these bit rates so far.
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24)

21
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -3,6 +3,8 @@
using System; using System;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
@ -23,6 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
private Configuration configuration;
private BmpBitsPerPixel? bitsPerPixel; private BmpBitsPerPixel? bitsPerPixel;
/// <summary> /// <summary>
@ -48,6 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetaData metaData = image.MetaData; ImageMetaData metaData = image.MetaData;
BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance); BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance);
this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel; this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel;
@ -101,9 +106,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var fileHeader = new BmpFileHeader( var fileHeader = new BmpFileHeader(
type: 19778, // BM type: 19778, // BM
offset: 54, fileSize: 54 + infoHeader.ImageSize,
reserved: 0, reserved: 0,
fileSize: 54 + infoHeader.ImageSize); offset: 54);
#if NETCOREAPP2_1 #if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[40]; Span<byte> buffer = stackalloc byte[40];
@ -163,7 +168,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length); PixelOperations<TPixel>.Instance.ToBgra32Bytes(
this.configuration,
pixelSpan,
row.GetSpan(),
pixelSpan.Length);
stream.Write(row.Array, 0, row.Length()); stream.Write(row.Array, 0, row.Length());
} }
} }
@ -183,7 +192,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length); PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration,
pixelSpan,
row.GetSpan(),
pixelSpan.Length);
stream.Write(row.Array, 0, row.Length()); stream.Write(row.Array, 0, row.Length());
} }
} }

2
src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs

@ -8,6 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
internal interface IBmpDecoderOptions internal interface IBmpDecoderOptions
{ {
// added this for consistancy so we can add stuff as required, no options currently availible // added this for consistency so we can add stuff as required, no options currently available
} }
} }

65
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private GifGraphicControlExtension graphicsControlExtension; private GifGraphicControlExtension graphicsControlExtension;
/// <summary> /// <summary>
/// The image desciptor. /// The image descriptor.
/// </summary> /// </summary>
private GifImageDescriptor imageDescriptor; private GifImageDescriptor imageDescriptor;
@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension(); this.ReadApplicationExtension();
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte(); this.SkipBlock(); // Not supported by any known decoder.
this.Skip(plainLength); // Not supported by any known decoder.
break; break;
} }
} }
@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (stream.ReadByte()) switch (stream.ReadByte())
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.SkipBlock(); // Skip graphic control extension block
// Skip graphic control extension block
this.Skip(0);
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments();
@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension(); this.ReadApplicationExtension();
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte(); this.SkipBlock(); // Not supported by any known decoder.
this.Skip(plainLength); // Not supported by any known decoder.
break; break;
} }
} }
@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Could be XMP or something else not supported yet. // Could be XMP or something else not supported yet.
// Back up and skip. // Back up and skip.
this.stream.Position -= appLength + 1; this.stream.Position -= appLength + 1;
this.Skip(appLength); this.SkipBlock(appLength);
return; return;
} }
this.Skip(appLength); // Not supported by any known decoder. this.SkipBlock(appLength); // Not supported by any known decoder.
} }
/// <summary> /// <summary>
/// Skips the designated number of bytes in the stream. /// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// </summary> /// </summary>
/// <param name="length">The number of bytes to skip.</param> private void SkipBlock(int blockSize = 0)
private void Skip(int length)
{ {
this.stream.Skip(length); if (blockSize > 0)
{
this.stream.Skip(blockSize);
}
int flag; int flag;
while ((flag = this.stream.ReadByte()) != 0) while ((flag = this.stream.ReadByte()) > 0)
{ {
this.stream.Skip(flag); this.stream.Skip(flag);
} }
@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor); this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
// Skip any remaining blocks // Skip any remaining blocks
this.Skip(0); this.SkipBlock();
} }
finally finally
{ {
@ -481,22 +480,36 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY)); ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
var rgba = new Rgba32(0, 0, 0, 255); bool transFlag = this.graphicsControlExtension.TransparencyFlag;
// #403 The left + width value can be larger than the image width if (!transFlag)
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
{ {
int index = Unsafe.Add(ref indicesRef, i); // #403 The left + width value can be larger than the image width
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
if (!this.graphicsControlExtension.TransparencyFlag
|| this.graphicsControlExtension.TransparencyIndex != index)
{ {
int index = Unsafe.Add(ref indicesRef, i);
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable[index]; Rgb24 rgb = colorTable[index];
pixel.PackFromRgba32(rgba); pixel.FromRgb24(rgb);
i++;
} }
}
else
{
byte transIndex = this.graphicsControlExtension.TransparencyIndex;
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
{
int index = Unsafe.Add(ref indicesRef, i);
if (transIndex != index)
{
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
Rgb24 rgb = colorTable[index];
pixel.FromRgb24(rgb);
}
i++; i++;
}
} }
} }

72
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -21,10 +23,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
internal sealed class GifEncoderCore internal sealed class GifEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during procesing operations. /// Used for allocating memory during processing operations.
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// Configuration bound to the encoding operation.
/// </summary>
private Configuration configuration;
/// <summary> /// <summary>
/// A reusable buffer used to reduce allocations. /// A reusable buffer used to reduce allocations.
/// </summary> /// </summary>
@ -80,6 +87,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetaData metaData = image.MetaData; ImageMetaData metaData = image.MetaData;
this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance); this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode; this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode;
@ -87,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Quantize the image returning a palette. // Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = QuantizedFrame<TPixel> quantized =
this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame); this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()).QuantizeFrame(image.Frames.RootFrame);
// Get the number of bits. // Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
@ -133,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream) private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser); var palleteQuantizer = new PaletteQuantizer<TPixel>(quantized.Palette, this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++) for (int i = 0; i < image.Frames.Count; i++)
{ {
@ -149,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
else else
{ {
using (QuantizedFrame<TPixel> paletteQuantized using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
= palleteQuantizer.CreateFrameQuantizer(() => quantized.Palette).QuantizeFrame(frame))
{ {
this.WriteImageData(paletteQuantized, stream); this.WriteImageData(paletteQuantized, stream);
} }
@ -170,15 +178,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (quantized is null) if (quantized is null)
{ {
// Allow each frame to be encoded at whatever color depth the frame designates if set. // Allow each frame to be encoded at whatever color depth the frame designates if set.
if (previousFrame != null if (previousFrame != null && previousMeta.ColorTableLength != frameMetaData.ColorTableLength
&& previousMeta.ColorTableLength != frameMetaData.ColorTableLength && frameMetaData.ColorTableLength > 0)
&& frameMetaData.ColorTableLength > 0)
{ {
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(frameMetaData.ColorTableLength).QuantizeFrame(frame); quantized = this.quantizer.CreateFrameQuantizer<TPixel>(
image.GetConfiguration(),
frameMetaData.ColorTableLength).QuantizeFrame(frame);
} }
else else
{ {
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(frame); quantized = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration())
.QuantizeFrame(frame);
} }
} }
@ -210,16 +220,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
// Transparent pixels are much more likely to be found at the end of a palette // Transparent pixels are much more likely to be found at the end of a palette
int index = -1; int index = -1;
Rgba32 trans = default; int length = quantized.Palette.Length;
ref TPixel paletteRef = ref MemoryMarshal.GetReference(quantized.Palette.AsSpan()); using (IMemoryOwner<Rgba32> rgbaBuffer = this.memoryAllocator.Allocate<Rgba32>(length))
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{ {
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i); Span<Rgba32> rgbaSpan = rgbaBuffer.GetSpan();
entry.ToRgba32(ref trans); ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan);
if (trans.Equals(default)) PixelOperations<TPixel>.Instance.ToRgba32(this.configuration, quantized.Palette, rgbaSpan);
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{ {
index = i; if (Unsafe.Add(ref paletteRef, i).Equals(default))
{
index = i;
}
} }
} }
@ -314,7 +328,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
private void WriteComments(ImageMetaData metadata, Stream stream) private void WriteComments(ImageMetaData metadata, Stream stream)
{ {
if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value)) if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property)
|| string.IsNullOrEmpty(property.Value))
{ {
return; return;
} }
@ -406,24 +421,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream) private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
int pixelCount = image.Palette.Length; // The maximum number of colors for the bit depth
// The maximium number of colors for the bit depth
int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3; int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
Rgb24 rgb = default; int pixelCount = image.Palette.Length;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength)) using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
{ {
ref TPixel paletteRef = ref MemoryMarshal.GetReference(image.Palette.AsSpan()); PixelOperations<TPixel>.Instance.ToRgb24Bytes(
ref Rgb24 rgb24Ref = ref Unsafe.As<byte, Rgb24>(ref MemoryMarshal.GetReference(colorTable.GetSpan())); this.configuration,
for (int i = 0; i < pixelCount; i++) image.Palette.AsSpan(),
{ colorTable.GetSpan(),
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i); pixelCount);
entry.ToRgb24(ref rgb);
Unsafe.Add(ref rgb24Ref, i) = rgb;
}
// Write the palette to the stream
stream.Write(colorTable.Array, 0, colorTableLength); stream.Write(colorTable.Array, 0, colorTableLength);
} }
} }

10
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}; };
/// <summary> /// <summary>
/// The maximium number of bits/code. /// The maximum number of bits/code.
/// </summary> /// </summary>
private const int MaxBits = 12; private const int MaxBits = 12;
@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk. /// flush the packet to disk.
/// </summary> /// </summary>
/// <param name="c">The character to add.</param> /// <param name="c">The character to add.</param>
/// <param name="accumulatorsRef">The reference to the storage for packat accumulators</param> /// <param name="accumulatorsRef">The reference to the storage for packet accumulators</param>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream) private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)
@ -309,10 +309,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Non-empty slot // Non-empty slot
if (Unsafe.Add(ref hashTableRef, i) >= 0) if (Unsafe.Add(ref hashTableRef, i) >= 0)
{ {
int disp = hsizeReg - i; int disp = 1;
if (i == 0) if (i != 0)
{ {
disp = 1; disp = hsizeReg - i;
} }
do do

4
src/ImageSharp/Formats/IImageFormat.cs

@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Formats
string Name { get; } string Name { get; }
/// <summary> /// <summary>
/// Gets the default mimetype that the image foramt uses /// Gets the default mimetype that the image format uses
/// </summary> /// </summary>
string DefaultMimeType { get; } string DefaultMimeType { get; }
/// <summary> /// <summary>
/// Gets all the mimetypes that have been used by this image foramt. /// Gets all the mimetypes that have been used by this image format.
/// </summary> /// </summary>
IEnumerable<string> MimeTypes { get; } IEnumerable<string> MimeTypes { get; }

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs

@ -10,7 +10,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens. /// Represents a Jpeg block with <see cref="short"/> coefficients.
/// </summary> /// </summary>
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8 : IEquatable<Block8x8> internal unsafe struct Block8x8 : IEquatable<Block8x8>
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Gets or sets a value in a row+coulumn of the 8x8 block /// Gets or sets a value in a row+column of the 8x8 block
/// </summary> /// </summary>
/// <param name="x">The x position index in the row</param> /// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param> /// <param name="y">The column index</param>
@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Calculate the total sum of absoulute differences of elements in 'a' and 'b'. /// Calculate the total sum of absolute differences of elements in 'a' and 'b'.
/// </summary> /// </summary>
public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) public static long TotalDifference(ref Block8x8 a, ref Block8x8 b)
{ {

181
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -3,64 +3,41 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
/// <summary> /// <summary>
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical. /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale) public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale)
{ {
if (horizontalScale == 1 && verticalScale == 1) if (horizontalScale == 1 && verticalScale == 1)
{ {
this.CopyTo(area); this.Copy1x1Scale(area);
return; return;
} }
else if (horizontalScale == 2 && verticalScale == 2)
if (horizontalScale == 2 && verticalScale == 2)
{ {
this.CopyTo2x2(area); this.Copy2x2Scale(area);
return; return;
} }
ref float destBase = ref area.GetReferenceToOrigin(); // TODO: Optimize: implement all cases with scale-specific, loopless code!
this.CopyArbitraryScale(area, horizontalScale, verticalScale);
// TODO: Optimize: implement all the cases with loopless special code! (T4?)
for (int y = 0; y < 8; y++)
{
int yy = y * verticalScale;
int y8 = y * 8;
for (int x = 0; x < 8; x++)
{
int xx = x * horizontalScale;
float value = this[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * area.Stride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
}
} }
// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Copy1x1Scale(in BufferArea<float> destination)
public void CopyTo(in BufferArea<float> area)
{ {
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin()); ref byte destBase = ref Unsafe.As<float, byte>(ref destination.GetReferenceToOrigin());
int destStride = area.Stride * sizeof(float); int destStride = destination.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
@ -80,76 +57,86 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
} }
private void CopyTo2x2(in BufferArea<float> area) private void Copy2x2Scale(in BufferArea<float> area)
{ {
ref float destBase = ref area.GetReferenceToOrigin(); ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin());
int destStride = area.Stride; int destStride = area.Stride / 2;
this.WidenCopyImpl2x2(ref destBase, 0, destStride); this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
this.WidenCopyImpl2x2(ref destBase, 1, destStride); this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
this.WidenCopyImpl2x2(ref destBase, 2, destStride); this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
this.WidenCopyImpl2x2(ref destBase, 3, destStride); this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
this.WidenCopyImpl2x2(ref destBase, 4, destStride); this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
this.WidenCopyImpl2x2(ref destBase, 5, destStride); this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
this.WidenCopyImpl2x2(ref destBase, 6, destStride); this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
this.WidenCopyImpl2x2(ref destBase, 7, destStride); this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WidenCopyImpl2x2(ref float destBase, int row, int destStride) private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride)
{ {
ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
int offset = 2 * row * destStride;
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; var xyLeft = new Vector4(sLeft.X);
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; xyLeft.Z = sLeft.Y;
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; xyLeft.W = sLeft.Y;
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; var zwLeft = new Vector4(sLeft.Z);
zwLeft.Z = sLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; zwLeft.W = sLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; var xyRight = new Vector4(sRight.X);
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; xyRight.Z = sRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; xyRight.W = sRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; var zwRight = new Vector4(sRight.Z);
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; zwRight.Z = sRight.W;
zwRight.W = sRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; dTopLeft = xyLeft;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; Unsafe.Add(ref dTopLeft, 2) = xyRight;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; Unsafe.Add(ref dTopLeft, 3) = zwRight;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; dBottomLeft = xyLeft;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
Unsafe.Add(ref dBottomLeft, 2) = xyRight;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; Unsafe.Add(ref dBottomLeft, 3) = zwRight;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ColdPath)]
private static void WidenCopyImpl(ref Vector4 s, ref float destBase) private void CopyArbitraryScale(BufferArea<float> area, int horizontalScale, int verticalScale)
{ {
Unsafe.Add(ref destBase, 0) = s.X; ref float destBase = ref area.GetReferenceToOrigin();
Unsafe.Add(ref destBase, 1) = s.X;
Unsafe.Add(ref destBase, 2) = s.Y; for (int y = 0; y < 8; y++)
Unsafe.Add(ref destBase, 3) = s.Y; {
Unsafe.Add(ref destBase, 4) = s.Z; int yy = y * verticalScale;
Unsafe.Add(ref destBase, 5) = s.Z; int y8 = y * 8;
Unsafe.Add(ref destBase, 6) = s.W;
Unsafe.Add(ref destBase, 7) = s.W; for (int x = 0; x < 8; x++)
{
int xx = x * horizontalScale;
float value = this[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * area.Stride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
}
} }
} }
} }

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block. /// Transpose the block into the destination block.
/// </summary> /// </summary>
/// <param name="d">The destination block</param> /// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d) public void TransposeInto(ref Block8x8F d)
{ {
d.V0L.X = V0L.X; d.V0L.X = V0L.X;
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step. /// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2() public void NormalizeColorsAndRoundInplaceAvx2()
{ {
Vector<float> off = new Vector<float>(128f); Vector<float> off = new Vector<float>(128f);
@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Fill the block from 'source' doing short -> float conversion. /// Fill the block from 'source' doing short -> float conversion.
/// </summary> /// </summary>
public void LoadFrom(ref Block8x8 source) public void LoadFromInt16Scalar(ref Block8x8 source)
{ {
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source); ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block. /// Transpose the block into the destination block.
/// </summary> /// </summary>
/// <param name="d">The destination block</param> /// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d) public void TransposeInto(ref Block8x8F d)
{ {
<# <#
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step. /// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2() public void NormalizeColorsAndRoundInplaceAvx2()
{ {
Vector<float> off = new Vector<float>(128f); Vector<float> off = new Vector<float>(128f);
@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Fill the block from 'source' doing short -> float conversion. /// Fill the block from 'source' doing short -> float conversion.
/// </summary> /// </summary>
public void LoadFrom(ref Block8x8 source) public void LoadFromInt16Scalar(ref Block8x8 source)
{ {
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source); ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);

88
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <returns>The float value at the specified index</returns> /// <returns>The float value at the specified index</returns>
public float this[int idx] public float this[int idx]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
get get
{ {
GuardBlockIndex(idx); GuardBlockIndex(idx);
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx); return Unsafe.Add(ref selfRef, idx);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
set set
{ {
GuardBlockIndex(idx); GuardBlockIndex(idx);
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Fill the block with defaults (zeroes) /// Fill the block with defaults (zeroes)
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void Clear() public void Clear()
{ {
// The cheapest way to do this in C#: // The cheapest way to do this in C#:
@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Load raw 32bit floating point data from source /// Load raw 32bit floating point data from source
/// </summary> /// </summary>
/// <param name="source">Source</param> /// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(Span<float> source) public void LoadFrom(Span<float> source)
{ {
ref byte s = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(source)); ref byte s = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(source));
@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
/// <param name="blockPtr">Block pointer</param> /// <param name="blockPtr">Block pointer</param>
/// <param name="source">Source</param> /// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source) public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source)
{ {
blockPtr->LoadFrom(source); blockPtr->LoadFrom(source);
@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest /// Copy raw 32bit floating point data to dest
/// </summary> /// </summary>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(Span<float> dest) public void CopyTo(Span<float> dest)
{ {
ref byte d = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(dest)); ref byte d = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(dest));
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
/// <param name="blockPtr">Pointer to block</param> /// <param name="blockPtr">Pointer to block</param>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest)
{ {
float* fPtr = (float*)blockPtr; float* fPtr = (float*)blockPtr;
@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
/// <param name="blockPtr">Block pointer</param> /// <param name="blockPtr">Block pointer</param>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<float> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, Span<float> dest)
{ {
blockPtr->CopyTo(dest); blockPtr->CopyTo(dest);
@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest /// Copy raw 32bit floating point data to dest
/// </summary> /// </summary>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public unsafe void CopyTo(float[] dest) public unsafe void CopyTo(float[] dest)
{ {
fixed (void* ptr = &this.V0L) fixed (void* ptr = &this.V0L)
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Multiply all elements of the block. /// Multiply all elements of the block.
/// </summary> /// </summary>
/// <param name="value">The value to multiply by</param> /// <param name="value">The value to multiply by</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(float value) public void MultiplyInplace(float value)
{ {
this.V0L *= value; this.V0L *= value;
@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Multiply all elements of the block by the corresponding elements of 'other' /// Multiply all elements of the block by the corresponding elements of 'other'
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(ref Block8x8F other) public void MultiplyInplace(ref Block8x8F other)
{ {
this.V0L *= other.V0L; this.V0L *= other.V0L;
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Adds a vector to all elements of the block. /// Adds a vector to all elements of the block.
/// </summary> /// </summary>
/// <param name="diff">The added vector</param> /// <param name="diff">The added vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
public void AddToAllInplace(Vector4 diff) public void AddToAllInplace(Vector4 diff)
{ {
this.V0L += diff; this.V0L += diff;
@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
/// <summary> /// <summary>
/// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block. /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
/// </summary> /// </summary>
/// <param name="destination">The destination block.</param> /// <param name="destination">The destination block.</param>
/// <param name="source">The source block.</param> /// <param name="source">The source block.</param>
@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{ {
a.V0L = DivideRound(a.V0L, b.V0L); a.V0L = DivideRound(a.V0L, b.V0L);
@ -493,25 +493,69 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
} }
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref Block8x8 source)
{
#if SUPPORTS_EXTENDED_INTRINSICS
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
this.LoadFromInt16ExtendedAvx2(ref source);
return;
}
#endif
this.LoadFromInt16Scalar(ref source);
}
/// <summary>
/// Loads values from <paramref name="source"/> using extended AVX2 intrinsics.
/// </summary>
/// <param name="source">The source <see cref="Block8x8"/></param>
public void LoadFromInt16ExtendedAvx2(ref Block8x8 source)
{
DebugGuard.IsTrue(
SimdUtils.IsAvx2CompatibleArchitecture,
"LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!");
ref Vector<short> sRef = ref Unsafe.As<Block8x8, Vector<short>>(ref source);
ref Vector<float> dRef = ref Unsafe.As<Block8x8F, Vector<float>>(ref this);
// Vector<ushort>.Count == 16 on AVX2
// We can process 2 block rows in a single step
SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector<float> top, out Vector<float> bottom);
dRef = top;
Unsafe.Add(ref dRef, 1) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom);
Unsafe.Add(ref dRef, 2) = top;
Unsafe.Add(ref dRef, 3) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom);
Unsafe.Add(ref dRef, 4) = top;
Unsafe.Add(ref dRef, 5) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom);
Unsafe.Add(ref dRef, 6) = top;
Unsafe.Add(ref dRef, 7) = bottom;
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('['); sb.Append('[');
for (int i = 0; i < Size; i++) for (int i = 0; i < Size - 1; i++)
{ {
sb.Append(this[i]); sb.Append(this[i]);
if (i < Size - 1) sb.Append(',');
{
sb.Append(',');
}
} }
sb.Append(this[Size - 1]);
sb.Append(']'); sb.Append(']');
return sb.ToString(); return sb.ToString();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max) private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max)
{ {
row += off; row += off;
@ -520,13 +564,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound(); return row.FastRound();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{ {
// sign(dividend) = max(min(dividend, 1), -1) // sign(dividend) = max(min(dividend, 1), -1)
var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One);
// AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset); return (dividend / divisor) + (sign * Offset);
} }

21
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -3,6 +3,8 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
@ -17,24 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
float y = yVals[i]; var v = new Vector4(Unsafe.Add(ref sBase, i));
v.W = 1f;
v.X = y;
v.Y = y;
v.Z = y;
v *= scale; v *= scale;
Unsafe.Add(ref dBase, i) = v;
result[i] = v;
} }
} }
} }

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -6,7 +6,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{ {
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
ref Vector4Pair yBase = ref Vector4Pair yBase =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Collect(ref r, ref g, ref b); destination.Pack(ref r, ref g, ref b);
} }
} }
} }

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -6,7 +6,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField // ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{ {
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Collect(ref rr, ref gg, ref bb); destination.Pack(ref rr, ref gg, ref bb);
} }
} }
} }

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -6,8 +6,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
/// <summary> /// <summary>
/// The avalilable converters /// The available converters
/// </summary> /// </summary>
private static readonly JpegColorConverter[] Converters = private static readonly JpegColorConverter[] Converters =
{ {
@ -157,9 +157,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public Vector4 V0, V1, V2, V3, V4, V5, V6, V7; public Vector4 V0, V1, V2, V3, V4, V5, V6, V7;
/// <summary> /// <summary>
/// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order. /// Pack (r0,r1...r7) (g0,g1...g7) (b0,b1...b7) vector values as (r0,g0,b0,1), (r1,g1,b1,1) ...
/// </summary> /// </summary>
public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b) public void Pack(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b)
{ {
this.V0.X = r.A.X; this.V0.X = r.A.X;
this.V0.Y = g.A.X; this.V0.Y = g.A.X;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
// Figure F.15: generate decoding tables for bit-sequential decoding. // Figure F.15: generate decoding tables for bit-sequential decoding.
// Compute largest code + 1 for this size. preshifted as we needit later. // Compute largest code + 1 for this size. preshifted as we need it later.
Unsafe.Add(ref maxcodeRef, k) = code << (16 - k); Unsafe.Add(ref maxcodeRef, k) = code << (16 - k);
code <<= 1; code <<= 1;
} }

11
src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary> /// <summary>
/// Gets the <see cref="Buffer2D{Block8x8}"/> storing the "raw" frequency-domain decoded + unzigged blocks. /// Gets the <see cref="Buffer2D{Block8x8}"/> storing the "raw" frequency-domain decoded + unzigged blocks.
/// We need to apply IDCT and dequantiazition to transform them into color-space blocks. /// We need to apply IDCT and dequantization to transform them into color-space blocks.
/// </summary> /// </summary>
Buffer2D<Block8x8> SpectralBlocks { get; } Buffer2D<Block8x8> SpectralBlocks { get; }
/// <summary>
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="SpectralBlocks"/>
/// </summary>
/// <param name="column">The column</param>
/// <param name="row">The row</param>
/// <returns>The <see cref="Block8x8"/></returns>
ref Block8x8 GetBlockReference(int column, int row);
} }
} }

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Size ImageSizeInPixels { get; } Size ImageSizeInPixels { get; }
/// <summary> /// <summary>
/// Gets the number of coponents. /// Gets the number of components.
/// </summary> /// </summary>
int ComponentCount { get; } int ComponentCount { get; }

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="minorVersion">The minor version</param> /// <param name="minorVersion">The minor version</param>
/// <param name="densityUnits">The units for the density values</param> /// <param name="densityUnits">The units for the density values</param>
/// <param name="xDensity">The horizontal pixel density</param> /// <param name="xDensity">The horizontal pixel density</param>
/// <param name="yDensity">The veritcal pixel density</param> /// <param name="yDensity">The vertical pixel density</param>
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{ {
Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity)); Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity));

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// - Dequantize /// - Dequantize
/// - Applying IDCT /// - Applying IDCT
/// - Level shift by +128, clip to [0, 255] /// - Level shift by +128, clip to [0, 255]
/// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/> /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary> /// </summary>
/// <param name="sourceBlock">The source block.</param> /// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param> /// <param name="destArea">The destination buffer area.</param>

18
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs

@ -128,21 +128,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
} }
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, AllocationOptions.Clean); int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
} int width = this.WidthInBlocks + 1;
int height = totalNumberOfBlocks / width;
[MethodImpl(MethodImplOptions.AggressiveInlining)] this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(width, height, AllocationOptions.Clean);
public ref Block8x8 GetBlockReference(int column, int row)
{
int offset = ((this.WidthInBlocks + 1) * row) + column;
return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref short GetBlockDataReference(int column, int row)
{
ref Block8x8 blockRef = ref this.GetBlockReference(column, row);
return ref Unsafe.As<Block8x8, short>(ref blockRef);
} }
} }
} }

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private int currentComponentRowInBlocks; private int currentComponentRowInBlocks;
/// <summary> /// <summary>
/// The size of the area in <see cref="ColorBuffer"/> corrsponding to one 8x8 Jpeg block /// The size of the area in <see cref="ColorBuffer"/> corresponding to one 8x8 Jpeg block
/// </summary> /// </summary>
private readonly Size blockAreaSize; private readonly Size blockAreaSize;
@ -88,12 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height; int yBuffer = y * this.blockAreaSize.Height;
for (int x = 0; x < this.SizeInBlocks.Width; x++) Span<Block8x8> blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
{
int xBlock = x; ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
int xBuffer = x * this.blockAreaSize.Width;
ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock); for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++)
{
ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock);
int xBuffer = xBlock * this.blockAreaSize.Width;
BufferArea<float> destArea = this.ColorBuffer.GetArea( BufferArea<float> destArea = this.ColorBuffer.GetArea(
xBuffer, xBuffer,

11
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary> /// </summary>
internal class JpegImagePostProcessor : IDisposable internal class JpegImagePostProcessor : IDisposable
{ {
private readonly Configuration configuration;
/// <summary> /// <summary>
/// The number of block rows to be processed in one Step. /// The number of block rows to be processed in one Step.
/// </summary> /// </summary>
@ -49,15 +51,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class. /// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </summary> /// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param> /// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param> /// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg) public JpegImagePostProcessor(Configuration configuration, IRawJpegData rawJpeg)
{ {
this.configuration = configuration;
this.RawJpeg = rawJpeg; this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First(); IJpegComponent c0 = rawJpeg.Components.First();
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray(); this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width); this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
@ -159,7 +163,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Span<TPixel> destRow = destination.GetPixelRowSpan(yy); Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.GetSpan(), destRow, destination.Width); // TODO: Investigate if slicing is actually necessary
PixelOperations<TPixel>.Instance.FromVector4(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow);
} }
} }
} }

66
src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs

@ -1,8 +1,10 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
@ -142,7 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y)); private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y));
private void ParseBaselineData() private void ParseBaselineData()
@ -179,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int h = component.HorizontalSamplingFactor; int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor; int v = component.VerticalSamplingFactor;
int mcuRow = mcu / mcusPerLine;
// Scan out an mcu's worth of this component; that's just determined // Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component // by the basic H and V specified for the component
for (int y = 0; y < v; y++) for (int y = 0; y < v; y++)
{ {
int blockRow = (mcuRow * v) + y;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++) for (int x = 0; x < h; x++)
{ {
if (this.eof) if (this.eof)
@ -190,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return; return;
} }
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine; int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x; int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline( this.DecodeBlockBaseline(
component, component,
blockRow, ref blockSpan[blockCol],
blockCol,
ref dcHuffmanTable, ref dcHuffmanTable,
ref acHuffmanTable, ref acHuffmanTable,
ref fastACRef); ref fastACRef);
@ -236,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0; int mcu = 0;
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
// TODO: Isn't blockRow == j actually?
int blockRow = mcu / w;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int i = 0; i < w; i++) for (int i = 0; i < w; i++)
{ {
if (this.eof) if (this.eof)
@ -243,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return; return;
} }
int blockRow = mcu / w; // TODO: Isn't blockCol == i actually?
int blockCol = mcu % w; int blockCol = mcu % w;
this.DecodeBlockBaseline( this.DecodeBlockBaseline(
component, component,
blockRow, ref blockSpan[blockCol],
blockCol,
ref dcHuffmanTable, ref dcHuffmanTable,
ref acHuffmanTable, ref acHuffmanTable,
ref fastACRef); ref fastACRef);
@ -299,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component // by the basic H and V specified for the component
for (int y = 0; y < v; y++) for (int y = 0; y < v; y++)
{ {
int mcuRow = mcu / mcusPerLine;
int blockRow = (mcuRow * v) + y;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++) for (int x = 0; x < h; x++)
{ {
if (this.eof) if (this.eof)
@ -306,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return; return;
} }
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine; int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x; int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC( this.DecodeBlockProgressiveDC(
component, component,
blockRow, ref blockSpan[blockCol],
blockCol,
ref dcHuffmanTable); ref dcHuffmanTable);
} }
} }
@ -351,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0; int mcu = 0;
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
// TODO: isn't blockRow == j actually?
int blockRow = mcu / w;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int i = 0; i < w; i++) for (int i = 0; i < w; i++)
{ {
if (this.eof) if (this.eof)
@ -358,23 +369,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return; return;
} }
int blockRow = mcu / w; // TODO: isn't blockCol == i actually?
int blockCol = mcu % w; int blockCol = mcu % w;
ref Block8x8 block = ref blockSpan[blockCol];
if (this.spectralStart == 0) if (this.spectralStart == 0)
{ {
this.DecodeBlockProgressiveDC( this.DecodeBlockProgressiveDC(
component, component,
blockRow, ref block,
blockCol,
ref dcHuffmanTable); ref dcHuffmanTable);
} }
else else
{ {
this.DecodeBlockProgressiveAC( this.DecodeBlockProgressiveAC(
component, ref block,
blockRow,
blockCol,
ref acHuffmanTable, ref acHuffmanTable,
ref fastACRef); ref fastACRef);
} }
@ -391,8 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline( private void DecodeBlockBaseline(
JpegComponent component, JpegComponent component,
int row, ref Block8x8 block,
int col,
ref HuffmanTable dcTable, ref HuffmanTable dcTable,
ref HuffmanTable acTable, ref HuffmanTable acTable,
ref short fastACRef) ref short fastACRef)
@ -405,7 +414,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowBadHuffmanCode(); JpegThrowHelper.ThrowBadHuffmanCode();
} }
ref short blockDataRef = ref component.GetBlockDataReference(col, row); ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
int diff = t != 0 ? this.ExtendReceive(t) : 0; int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff; int dc = component.DcPredictor + diff;
@ -470,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC( private void DecodeBlockProgressiveDC(
JpegComponent component, JpegComponent component,
int row, ref Block8x8 block,
int col,
ref HuffmanTable dcTable) ref HuffmanTable dcTable)
{ {
if (this.spectralEnd != 0) if (this.spectralEnd != 0)
@ -481,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.CheckBits(); this.CheckBits();
ref short blockDataRef = ref component.GetBlockDataReference(col, row); ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
if (this.successiveHigh == 0) if (this.successiveHigh == 0)
{ {
@ -505,9 +513,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
private void DecodeBlockProgressiveAC( private void DecodeBlockProgressiveAC(
JpegComponent component, ref Block8x8 block,
int row,
int col,
ref HuffmanTable acTable, ref HuffmanTable acTable,
ref short fastACRef) ref short fastACRef)
{ {
@ -516,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC."); JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
} }
ref short blockDataRef = ref component.GetBlockDataReference(col, row); ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
if (this.successiveHigh == 0) if (this.successiveHigh == 0)
{ {
@ -749,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
private void FillBuffer() private void FillBuffer()
{ {
// Attempt to load at least the minimum nbumber of required bits into the buffer. // Attempt to load at least the minimum number of required bits into the buffer.
// We fail to do so only if we hit a marker or reach the end of the input stream. // We fail to do so only if we hit a marker or reach the end of the input stream.
do do
{ {
@ -906,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
// If it's NOT a restart, then just bail, so we get corrupt data rather than no data. // If it's NOT a restart, then just bail, so we get corrupt data rather than no data.
// Reset the stream to before any bad markers to ensure we can read sucessive segments. // Reset the stream to before any bad markers to ensure we can read successive segments.
if (this.badMarker) if (this.badMarker)
{ {
this.stream.Position = this.markerPosition; this.stream.Position = this.markerPosition;

2
src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs

@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <summary> /// <summary>
/// Initializes the YCbCr tables /// Initializes the YCbCr tables
/// </summary> /// </summary>
/// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns> /// <returns>The initialized <see cref="RgbToYCbCrTables"/></returns>
public static RgbToYCbCrTables Create() public static RgbToYCbCrTables Create()
{ {
RgbToYCbCrTables tables = default; RgbToYCbCrTables tables = default;

8
src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type to work on</typeparam> /// <typeparam name="TPixel">The pixel type to work on</typeparam>
internal struct YCbCrForwardConverter<TPixel> internal ref struct YCbCrForwardConverter<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
@ -53,12 +53,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <summary> /// <summary>
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>) /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
/// </summary> /// </summary>
public void Convert(IPixelSource<TPixel> pixels, int x, int y) public void Convert(ImageFrame<TPixel> frame, int x, int y)
{ {
this.pixelBlock.LoadAndStretchEdges(pixels, x, y); this.pixelBlock.LoadAndStretchEdges(frame, x, y);
Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe(); Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe();
PixelOperations<TPixel>.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); PixelOperations<TPixel>.Instance.ToRgb24(frame.Configuration, this.pixelBlock.AsSpanUnsafe(), rgbSpan);
ref float yBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Y); ref float yBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Y);
ref float cbBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cb); ref float cbBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cb);

2
src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// FOR TESTING ONLY! /// FOR TESTING ONLY!
/// Gets or sets a value in a row+coulumn of the 8x8 block /// Gets or sets a value in a row+column of the 8x8 block
/// </summary> /// </summary>
/// <param name="x">The x position index in the row</param> /// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param> /// <param name="y">The column index</param>

23
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -51,12 +51,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private readonly byte[] markerBuffer = new byte[2]; private readonly byte[] markerBuffer = new byte[2];
/// <summary> /// <summary>
/// The DC HUffman tables /// The DC Huffman tables
/// </summary> /// </summary>
private HuffmanTables dcHuffmanTables; private HuffmanTables dcHuffmanTables;
/// <summary> /// <summary>
/// The AC HUffman tables /// The AC Huffman tables
/// </summary> /// </summary>
private HuffmanTables acHuffmanTables; private HuffmanTables acHuffmanTables;
@ -856,10 +856,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void ProcessStartOfScanMarker() private void ProcessStartOfScanMarker()
{ {
int selectorsCount = this.InputStream.ReadByte(); int selectorsCount = this.InputStream.ReadByte();
int componentIndex = -1;
for (int i = 0; i < selectorsCount; i++) for (int i = 0; i < selectorsCount; i++)
{ {
componentIndex = -1; int componentIndex = -1;
int selector = this.InputStream.ReadByte(); int selector = this.InputStream.ReadByte();
for (int j = 0; j < this.Frame.ComponentIds.Length; j++) for (int j = 0; j < this.Frame.ComponentIds.Length; j++)
@ -913,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="index">The table index</param> /// <param name="index">The table index</param>
/// <param name="codeLengths">The codelengths</param> /// <param name="codeLengths">The codelengths</param>
/// <param name="values">The values</param> /// <param name="values">The values</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values) private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); => tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
@ -921,7 +920,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Reads a <see cref="ushort"/> from the stream advancing it by two bytes /// Reads a <see cref="ushort"/> from the stream advancing it by two bytes
/// </summary> /// </summary>
/// <returns>The <see cref="ushort"/></returns> /// <returns>The <see cref="ushort"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16() private ushort ReadUint16()
{ {
this.InputStream.Read(this.markerBuffer, 0, 2); this.InputStream.Read(this.markerBuffer, 0, 2);
@ -936,12 +935,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image<TPixel> PostProcessIntoImage<TPixel>() private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this)) var image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.ImageWidth,
this.ImageHeight,
this.MetaData);
using (var postProcessor = new JpegImagePostProcessor(this.configuration, this))
{ {
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame); postProcessor.PostProcess(image.Frames.RootFrame);
return image;
} }
return image;
} }
} }
} }

7
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -822,11 +822,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
for (int i = 0; i < componentCount; i++) for (int i = 0; i < componentCount; i++)
{ {
this.buffer[(3 * i) + 6] = (byte)(i + 1); int i3 = 3 * i;
this.buffer[i3 + 6] = (byte)(i + 1);
// We use 4:2:0 chroma subsampling by default. // We use 4:2:0 chroma subsampling by default.
this.buffer[(3 * i) + 7] = subsamples[i]; this.buffer[i3 + 7] = subsamples[i];
this.buffer[(3 * i) + 8] = chroma[i]; this.buffer[i3 + 8] = chroma[i];
} }
} }

2
src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Chunks
/// <summary> /// <summary>
/// Constructs the PngPhysicalChunkData from the provided metadata. /// Constructs the PngPhysicalChunkData from the provided metadata.
/// If the resolution units are not in meters, they are automatically convereted. /// If the resolution units are not in meters, they are automatically converted.
/// </summary> /// </summary>
/// <param name="meta">The metadata.</param> /// <param name="meta">The metadata.</param>
/// <returns>The constructed PngPhysicalChunkData instance.</returns> /// <returns>The constructed PngPhysicalChunkData instance.</returns>

2
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
int offset = bytesPerPixel + 1; // Add one bcause x starts at one. int offset = bytesPerPixel + 1; // Add one because x starts at one.
int x = 1; int x = 1;
for (; x < offset; x++) for (; x < offset; x++)
{ {

2
src/ImageSharp/Formats/Png/IPngDecoderOptions.cs

@ -6,7 +6,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
/// The optioas for decoding png images /// The options for decoding png images
/// </summary> /// </summary>
internal interface IPngDecoderOptions internal interface IPngDecoderOptions
{ {

4
src/ImageSharp/Formats/Png/PngChunkType.cs

@ -56,10 +56,10 @@ namespace SixLabors.ImageSharp.Formats.Png
Text = 0x74455874U, Text = 0x74455874U,
/// <summary> /// <summary>
/// This chunk specifies that the image uses simple transparency: /// The tRNS chunk specifies that the image uses simple transparency:
/// either alpha values associated with palette entries (for indexed-color images) /// either alpha values associated with palette entries (for indexed-color images)
/// or a single transparent color (for grayscale and true color images). /// or a single transparent color (for grayscale and true color images).
/// </summary> /// </summary>
PaletteAlpha = 0x74524E53U Transparency = 0x74524E53U
} }
} }

97
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -124,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
private PngColorType pngColorType; private PngColorType pngColorType;
/// <summary>
/// Represents any color in an 8 bit Rgb24 encoded png that should be transparent
/// </summary>
private Rgb24 rgb24Trans;
/// <summary>
/// Represents any color in a 16 bit Rgb24 encoded png that should be transparent
/// </summary>
private Rgb48 rgb48Trans;
/// <summary>
/// Represents any color in an 8 bit grayscale encoded png that should be transparent
/// </summary>
private byte luminanceTrans;
/// <summary>
/// Represents any color in a 16 bit grayscale encoded png that should be transparent
/// </summary>
private ushort luminance16Trans;
/// <summary>
/// Whether the image has transparency chunk and markers were decoded
/// </summary>
private bool hasTrans;
/// <summary> /// <summary>
/// The next chunk of data to return /// The next chunk of data to return
/// </summary> /// </summary>
@ -213,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Png
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{ {
deframeStream.AllocateNewBytes(chunk.Length); deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData);
} }
break; break;
@ -222,11 +197,11 @@ namespace SixLabors.ImageSharp.Formats.Png
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal; this.palette = pal;
break; break;
case PngChunkType.PaletteAlpha: case PngChunkType.Transparency:
byte[] alpha = new byte[chunk.Length]; byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha); this.AssignTransparentMarkers(alpha, pngMetaData);
break; break;
case PngChunkType.Text: case PngChunkType.Text:
this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length));
@ -496,16 +471,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="image"> The pixel data.</param> /// <param name="image"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{ {
this.DecodeInterlacedPixelData(dataStream, image); this.DecodeInterlacedPixelData(dataStream, image, pngMetaData);
} }
else else
{ {
this.DecodePixelData(dataStream, image); this.DecodePixelData(dataStream, image, pngMetaData);
} }
} }
@ -515,7 +491,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The image to decode to.</param> /// <param name="image">The image to decode to.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void DecodePixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (this.currentRow < this.header.Height) while (this.currentRow < this.header.Height)
@ -555,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("Unknown filter type."); throw new ImageFormatException("Unknown filter type.");
} }
this.ProcessDefilteredScanline(scanlineSpan, image); this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData);
this.SwapBuffers(); this.SwapBuffers();
this.currentRow++; this.currentRow++;
@ -569,7 +546,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The current image.</param> /// <param name="image">The current image.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (true) while (true)
@ -626,7 +604,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetaData, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]);
this.SwapBuffers(); this.SwapBuffers();
@ -654,7 +632,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</param> /// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels) /// <param name="pngMetaData">The png meta data</param>
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
@ -674,9 +653,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.hasTrans, pngMetaData.HasTrans,
this.luminance16Trans, pngMetaData.TransparentGray16.GetValueOrDefault(),
this.luminanceTrans); pngMetaData.TransparentGray8.GetValueOrDefault());
break; break;
@ -702,19 +681,21 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb: case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline( PngScanlineProcessor.ProcessRgbScanline(
this.configuration,
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.bytesPerPixel, this.bytesPerPixel,
this.bytesPerSample, this.bytesPerSample,
this.hasTrans, pngMetaData.HasTrans,
this.rgb48Trans, pngMetaData.TransparentRgb48.GetValueOrDefault(),
this.rgb24Trans); pngMetaData.TransparentRgb24.GetValueOrDefault());
break; break;
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline( PngScanlineProcessor.ProcessRgbaScanline(
this.configuration,
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
@ -733,9 +714,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="rowSpan">The current image row.</param> /// <param name="rowSpan">The current image row.</param>
/// <param name="pngMetaData">The png meta data</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param> /// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param> /// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1) private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetaData pngMetaData, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// Trim the first marker byte from the buffer // Trim the first marker byte from the buffer
@ -755,9 +737,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan, rowSpan,
pixelOffset, pixelOffset,
increment, increment,
this.hasTrans, pngMetaData.HasTrans,
this.luminance16Trans, pngMetaData.TransparentGray16.GetValueOrDefault(),
this.luminanceTrans); pngMetaData.TransparentGray8.GetValueOrDefault());
break; break;
@ -794,9 +776,9 @@ namespace SixLabors.ImageSharp.Formats.Png
increment, increment,
this.bytesPerPixel, this.bytesPerPixel,
this.bytesPerSample, this.bytesPerSample,
this.hasTrans, pngMetaData.HasTrans,
this.rgb48Trans, pngMetaData.TransparentRgb48.GetValueOrDefault(),
this.rgb24Trans); pngMetaData.TransparentRgb24.GetValueOrDefault());
break; break;
@ -820,7 +802,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Decodes and assigns marker colors that identify transparent pixels in non indexed images /// Decodes and assigns marker colors that identify transparent pixels in non indexed images
/// </summary> /// </summary>
/// <param name="alpha">The alpha tRNS array</param> /// <param name="alpha">The alpha tRNS array</param>
private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha) /// <param name="pngMetaData">The png meta data</param>
private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha, PngMetaData pngMetaData)
{ {
if (this.pngColorType == PngColorType.Rgb) if (this.pngColorType == PngColorType.Rgb)
{ {
@ -832,16 +815,16 @@ namespace SixLabors.ImageSharp.Formats.Png
ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2));
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
this.rgb48Trans = new Rgb48(rc, gc, bc); pngMetaData.TransparentRgb48 = new Rgb48(rc, gc, bc);
this.hasTrans = true; pngMetaData.HasTrans = true;
return; return;
} }
byte r = ReadByteLittleEndian(alpha, 0); byte r = ReadByteLittleEndian(alpha, 0);
byte g = ReadByteLittleEndian(alpha, 2); byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4); byte b = ReadByteLittleEndian(alpha, 4);
this.rgb24Trans = new Rgb24(r, g, b); pngMetaData.TransparentRgb24 = new Rgb24(r, g, b);
this.hasTrans = true; pngMetaData.HasTrans = true;
} }
} }
else if (this.pngColorType == PngColorType.Grayscale) else if (this.pngColorType == PngColorType.Grayscale)
@ -850,14 +833,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
if (this.header.BitDepth == 16) if (this.header.BitDepth == 16)
{ {
this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); pngMetaData.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)));
} }
else else
{ {
this.luminanceTrans = ReadByteLittleEndian(alpha, 0); pngMetaData.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0));
} }
this.hasTrans = true; pngMetaData.HasTrans = true;
} }
} }
} }

207
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -42,6 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The configuration instance for the decoding operation
/// </summary>
private Configuration configuration;
/// <summary> /// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks. /// The maximum block size, defaults at 64k for uncompressed blocks.
/// </summary> /// </summary>
@ -200,6 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
this.width = image.Width; this.width = image.Width;
this.height = image.Height; this.height = image.Height;
@ -236,7 +243,8 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
// Create quantized frame returning the palette and set the bit depth. // Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame); quantized = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration())
.QuantizeFrame(image.Frames.RootFrame);
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
bits = Math.Max(bits, quantizedBits); bits = Math.Max(bits, quantizedBits);
@ -282,6 +290,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePaletteChunk(stream, quantized); this.WritePaletteChunk(stream, quantized);
} }
if (pngMetaData.HasTrans)
{
this.WriteTransparencyChunk(stream, pngMetaData);
}
this.WritePhysicalChunk(stream, metaData); this.WritePhysicalChunk(stream, metaData);
this.WriteGammaChunk(stream); this.WriteGammaChunk(stream);
this.WriteExifChunk(stream, metaData); this.WriteExifChunk(stream, metaData);
@ -312,27 +325,27 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan) private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// Use ITU-R recommendation 709 to match libpng.
const float RX = .2126F;
const float GX = .7152F;
const float BX = .0722F;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan(); Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan); ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan);
if (this.pngColorType.Equals(PngColorType.Grayscale)) if (this.pngColorType.Equals(PngColorType.Grayscale))
{ {
// TODO: Research and add support for grayscale plus tRNS
if (this.use16Bit) if (this.use16Bit)
{ {
// 16 bit grayscale // 16 bit grayscale
Rgb48 rgb = default; using (IMemoryOwner<Gray16> luminanceBuffer = this.memoryAllocator.Allocate<Gray16>(rowSpan.Length))
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{ {
Unsafe.Add(ref rowSpanRef, x).ToRgb48(ref rgb); Span<Gray16> luminanceSpan = luminanceBuffer.GetSpan();
ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)); ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance); PixelOperations<TPixel>.Instance.ToGray16(this.configuration, rowSpan, luminanceSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2)
{
Gray16 luminance = Unsafe.Add(ref luminanceRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue);
}
} }
} }
else else
@ -340,30 +353,29 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth == 8) if (this.bitDepth == 8)
{ {
// 8 bit grayscale // 8 bit grayscale
Rgb24 rgb = default; PixelOperations<TPixel>.Instance.ToGray8Bytes(
for (int x = 0; x < rowSpan.Length; x++) this.configuration,
{ rowSpan,
Unsafe.Add(ref rowSpanRef, x).ToRgb24(ref rgb); rawScanlineSpan,
Unsafe.Add(ref rawScanlineSpanRef, x) = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)); rowSpan.Length);
}
} }
else else
{ {
// 1, 2, and 4 bit grayscale // 1, 2, and 4 bit grayscale
using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(rowSpan.Length, AllocationOptions.Clean)) using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(
rowSpan.Length,
AllocationOptions.Clean))
{ {
int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(this.bitDepth) - 1); int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan(); Span<byte> tempSpan = temp.GetSpan();
ref byte tempSpanRef = ref MemoryMarshal.GetReference(tempSpan);
Rgb24 rgb = default; // We need to first create an array of luminance bytes then scale them down to the correct bit depth.
for (int x = 0; x < rowSpan.Length; x++) PixelOperations<TPixel>.Instance.ToGray8Bytes(
{ this.configuration,
Unsafe.Add(ref rowSpanRef, x).ToRgb24(ref rgb); rowSpan,
float luminance = ((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)) / scaleFactor; tempSpan,
Unsafe.Add(ref tempSpanRef, x) = (byte)luminance; rowSpan.Length);
this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth); this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
} }
} }
} }
@ -373,23 +385,33 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.use16Bit) if (this.use16Bit)
{ {
// 16 bit grayscale + alpha // 16 bit grayscale + alpha
Rgba64 rgba = default; // TODO: Should we consider in the future a GrayAlpha32 type.
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 4) using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
{ {
Unsafe.Add(ref rowSpanRef, x).ToRgba64(ref rgba); Span<Rgba64> rgbaSpan = rgbaBuffer.GetSpan();
ushort luminance = (ushort)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B)); ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance); PixelOperations<TPixel>.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rgbaSpan.Length; x++, o += 4)
{
Rgba64 rgba = Unsafe.Add(ref rgbaRef, x);
ushort luminance = ImageMaths.Get16BitBT709Luminance(rgba.R, rgba.G, rgba.B);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A);
}
} }
} }
else else
{ {
// 8 bit grayscale + alpha // 8 bit grayscale + alpha
// TODO: Should we consider in the future a GrayAlpha16 type.
Rgba32 rgba = default; Rgba32 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2) for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{ {
Unsafe.Add(ref rowSpanRef, x).ToRgba32(ref rgba); Unsafe.Add(ref rowSpanRef, x).ToRgba32(ref rgba);
Unsafe.Add(ref rawScanlineSpanRef, o) = (byte)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B)); Unsafe.Add(ref rawScanlineSpanRef, o) =
ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
Unsafe.Add(ref rawScanlineSpanRef, o + 1) = rgba.A; Unsafe.Add(ref rawScanlineSpanRef, o + 1) = rgba.A;
} }
} }
@ -411,29 +433,43 @@ namespace SixLabors.ImageSharp.Formats.Png
case 4: case 4:
{ {
// 8 bit Rgba // 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width); PixelOperations<TPixel>.Instance.ToRgba32Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
this.width);
break; break;
} }
case 3: case 3:
{ {
// 8 bit Rgb // 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width); PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
this.width);
break; break;
} }
case 8: case 8:
{ {
// 16 bit Rgba // 16 bit Rgba
Rgba64 rgba = default; using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
{ {
Unsafe.Add(ref rowSpanRef, x).ToRgba64(ref rgba); Span<Rgba64> rgbaSpan = rgbaBuffer.GetSpan();
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R); ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G); PixelOperations<TPixel>.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A); // Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
{
Rgba64 rgba = Unsafe.Add(ref rgbaRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A);
}
} }
break; break;
@ -442,14 +478,20 @@ namespace SixLabors.ImageSharp.Formats.Png
default: default:
{ {
// 16 bit Rgb // 16 bit Rgb
Rgb48 rgb = default; using (IMemoryOwner<Rgb48> rgbBuffer = this.memoryAllocator.Allocate<Rgb48>(rowSpan.Length))
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
{ {
Unsafe.Add(ref rowSpanRef, x).ToRgb48(ref rgb); Span<Rgb48> rgbSpan = rgbBuffer.GetSpan();
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R); ref Rgb48 rgbRef = ref MemoryMarshal.GetReference(rgbSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G); PixelOperations<TPixel>.Instance.ToRgb48(this.configuration, rowSpan, rgbSpan);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
{
Rgb48 rgb = Unsafe.Add(ref rgbRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B);
}
} }
break; break;
@ -624,7 +666,6 @@ namespace SixLabors.ImageSharp.Formats.Png
TPixel[] palette = quantized.Palette; TPixel[] palette = quantized.Palette;
int paletteLength = Math.Min(palette.Length, 256); int paletteLength = Math.Min(palette.Length, 256);
int colorTableLength = paletteLength * 3; int colorTableLength = paletteLength * 3;
Rgba32 rgba = default;
bool anyAlpha = false; bool anyAlpha = false;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength)) using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
@ -634,6 +675,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan()); ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
Span<byte> quantizedSpan = quantized.GetPixelSpan(); Span<byte> quantizedSpan = quantized.GetPixelSpan();
Rgba32 rgba = default;
for (int i = 0; i < paletteLength; i++) for (int i = 0; i < paletteLength; i++)
{ {
if (quantizedSpan.IndexOf((byte)i) > -1) if (quantizedSpan.IndexOf((byte)i) > -1)
@ -662,7 +705,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// Write the transparency data // Write the transparency data
if (anyAlpha) if (anyAlpha)
{ {
this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaTable.Array, 0, paletteLength); this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
} }
} }
} }
@ -710,6 +753,51 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
} }
/// <summary>
/// Writes the transparency chunk to the stream
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="pngMetaData">The image meta data.</param>
private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData)
{
Span<byte> alpha = this.chunkDataBuffer.AsSpan();
if (pngMetaData.ColorType.Equals(PngColorType.Rgb))
{
if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit)
{
Rgb48 rgb = pngMetaData.TransparentRgb48.Value;
BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R);
BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G);
BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B);
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6);
}
else if (pngMetaData.TransparentRgb24.HasValue)
{
alpha.Clear();
Rgb24 rgb = pngMetaData.TransparentRgb24.Value;
alpha[1] = rgb.R;
alpha[3] = rgb.G;
alpha[5] = rgb.B;
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6);
}
}
else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale))
{
if (pngMetaData.TransparentGray16.HasValue && this.use16Bit)
{
BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue);
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
}
else if (pngMetaData.TransparentGray8.HasValue)
{
alpha.Clear();
alpha[1] = pngMetaData.TransparentGray8.Value.PackedValue;
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
}
}
}
/// <summary> /// <summary>
/// Writes the pixel information to the stream. /// Writes the pixel information to the stream.
/// </summary> /// </summary>
@ -851,20 +939,21 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="source">The source span in 8 bits.</param> /// <param name="source">The source span in 8 bits.</param>
/// <param name="result">The resultant span in <paramref name="bits"/>.</param> /// <param name="result">The resultant span in <paramref name="bits"/>.</param>
/// <param name="bits">The bit depth.</param> /// <param name="bits">The bit depth.</param>
private void ScaleDownFrom8BitArray(ReadOnlySpan<byte> source, Span<byte> result, int bits) /// <param name="scale">The scaling factor.</param>
private void ScaleDownFrom8BitArray(ReadOnlySpan<byte> source, Span<byte> result, int bits, float scale = 1)
{ {
ref byte sourceRef = ref MemoryMarshal.GetReference(source); ref byte sourceRef = ref MemoryMarshal.GetReference(source);
ref byte resultRef = ref MemoryMarshal.GetReference(result); ref byte resultRef = ref MemoryMarshal.GetReference(result);
byte mask = (byte)(0xFF >> (8 - bits));
byte shift0 = (byte)(8 - bits);
int shift = 8 - bits; int shift = 8 - bits;
byte mask = (byte)(0xFF >> shift);
byte shift0 = (byte)shift;
int v = 0; int v = 0;
int resultOffset = 0; int resultOffset = 0;
for (int i = 0; i < source.Length; i++) for (int i = 0; i < source.Length; i++)
{ {
int value = Unsafe.Add(ref sourceRef, i) & mask; int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, i) / scale)) & mask;
v |= value << shift; v |= value << shift;
if (shift == 0) if (shift == 0)
@ -907,4 +996,4 @@ namespace SixLabors.ImageSharp.Formats.Png
return scanlineLength / mod; return scanlineLength / mod;
} }
} }
} }

32
src/ImageSharp/Formats/Png/PngMetaData.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
@ -24,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.BitDepth = other.BitDepth; this.BitDepth = other.BitDepth;
this.ColorType = other.ColorType; this.ColorType = other.ColorType;
this.Gamma = other.Gamma; this.Gamma = other.Gamma;
this.HasTrans = other.HasTrans;
this.TransparentGray8 = other.TransparentGray8;
this.TransparentGray16 = other.TransparentGray16;
this.TransparentRgb24 = other.TransparentRgb24;
this.TransparentRgb48 = other.TransparentRgb48;
} }
/// <summary> /// <summary>
@ -42,6 +49,31 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
public float Gamma { get; set; } public float Gamma { get; set; }
/// <summary>
/// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent
/// </summary>
public Rgb24? TransparentRgb24 { get; set; }
/// <summary>
/// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent
/// </summary>
public Rgb48? TransparentRgb48 { get; set; }
/// <summary>
/// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent
/// </summary>
public Gray8? TransparentGray8 { get; set; }
/// <summary>
/// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent
/// </summary>
public Gray16? TransparentGray16 { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded
/// </summary>
public bool HasTrans { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new PngMetaData(this); public IDeepCloneable DeepClone() => new PngMetaData(this);
} }

118
src/ImageSharp/Formats/Png/PngScanlineProcessor.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
/// Provides methods to allow the decoding of raw scanlines to image rows of different pixel formats. /// Provides methods to allow the decoding of raw scanlines to image rows of different pixel formats.
/// TODO: We should make this a stateful class or struct to reduce the number of arguments on methods (most are invariant).
/// </summary> /// </summary>
internal static class PngScanlineProcessor internal static class PngScanlineProcessor
{ {
@ -19,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
bool hasTrans, bool hasTrans,
ushort luminance16Trans, Gray16 luminance16Trans,
byte luminanceTrans) Gray8 luminanceTrans)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel pixel = default; TPixel pixel = default;
@ -32,30 +33,19 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
if (header.BitDepth == 16) if (header.BitDepth == 16)
{ {
Rgb48 rgb48 = default;
for (int x = 0, o = 0; x < header.Width; x++, o += 2) for (int x = 0, o = 0; x < header.Width; x++, o += 2)
{ {
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance; pixel.FromGray16(new Gray16(luminance));
rgb48.G = luminance;
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
// TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method.
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < header.Width; x++) for (int x = 0; x < header.Width; x++)
{ {
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor);
rgba32.R = luminance; pixel.FromGray8(new Gray8(luminance));
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -72,14 +62,15 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance; rgba64.R = luminance;
rgba64.G = luminance; rgba64.G = luminance;
rgba64.B = luminance; rgba64.B = luminance;
rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue; rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
for (int x = 0; x < header.Width; x++) for (int x = 0; x < header.Width; x++)
{ {
@ -87,9 +78,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.R = luminance; rgba32.R = luminance;
rgba32.G = luminance; rgba32.G = luminance;
rgba32.B = luminance; rgba32.B = luminance;
rgba32.A = luminance.Equals(luminanceTrans) ? byte.MinValue : byte.MaxValue; rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32); pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -102,8 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int pixelOffset, int pixelOffset,
int increment, int increment,
bool hasTrans, bool hasTrans,
ushort luminance16Trans, Gray16 luminance16Trans,
byte luminanceTrans) Gray8 luminanceTrans)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel pixel = default; TPixel pixel = default;
@ -115,30 +106,19 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
if (header.BitDepth == 16) if (header.BitDepth == 16)
{ {
Rgb48 rgb48 = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2)
{ {
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance; pixel.FromGray16(new Gray16(luminance));
rgb48.G = luminance;
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
// TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method.
var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++)
{ {
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
rgba32.R = luminance; pixel.FromGray8(new Gray8(luminance));
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -155,14 +135,15 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance; rgba64.R = luminance;
rgba64.G = luminance; rgba64.G = luminance;
rgba64.B = luminance; rgba64.B = luminance;
rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue; rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
for (int x = pixelOffset; x < header.Width; x += increment) for (int x = pixelOffset; x < header.Width; x += increment)
{ {
@ -170,9 +151,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.R = luminance; rgba32.R = luminance;
rgba32.G = luminance; rgba32.G = luminance;
rgba32.B = luminance; rgba32.B = luminance;
rgba32.A = luminance.Equals(luminanceTrans) ? byte.MinValue : byte.MaxValue; rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32); pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -202,26 +183,25 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = luminance; rgba64.B = luminance;
rgba64.A = alpha; rgba64.A = alpha;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
int bps = bytesPerSample;
for (int x = 0; x < header.Width; x++) for (int x = 0; x < header.Width; x++)
{ {
int offset = x * bytesPerPixel; int offset = x * bytesPerPixel;
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bps); byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
rgba32.R = luminance; rgba32.R = luminance;
rgba32.G = luminance; rgba32.G = luminance;
rgba32.B = luminance; rgba32.B = luminance;
rgba32.A = alpha; rgba32.A = alpha;
pixel.PackFromRgba32(rgba32); pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -253,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = luminance; rgba64.B = luminance;
rgba64.A = alpha; rgba64.A = alpha;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -270,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.B = luminance; rgba32.B = luminance;
rgba32.A = alpha; rgba32.A = alpha;
pixel.PackFromRgba32(rgba32); pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -303,20 +283,18 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
pixel.PackFromRgba32(rgba); pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
// TODO: We should have PackFromRgb24.
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < header.Width; x++) for (int x = 0; x < header.Width; x++)
{ {
int index = Unsafe.Add(ref scanlineSpanRef, x); int index = Unsafe.Add(ref scanlineSpanRef, x);
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba); pixel.FromRgb24(rgb);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -350,25 +328,25 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba); pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++)
{ {
int index = Unsafe.Add(ref scanlineSpanRef, o); int index = Unsafe.Add(ref scanlineSpanRef, o);
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba); pixel.FromRgb24(rgb);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
} }
public static void ProcessRgbScanline<TPixel>( public static void ProcessRgbScanline<TPixel>(
Configuration configuration,
in PngHeader header, in PngHeader header,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
@ -380,7 +358,6 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (!hasTrans) if (!hasTrans)
@ -394,13 +371,13 @@ namespace SixLabors.ImageSharp.Formats.Png
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
pixel.PackFromRgb48(rgb48); pixel.FromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineSpan, rowSpan, header.Width); PixelOperations<TPixel>.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width);
} }
return; return;
@ -419,7 +396,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.Rgb = rgb48; rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue; rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -434,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.Rgb = rgb24; rgba32.Rgb = rgb24;
rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue; rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32); pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -472,7 +449,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.Rgb = rgb48; rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue; rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -485,7 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
pixel.PackFromRgb48(rgb48); pixel.FromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -503,26 +480,27 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample));
rgba.A = rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; rgba.A = rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba); pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
var rgba = new Rgba32(0, 0, 0, byte.MaxValue); Rgb24 rgb = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel)
{ {
rgba.R = Unsafe.Add(ref scanlineSpanRef, o); rgb.R = Unsafe.Add(ref scanlineSpanRef, o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); rgb.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample);
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); rgb.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample));
pixel.PackFromRgba32(rgba); pixel.FromRgb24(rgb);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
} }
public static void ProcessRgbaScanline<TPixel>( public static void ProcessRgbaScanline<TPixel>(
Configuration configuration,
in PngHeader header, in PngHeader header,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
@ -543,13 +521,13 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample));
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineSpan, rowSpan, header.Width); PixelOperations<TPixel>.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width);
} }
} }
@ -577,7 +555,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample));
pixel.PackFromRgba64(rgba64); pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -591,7 +569,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample));
rgba.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * bytesPerSample)); rgba.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * bytesPerSample));
pixel.PackFromRgba32(rgba); pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }

2
src/ImageSharp/IImageInfo.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
/// <summary> /// <summary>
/// Encapsulates properties that descibe basic image information including dimensions, pixel type information /// Encapsulates properties that describe basic image information including dimensions, pixel type information
/// and additional metadata /// and additional metadata
/// </summary> /// </summary>
public interface IImageInfo public interface IImageInfo

24
src/ImageSharp/Image.Decode.cs

@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp
/// </content> /// </content>
public static partial class Image public static partial class Image
{ {
/// <summary>
/// Creates an <see cref="Image{TPixel}"/> instance backed by an uninitialized memory buffer.
/// This is an optimized creation method intended to be used by decoders.
/// The image might be filled with memory garbage.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="width">The width of the image</param>
/// <param name="height">The height of the image</param>
/// <param name="metadata">The <see cref="ImageMetaData"/></param>
/// <returns>The result <see cref="Image{TPixel}"/></returns>
internal static Image<TPixel> CreateUninitialized<TPixel>(
Configuration configuration,
int width,
int height,
ImageMetaData metadata)
where TPixel : struct, IPixel<TPixel>
{
Buffer2D<TPixel> uninitializedMemoryBuffer =
configuration.MemoryAllocator.Allocate2D<TPixel>(width, height);
return new Image<TPixel>(configuration, uninitializedMemoryBuffer.MemorySource, width, height, metadata);
}
/// <summary> /// <summary>
/// By reading the header on the provided stream this calculates the images format. /// By reading the header on the provided stream this calculates the images format.
/// </summary> /// </summary>

5
src/ImageSharp/Image.FromBytes.cs

@ -198,18 +198,17 @@ namespace SixLabors.ImageSharp
return null; return null;
} }
IImageFormat format = default;
foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors) foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors)
{ {
IImageFormat f = detector.DetectFormat(data); IImageFormat f = detector.DetectFormat(data);
if (f != null) if (f != null)
{ {
format = f; return f;
} }
} }
return format; return default;
} }
/// <summary> /// <summary>

24
src/ImageSharp/Image.WrapMemory.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
public static partial class Image public static partial class Image
{ {
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle. /// The memory is being observed, the caller remains responsible for managing it's lifecycle.
/// </summary> /// </summary>
@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance, /// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>. /// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image. /// It will be disposed together with the result image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param> /// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param> /// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param> /// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param> /// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param> /// <param name="metaData">The <see cref="ImageMetaData"/></param>
@ -105,15 +105,15 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance, /// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>. /// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image. /// It will be disposed together with the result image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param> /// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param> /// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param> /// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param> /// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns> /// <returns>An <see cref="Image{TPixel}"/> instance</returns>
@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels, /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance, /// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>. /// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image. /// It will be disposed together with the result image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param> /// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param> /// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param> /// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns> /// <returns>An <see cref="Image{TPixel}"/> instance</returns>

4
src/ImageSharp/ImageExtensions.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
if (format is null) if (format is null)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); sb.AppendLine($"Can't find a format that is associated with the file extension '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{ {
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp
if (encoder is null) if (encoder is null)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); sb.AppendLine($"Can't find encoder for file extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{ {
sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");

32
src/ImageSharp/ImageFrame{TPixel}.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
@ -23,7 +21,6 @@ namespace SixLabors.ImageSharp
public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly Configuration configuration;
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
@ -86,7 +83,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));
this.configuration = configuration; this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator; this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(width, height); this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(width, height);
this.MetaData = metaData ?? new ImageFrameMetaData(); this.MetaData = metaData ?? new ImageFrameMetaData();
@ -120,7 +117,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData)); Guard.NotNull(metaData, nameof(metaData));
this.configuration = configuration; this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator; this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D<TPixel>(memorySource, width, height); this.PixelBuffer = new Buffer2D<TPixel>(memorySource, width, height);
this.MetaData = metaData; this.MetaData = metaData;
@ -136,7 +133,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));
this.configuration = configuration; this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator; this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height); this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan()); source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan());
@ -148,6 +145,11 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
public MemoryAllocator MemoryAllocator { get; } public MemoryAllocator MemoryAllocator { get; }
/// <summary>
/// Gets the <see cref="Configuration"/> instance associated with this <see cref="ImageFrame{TPixel}"/>.
/// </summary>
internal Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor. /// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary> /// </summary>
@ -250,13 +252,13 @@ namespace SixLabors.ImageSharp
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})";
/// <summary> /// <summary>
/// Clones the current instance. /// Clones the current instance.
/// </summary> /// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns> /// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> Clone() => this.Clone(this.configuration); internal ImageFrame<TPixel> Clone() => this.Clone(this.Configuration);
/// <summary> /// <summary>
/// Clones the current instance. /// Clones the current instance.
@ -271,7 +273,7 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel2">The pixel format.</typeparam> /// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="ImageFrame{TPixel2}"/></returns> /// <returns>The <see cref="ImageFrame{TPixel2}"/></returns>
internal ImageFrame<TPixel2> CloneAs<TPixel2>() internal ImageFrame<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2> => this.CloneAs<TPixel2>(this.configuration); where TPixel2 : struct, IPixel<TPixel2> => this.CloneAs<TPixel2>(this.Configuration);
/// <summary> /// <summary>
/// Returns a copy of the image frame in the given pixel format. /// Returns a copy of the image frame in the given pixel format.
@ -289,22 +291,16 @@ namespace SixLabors.ImageSharp
var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.MetaData.DeepClone()); var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.MetaData.DeepClone());
ParallelHelper.IterateRowsWithTempBuffer<Vector4>( ParallelHelper.IterateRows(
this.Bounds(), this.Bounds(),
configuration, configuration,
(rows, tempRowBuffer) => (rows) =>
{ {
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> sourceRow = this.GetPixelRowSpan(y); Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y); Span<TPixel2> targetRow = target.GetPixelRowSpan(y);
Span<Vector4> tempRowSpan = tempRowBuffer.Span; PixelOperations<TPixel>.Instance.To(configuration, sourceRow, targetRow);
PixelOperations<TPixel>.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
PixelOperations<TPixel2>.Instance.PackFromScaledVector4(
tempRowSpan,
targetRow,
targetRow.Length);
} }
}); });

95
src/ImageSharp/ImageSharp.csproj

@ -5,7 +5,7 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix> <VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix> <VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors> <Authors>Six Labors and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;netcoreapp2.1;net472</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName> <AssemblyName>SixLabors.ImageSharp</AssemblyName>
@ -31,27 +31,32 @@
<Features>IOperation</Features> <Features>IOperation</Features>
<LangVersion>Latest</LangVersion> <LangVersion>Latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' OR '$(TargetFramework)' == 'net472' ">
<DefineConstants>$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" /> <Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0006" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-dev000089" />
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3'"> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" /> <PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> <PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>
<RootNamespace>SixLabors.ImageSharp</RootNamespace> <RootNamespace>SixLabors.ImageSharp</RootNamespace>
@ -72,14 +77,46 @@
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Block8x8F.Generated.cs</LastGenOutput> <LastGenOutput>Block8x8F.Generated.cs</LastGenOutput>
</None> </None>
<None Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.tt"> <None Update="PixelFormats\PixelOperations{TPixel}.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>PixelOperations{TPixel}.Generated.cs</LastGenOutput> <LastGenOutput>PixelOperations{TPixel}.Generated.cs</LastGenOutput>
</None> </None>
<None Update="PixelFormats\Generated\Rgba32.PixelOperations.Generated.tt"> <None Update="PixelFormats\PixelImplementations\Generated\Argb32.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Argb32.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Bgr24.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Bgr24.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Bgra32.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Bgra32.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Gray8.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Gray8.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Gray16.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Gray16.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Rgb24.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Rgb24.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Rgba32.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Rgba32.PixelOperations.Generated.cs</LastGenOutput> <LastGenOutput>Rgba32.PixelOperations.Generated.cs</LastGenOutput>
</None> </None>
<None Update="PixelFormats\PixelImplementations\Generated\Rgb48.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Rgb48.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelImplementations\Generated\Rgba64.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Rgba64.PixelOperations.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\PixelBlenders\PorterDuffFunctions.Generated.tt"> <None Update="PixelFormats\PixelBlenders\PorterDuffFunctions.Generated.tt">
<LastGenOutput>PorterDuffFunctions.Generated.cs</LastGenOutput> <LastGenOutput>PorterDuffFunctions.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
@ -105,16 +142,56 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Block8x8F.Generated.tt</DependentUpon> <DependentUpon>Block8x8F.Generated.tt</DependentUpon>
</Compile> </Compile>
<Compile Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.cs"> <Compile Update="PixelFormats\PixelOperations{TPixel}.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>PixelOperations{TPixel}.Generated.tt</DependentUpon> <DependentUpon>PixelOperations{TPixel}.Generated.tt</DependentUpon>
</Compile> </Compile>
<Compile Update="PixelFormats\Generated\Rgba32.PixelOperations.Generated.cs"> <Compile Update="PixelFormats\PixelImplementations\Generated\Argb32.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Argb32.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Bgr24.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Bgr24.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Bgra32.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Bgra32.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Gray8.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Gray8.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Gray16.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Gray16.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Rgb24.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Rgb24.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Rgba32.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Rgba32.PixelOperations.Generated.tt</DependentUpon> <DependentUpon>Rgba32.PixelOperations.Generated.tt</DependentUpon>
</Compile> </Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Rgb48.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Rgb48.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelImplementations\Generated\Rgba64.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Rgba64.PixelOperations.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelBlenders\DefaultPixelBlenders.Generated.cs"> <Compile Update="PixelFormats\PixelBlenders\DefaultPixelBlenders.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>

9
src/ImageSharp/ImageSharp.csproj.DotSettings

@ -1,3 +1,10 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cgenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpackedpixels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixelimplementations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixeltypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cutils/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresamplers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresize/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

2
src/ImageSharp/Image{TPixel}.cs

@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp
public Image<TPixel> Clone() => this.Clone(this.configuration); public Image<TPixel> Clone() => this.Clone(this.configuration);
/// <summary> /// <summary>
/// Clones the current image with the given configueation. /// Clones the current image with the given configuration.
/// </summary> /// </summary>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param> /// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <returns>Returns a new <see cref="Image{TPixel}"/> with all the same pixel data as the original.</returns> /// <returns>Returns a new <see cref="Image{TPixel}"/> with all the same pixel data as the original.</returns>

13
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{ {
this.Parts = ExifParts.All; this.Parts = ExifParts.All;
this.data = data; this.data = data;
this.InvalidTags = new List<ExifTag>(); this.InvalidTags = Array.Empty<ExifTag>();
} }
/// <summary> /// <summary>
@ -63,7 +63,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.Parts = other.Parts; this.Parts = other.Parts;
this.thumbnailLength = other.thumbnailLength; this.thumbnailLength = other.thumbnailLength;
this.thumbnailOffset = other.thumbnailOffset; this.thumbnailOffset = other.thumbnailOffset;
this.InvalidTags = new List<ExifTag>(other.InvalidTags);
this.InvalidTags = other.InvalidTags.Count > 0
? new List<ExifTag>(other.InvalidTags)
: (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>();
if (other.values != null) if (other.values != null)
{ {
this.values = new List<ExifValue>(other.Values.Count); this.values = new List<ExifValue>(other.Values.Count);
@ -289,7 +293,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.values = reader.ReadValues(); this.values = reader.ReadValues();
this.InvalidTags = new List<ExifTag>(reader.InvalidTags); this.InvalidTags = reader.InvalidTags.Count > 0
? new List<ExifTag>(reader.InvalidTags)
: (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>();
this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength; this.thumbnailLength = (int)reader.ThumbnailLength;
} }

43
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
internal sealed class ExifReader internal sealed class ExifReader
{ {
private readonly List<ExifTag> invalidTags = new List<ExifTag>(); private List<ExifTag> invalidTags;
private readonly byte[] exifData; private readonly byte[] exifData;
private int position; private int position;
private Endianness endianness = Endianness.BigEndian; private Endianness endianness = Endianness.BigEndian;
@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Gets the invalid tags. /// Gets the invalid tags.
/// </summary> /// </summary>
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags; public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags ?? (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>();
/// <summary> /// <summary>
/// Gets the thumbnail length in the byte stream /// Gets the thumbnail length in the byte stream
@ -125,7 +124,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0]; private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer) private string ConvertToString(ReadOnlySpan<byte> buffer)
{ {
int nullCharIndex = buffer.IndexOf((byte)0); int nullCharIndex = buffer.IndexOf((byte)0);
@ -338,7 +337,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
// Ensure that the new index does not overrun the data // Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue) if (newIndex > int.MaxValue)
{ {
this.invalidTags.Add(tag); this.AddInvalidTag(tag);
exifValue = default; exifValue = default;
@ -349,7 +348,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.RemainingLength < size) if (this.RemainingLength < size)
{ {
this.invalidTags.Add(tag); this.AddInvalidTag(tag);
this.position = oldIndex; this.position = oldIndex;
exifValue = default; exifValue = default;
@ -372,13 +372,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return true; return true;
} }
private void AddInvalidTag(ExifTag tag)
{
if (this.invalidTags == null)
{
this.invalidTags = new List<ExifTag>();
}
this.invalidTags.Add(tag);
}
[MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue) private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
where TEnum : struct where TEnum : struct
{ {
var enumValue = (TEnum)(object)value; if (EnumHelper<TEnum>.IsDefined(value))
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
{ {
return enumValue; return Unsafe.As<int, TEnum>(ref value);
} }
return defaultValue; return defaultValue;
@ -547,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer) ? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer); : BinaryPrimitives.ReadInt16LittleEndian(buffer);
} }
private class EnumHelper<TEnum>
where TEnum : struct
{
private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>()
.Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsDefined(int value)
{
return Array.BinarySearch(Values, value) >= 0;
}
}
} }
} }

4
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -17,8 +17,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Which parts will be written. /// Which parts will be written.
/// </summary> /// </summary>
private ExifParts allowedParts; private readonly ExifParts allowedParts;
private IList<ExifValue> values; private readonly IList<ExifValue> values;
private List<int> dataOffsets; private List<int> dataOffsets;
private readonly List<int> ifdIndexes; private readonly List<int> ifdIndexes;
private readonly List<int> exifIndexes; private readonly List<int> exifIndexes;

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

Loading…
Cancel
Save