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.
#### **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?**
* 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
is_32bit: True
- target_framework: net471
- target_framework: net472
is_32bit: False
- target_framework: net471
- target_framework: net472
is_32bit: True
- target_framework: net462

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

@ -38,8 +38,8 @@
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta0007" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0007" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-dev000119" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000102" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</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);
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);
TPixel resultColor = default;
resultColor.PackFromVector4(result);
resultColor.FromVector4(result);
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);
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);
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;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelHelper.IterateRows(
@ -93,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
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.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
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.
// 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#
// region to alline with the pixel grid.
// region to align with the pixel grid.
float offset = 0.5f;
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;
if (subpixelCount < 4)
{
@ -107,6 +107,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush);
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
@ -121,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
// nothing on this line skip
// nothing on this line, skip
continue;
}
@ -168,16 +170,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
if (!this.Options.Antialias)
{
bool hasOnes = false;
bool hasZeros = false;
for (int x = 0; x < scanlineWidth; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
hasOnes = true;
}
else
{
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="brush">The brush to source pixel colors from.</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)
{
Guard.NotNull(text, nameof(text));
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
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)
{
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.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)
@ -138,10 +139,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
fistRow = -startY;
}
int end = operation.Map.Height;
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++)
{
@ -164,18 +163,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
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 GlyphRendererParameters currentGlyphRenderParams = default;
private int offset = 0;
private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
private readonly int offset = 0;
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 bool renderFill = false;
private bool raterizationRequired = false;
private readonly bool renderOutline = false;
private readonly bool renderFill = false;
private bool rasterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
@ -213,17 +220,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
this.builder.StartFigure();
}
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters paramters)
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters)
{
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
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
this.currentGlyphRenderParams = paramters;
if (this.glyphData.ContainsKey(paramters))
this.currentGlyphRenderParams = (parameters, subPixelOffset);
if (this.glyphData.ContainsKey(this.currentGlyphRenderParams))
{
// we have already drawn the glyph vectors skip trying again
this.raterizationRequired = false;
this.rasterizationRequired = 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
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
this.raterizationRequired = true;
this.rasterizationRequired = true;
return true;
}
@ -252,7 +264,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
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();
}
@ -270,7 +282,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
if (this.raterizationRequired)
if (this.rasterizationRequired)
{
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 :)
var maxColor = default(TPixel);
maxColor.PackFromVector4(new Vector4(float.MaxValue));
maxColor.FromVector4(new Vector4(float.MaxValue));
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;
}
@ -158,7 +158,12 @@ namespace SixLabors.ImageSharp.Processing
}
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 />
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;
Configuration configuration = this.Target.Configuration;
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
{
@ -108,7 +119,12 @@ namespace SixLabors.ImageSharp.Processing
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>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <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)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetSpan();
@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span retuned from Pixel source
/// The span returned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span retuned from Pixel source
/// The span returned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
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>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -70,10 +70,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -103,10 +103,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -136,10 +136,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -171,10 +171,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -203,10 +203,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -236,10 +236,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -268,10 +268,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -301,10 +301,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -334,10 +334,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -367,10 +367,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -400,10 +400,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);
@ -433,10 +433,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination, int count)
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination)
{
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 CieLab destRef = ref MemoryMarshal.GetReference(destination);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -12,26 +12,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
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>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination)
{
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 HunterLab destRef = ref MemoryMarshal.GetReference(destination);
@ -240,14 +160,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Hsl color)
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
@ -255,180 +185,250 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination)
{
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);
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);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Hsv color)
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination)
{
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);
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);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in LinearRgb color)
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination, int count)
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination)
{
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);
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);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
/// 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 Lms color)
public HunterLab ToHunterLab(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/>
/// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination, int count)
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieLch color)
{
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);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
/// <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);
}
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
/// <summary>
/// Converts a <see cref="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>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
/// 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 Rgb color)
public HunterLab ToHunterLab(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/>
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination, int count)
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieXyz color)
{
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);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
// Conversion
return this.cieXyzToHunterLabConverter.Convert(adapted);
}
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
/// <summary>
/// Converts a <see cref="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>
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/>
/// Converts a <see cref="Hsl"/> 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)
public HunterLab ToHunterLab(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/>
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination, int count)
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in Hsv color)
{
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);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
/// <summary>
/// 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++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,26 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
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>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination, int count)
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination)
{
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 LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
@ -433,5 +288,151 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
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>
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>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination, int count)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination)
{
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 Lms destRef = ref MemoryMarshal.GetReference(destination);
@ -423,5 +284,144 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,26 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
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>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination, int count)
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination)
{
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 Rgb destRef = ref MemoryMarshal.GetReference(destination);
@ -436,5 +288,154 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -15,27 +16,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
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>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination)
{
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 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>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination, int count)
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination)
{
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 YCbCr destRef = ref MemoryMarshal.GetReference(destination);
@ -405,5 +267,144 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
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="sourceWhitePoint">The source white point.</param>
/// <param name="destinationWhitePoint">The destination white point.</param>
/// <param name="count">The number of colors to convert.</param>
void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count);
void Transform(
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:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
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 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 c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 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:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
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 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 c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a);
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 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 kb = ComputeKb(this.HunterLabWhitePoint);
float l = 100 * MathF.Sqrt(y / yn);
float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn));
float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn));
float yByYn = y / yn;
float sqrtYbyYn = MathF.Sqrt(yByYn);
float l = 100 * sqrtYbyYn;
float a = ka * (((x / xn) - yByYn) / sqrtYbyYn);
float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn);
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 kb = ComputeKb(input.WhitePoint);
float y = ImageMaths.Pow2(l / 100F) * yn;
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);
float pow = ImageMaths.Pow2(l / 100F);
float sqrtPow = MathF.Sqrt(pow);
float y = pow * yn;
float x = (((a / ka) * sqrtPow) + pow) * xn;
float z = (((b / kb) * sqrtPow) - pow) * (-zn);
return new CieXyz(x, y, z);
}

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

@ -65,9 +65,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
/// <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))
{

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

@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp
/// <returns>The string.</returns>
public static string GetString(this Encoding encoding, ReadOnlySpan<byte> buffer)
{
if (buffer.Length == 0)
{
return string.Empty;
}
fixed (byte* bytes = buffer)
{
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>
/// Verifies, that the method parameter with specified target value is false
/// 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.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
@ -19,12 +20,13 @@ namespace SixLabors.ImageSharp
/// <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>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNull<T>(T value, string parameterName)
where T : class
{
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>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</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)
{
if (value is null)
{
throw new ArgumentNullException(parameterName);
ThrowArgumentNullException(parameterName);
}
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>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName)
{
if (value is null)
{
throw new ArgumentNullException(parameterName);
ThrowArgumentNullException(parameterName);
}
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">
/// <paramref name="value"/> is greater than the maximum value.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
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">
/// <paramref name="value"/> is greater than the maximum value.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
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">
/// <paramref name="value"/> is less than the minimum value.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) <= 0)
{
throw new ArgumentOutOfRangeException(
ThrowArgumentOutOfRangeException(
parameterName,
$"Value {value} must be greater than {min}.");
}
@ -141,12 +148,13 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue>
{
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">
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
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">
/// <paramref name="target"/> is false
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IsTrue(bool target, string parameterName, string message)
{
if (!target)
{
throw new ArgumentException(message, parameterName);
ThrowArgumentException(message, parameterName);
}
}
@ -199,11 +209,12 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is true
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IsFalse(bool target, string parameterName, string message)
{
if (target)
{
throw new ArgumentException(message, parameterName);
ThrowArgumentException(message, parameterName);
}
}
@ -217,11 +228,32 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException">
/// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName)
{
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">
/// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName)
{
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 given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
Span<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
[MethodImpl(InliningOptions.ColdPath)]
private static void ThrowArgumentException(string message, string parameterName)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
throw new ArgumentException(message, parameterName);
}
/// <summary>
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
ReadOnlySpan<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
[MethodImpl(InliningOptions.ColdPath)]
private static void ThrowArgumentOutOfRangeException(string parameterName, string message)
{
throw new ArgumentOutOfRangeException(parameterName, message);
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ThrowArgumentNullException(string parameterName)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
throw new ArgumentNullException(parameterName);
}
}
}

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

@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
@ -14,6 +15,75 @@ namespace SixLabors.ImageSharp
/// </summary>
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>
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
/// </summary>
@ -31,7 +101,6 @@ namespace SixLabors.ImageSharp
/// <summary>
/// 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>
public static int LeastCommonMultiple(int a, int b)
{
@ -39,6 +108,25 @@ namespace SixLabors.ImageSharp
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>
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
/// </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"/>
/// </param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static int FastAbs(int x)
{
int y = x >> 31;
@ -58,7 +146,7 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <param name="x">A single-precision floating-point number</param>
/// <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;
/// <summary>
@ -66,7 +154,7 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <param name="x">A single-precision floating-point number</param>
/// <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;
/// <summary>
@ -77,7 +165,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
/// <summary>
@ -85,7 +173,7 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <param name="bitDepth">The bit depth.</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth;
/// <summary>
@ -94,7 +182,7 @@ namespace SixLabors.ImageSharp
/// <param name="x">The x provided to G(x).</param>
/// <param name="sigma">The spread of the blur.</param>
/// <returns>The Gaussian G(x)</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float Gaussian(float x, float sigma)
{
const float Numerator = 1.0f;
@ -117,7 +205,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The sine cardinal of <paramref name="f" />.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float SinC(float f)
{
if (MathF.Abs(f) > Constants.Epsilon)
@ -140,7 +228,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float GetBcValue(float x, float b, float c)
{
if (x < 0F)
@ -176,7 +264,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The bounding <see cref="Rectangle"/>.
/// </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);
/// <summary>

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// 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
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!
/// </summary>
internal const string ImageSharpBuiltAgainst =
#if NETCOREAPP2_1
#if NET472
"netfx4.7.2";
#elif NETCOREAPP2_1
"netcoreapp2.1";
#elif NETSTANDARD1_3
"netstandard1.3";
#else
"netstandard2.0";
#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.InteropServices;
namespace SixLabors.ImageSharp.Common.Tuples
namespace SixLabors.ImageSharp.Tuples
{
/// <summary>
/// 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"/>!
/// TODO: Investigate defining this as union with an Octet.OfSingle type.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct Vector4Pair
@ -15,8 +16,6 @@ namespace SixLabors.ImageSharp.Common.Tuples
public Vector4 B;
private static readonly Vector4 Scale = new Vector4(1 / 255f);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MultiplyInplace(float value)
{
@ -52,8 +51,9 @@ namespace SixLabors.ImageSharp.Common.Tuples
b = b.FastRound();
// Downscale by 1/255
this.A *= Scale;
this.B *= Scale;
var scale = new Vector4(1 / 255f);
this.A *= scale;
this.B *= scale;
}
/// <summary>
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Common.Tuples
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>
/// 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
{
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (Buffer2D<byte> buffer = this.memoryAllocator.Allocate2D<byte>(width, height, AllocationOptions.Clean))
{
this.UncompressRle8(width, buffer.GetSpan());
@ -233,8 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int x = 0; x < width; x++)
{
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]);
color.PackFromRgba32(rgba);
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]));
pixelRow[x] = color;
}
}
@ -313,9 +310,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
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))
{
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
@ -363,7 +361,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
// TODO: Could use PixelOperations here!
for (int x = 0; x < arrayWidth; x++)
{
int colOffset = x * ppb;
@ -371,9 +368,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
// Stored in b-> g-> r order.
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[colorIndex]);
color.PackFromRgba32(rgba);
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIndex]));
pixelRow[newX] = color;
}
@ -397,7 +392,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding;
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
@ -412,11 +406,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
short temp = BitConverter.ToInt16(buffer.Array, offset);
rgba.R = GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10);
rgba.G = GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5);
rgba.B = GetBytesFrom5BitValue(temp & Rgb16BMask);
var rgb = new Rgb24(
GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10),
GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5),
GetBytesFrom5BitValue(temp & Rgb16BMask));
color.PackFromRgba32(rgba);
color.FromRgb24(rgb);
pixelRow[x] = color;
offset += 2;
}
@ -444,7 +439,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row);
int newY = Invert(y, height, inverted);
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);
int newY = Invert(y, height, inverted);
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;
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.
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24)

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

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

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

@ -8,6 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
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;
/// <summary>
/// The image desciptor.
/// The image descriptor.
/// </summary>
private GifImageDescriptor imageDescriptor;
@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
// Skip graphic control extension block
this.Skip(0);
this.SkipBlock(); // Skip graphic control extension block
break;
case GifConstants.CommentLabel:
this.ReadComments();
@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Could be XMP or something else not supported yet.
// Back up and skip.
this.stream.Position -= appLength + 1;
this.Skip(appLength);
this.SkipBlock(appLength);
return;
}
this.Skip(appLength); // Not supported by any known decoder.
this.SkipBlock(appLength); // Not supported by any known decoder.
}
/// <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>
/// <param name="length">The number of bytes to skip.</param>
private void Skip(int length)
private void SkipBlock(int blockSize = 0)
{
this.stream.Skip(length);
if (blockSize > 0)
{
this.stream.Skip(blockSize);
}
int flag;
while ((flag = this.stream.ReadByte()) != 0)
while ((flag = this.stream.ReadByte()) > 0)
{
this.stream.Skip(flag);
}
@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
this.SkipBlock();
}
finally
{
@ -481,22 +480,36 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
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
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
if (!transFlag)
{
int index = Unsafe.Add(ref indicesRef, i);
if (!this.graphicsControlExtension.TransparencyFlag
|| this.graphicsControlExtension.TransparencyIndex != index)
// #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++)
{
int index = Unsafe.Add(ref indicesRef, i);
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable[index];
pixel.PackFromRgba32(rgba);
Rgb24 rgb = colorTable[index];
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.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
@ -21,10 +23,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
internal sealed class GifEncoderCore
{
/// <summary>
/// Used for allocating memory during procesing operations.
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// Configuration bound to the encoding operation.
/// </summary>
private Configuration configuration;
/// <summary>
/// A reusable buffer used to reduce allocations.
/// </summary>
@ -80,6 +87,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetaData metaData = image.MetaData;
this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode;
@ -87,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Quantize the image returning a palette.
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.
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)
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++)
{
@ -149,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
using (QuantizedFrame<TPixel> paletteQuantized
= palleteQuantizer.CreateFrameQuantizer(() => quantized.Palette).QuantizeFrame(frame))
using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@ -170,15 +178,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.
if (previousFrame != null
&& previousMeta.ColorTableLength != frameMetaData.ColorTableLength
&& frameMetaData.ColorTableLength > 0)
if (previousFrame != null && previousMeta.ColorTableLength != frameMetaData.ColorTableLength
&& frameMetaData.ColorTableLength > 0)
{
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(frameMetaData.ColorTableLength).QuantizeFrame(frame);
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(
image.GetConfiguration(),
frameMetaData.ColorTableLength).QuantizeFrame(frame);
}
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
int index = -1;
Rgba32 trans = default;
int length = quantized.Palette.Length;
ref TPixel paletteRef = ref MemoryMarshal.GetReference(quantized.Palette.AsSpan());
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
using (IMemoryOwner<Rgba32> rgbaBuffer = this.memoryAllocator.Allocate<Rgba32>(length))
{
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgba32(ref trans);
if (trans.Equals(default))
Span<Rgba32> rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan);
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>
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;
}
@ -406,24 +421,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
int pixelCount = image.Palette.Length;
// The maximium number of colors for the bit depth
// The maximum number of colors for the bit depth
int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
Rgb24 rgb = default;
int pixelCount = image.Palette.Length;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
{
ref TPixel paletteRef = ref MemoryMarshal.GetReference(image.Palette.AsSpan());
ref Rgb24 rgb24Ref = ref Unsafe.As<byte, Rgb24>(ref MemoryMarshal.GetReference(colorTable.GetSpan()));
for (int i = 0; i < pixelCount; i++)
{
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgb24(ref rgb);
Unsafe.Add(ref rgb24Ref, i) = rgb;
}
// Write the palette to the stream
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
image.Palette.AsSpan(),
colorTable.GetSpan(),
pixelCount);
stream.Write(colorTable.Array, 0, colorTableLength);
}
}

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

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

4
src/ImageSharp/Formats/IImageFormat.cs

@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Formats
string Name { get; }
/// <summary>
/// Gets the default mimetype that the image foramt uses
/// Gets the default mimetype that the image format uses
/// </summary>
string DefaultMimeType { get; }
/// <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>
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
{
/// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens.
/// Represents a Jpeg block with <see cref="short"/> coefficients.
/// </summary>
// ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8 : IEquatable<Block8x8>
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <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>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>
@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <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>
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
/// <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>
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
this.CopyTo(area);
this.Copy1x1Scale(area);
return;
}
else if (horizontalScale == 2 && verticalScale == 2)
if (horizontalScale == 2 && verticalScale == 2)
{
this.CopyTo2x2(area);
this.Copy2x2Scale(area);
return;
}
ref float destBase = ref area.GetReferenceToOrigin();
// 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;
}
}
}
}
// TODO: Optimize: implement all cases with scale-specific, loopless code!
this.CopyArbitraryScale(area, horizontalScale, verticalScale);
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(in BufferArea<float> area)
public void Copy1x1Scale(in BufferArea<float> destination)
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin());
int destStride = area.Stride * sizeof(float);
ref byte destBase = ref Unsafe.As<float, byte>(ref destination.GetReferenceToOrigin());
int destStride = destination.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
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));
}
private void CopyTo2x2(in BufferArea<float> area)
private void Copy2x2Scale(in BufferArea<float> area)
{
ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride;
this.WidenCopyImpl2x2(ref destBase, 0, destStride);
this.WidenCopyImpl2x2(ref destBase, 1, destStride);
this.WidenCopyImpl2x2(ref destBase, 2, destStride);
this.WidenCopyImpl2x2(ref destBase, 3, destStride);
this.WidenCopyImpl2x2(ref destBase, 4, destStride);
this.WidenCopyImpl2x2(ref destBase, 5, destStride);
this.WidenCopyImpl2x2(ref destBase, 6, destStride);
this.WidenCopyImpl2x2(ref destBase, 7, destStride);
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin());
int destStride = area.Stride / 2;
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
}
[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 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
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;
ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
int offset = 2 * row * destStride;
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X);
xyLeft.Z = sLeft.Y;
xyLeft.W = sLeft.Y;
var zwLeft = new Vector4(sLeft.Z);
zwLeft.Z = sLeft.W;
zwLeft.W = sLeft.W;
var xyRight = new Vector4(sRight.X);
xyRight.Z = sRight.Y;
xyRight.W = sRight.Y;
var zwRight = new Vector4(sRight.Z);
zwRight.Z = sRight.W;
zwRight.W = sRight.W;
dTopLeft = xyLeft;
Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref dTopLeft, 2) = xyRight;
Unsafe.Add(ref dTopLeft, 3) = zwRight;
dBottomLeft = xyLeft;
Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
Unsafe.Add(ref dBottomLeft, 2) = xyRight;
Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl(ref Vector4 s, ref float destBase)
[MethodImpl(InliningOptions.ColdPath)]
private void CopyArbitraryScale(BufferArea<float> area, int horizontalScale, int verticalScale)
{
Unsafe.Add(ref destBase, 0) = s.X;
Unsafe.Add(ref destBase, 1) = s.X;
Unsafe.Add(ref destBase, 2) = s.Y;
Unsafe.Add(ref destBase, 3) = s.Y;
Unsafe.Add(ref destBase, 4) = s.Z;
Unsafe.Add(ref destBase, 5) = s.Z;
Unsafe.Add(ref destBase, 6) = s.W;
Unsafe.Add(ref destBase, 7) = s.W;
ref float destBase = ref area.GetReferenceToOrigin();
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;
}
}
}
}
}
}
}

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.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
d.V0L.X = V0L.X;
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
public void LoadFromInt16Scalar(ref Block8x8 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.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
<#
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
public void LoadFromInt16Scalar(ref Block8x8 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>
public float this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
get
{
GuardBlockIndex(idx);
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
set
{
GuardBlockIndex(idx);
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void Clear()
{
// 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
/// </summary>
/// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(Span<float> source)
{
ref byte s = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(source));
@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source)
{
blockPtr->LoadFrom(source);
@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
/// </summary>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(Span<float> dest)
{
ref byte d = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(dest));
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Pointer to block</param>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest)
{
float* fPtr = (float*)blockPtr;
@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<float> dest)
{
blockPtr->CopyTo(dest);
@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
/// </summary>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void CopyTo(float[] dest)
{
fixed (void* ptr = &this.V0L)
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Multiply all elements of the block.
/// </summary>
/// <param name="value">The value to multiply by</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(float value)
{
this.V0L *= value;
@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Multiply all elements of the block by the corresponding elements of 'other'
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(ref Block8x8F other)
{
this.V0L *= other.V0L;
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Adds a vector to all elements of the block.
/// </summary>
/// <param name="diff">The added vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void AddToAllInplace(Vector4 diff)
{
this.V0L += diff;
@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <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>
/// <param name="destination">The destination 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)
{
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 />
public override string ToString()
{
var sb = new StringBuilder();
sb.Append('[');
for (int i = 0; i < Size; i++)
for (int i = 0; i < Size - 1; i++)
{
sb.Append(this[i]);
if (i < Size - 1)
{
sb.Append(',');
}
sb.Append(',');
}
sb.Append(this[Size - 1]);
sb.Append(']');
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max)
{
row += off;
@ -520,13 +564,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(dividend) = max(min(dividend, 1), -1)
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);
}

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

@ -3,6 +3,8 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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)
{
// 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);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
v.X = y;
v.Y = y;
v.Z = y;
var v = new Vector4(Unsafe.Add(ref sBase, i));
v.W = 1f;
v *= scale;
result[i] = v;
Unsafe.Add(ref dBase, 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.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
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 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:
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.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
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:
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.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
using SixLabors.Memory;
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
{
/// <summary>
/// The avalilable converters
/// The available converters
/// </summary>
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;
/// <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>
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.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.
// 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);
code <<= 1;
}

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

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// 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>
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; }
/// <summary>
/// Gets the number of coponents.
/// Gets the number of components.
/// </summary>
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="densityUnits">The units for the density values</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)
{
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
/// - Applying IDCT
/// - 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>
/// <param name="sourceBlock">The source block.</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.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)]
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);
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(width, height, AllocationOptions.Clean);
}
}
}

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

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private int currentComponentRowInBlocks;
/// <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>
private readonly Size blockAreaSize;
@ -88,12 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height;
for (int x = 0; x < this.SizeInBlocks.Width; x++)
{
int xBlock = x;
int xBuffer = x * this.blockAreaSize.Width;
Span<Block8x8> blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
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(
xBuffer,

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

@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary>
internal class JpegImagePostProcessor : IDisposable
{
private readonly Configuration configuration;
/// <summary>
/// The number of block rows to be processed in one Step.
/// </summary>
@ -49,15 +51,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </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>
public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg)
public JpegImagePostProcessor(Configuration configuration, IRawJpegData rawJpeg)
{
this.configuration = configuration;
this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First();
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
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.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
@ -159,7 +163,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
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 void ParseBaselineData()
@ -179,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
int mcuRow = mcu / mcusPerLine;
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
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++)
{
if (this.eof)
@ -190,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@ -236,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
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++)
{
if (this.eof)
@ -243,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int blockRow = mcu / w;
// TODO: Isn't blockCol == i actually?
int blockCol = mcu % w;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@ -299,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
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++)
{
if (this.eof)
@ -306,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable);
}
}
@ -351,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
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++)
{
if (this.eof)
@ -358,23 +369,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int blockRow = mcu / w;
// TODO: isn't blockCol == i actually?
int blockCol = mcu % w;
ref Block8x8 block = ref blockSpan[blockCol];
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref block,
ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(
component,
blockRow,
blockCol,
ref block,
ref acHuffmanTable,
ref fastACRef);
}
@ -391,8 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable dcTable,
ref HuffmanTable acTable,
ref short fastACRef)
@ -405,7 +414,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 dc = component.DcPredictor + diff;
@ -470,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable dcTable)
{
if (this.spectralEnd != 0)
@ -481,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.CheckBits();
ref short blockDataRef = ref component.GetBlockDataReference(col, row);
ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
if (this.successiveHigh == 0)
{
@ -505,9 +513,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
private void DecodeBlockProgressiveAC(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
{
@ -516,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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)
{
@ -749,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ColdPath)]
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.
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.
// 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)
{
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>
/// Initializes the YCbCr tables
/// </summary>
/// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns>
/// <returns>The initialized <see cref="RgbToYCbCrTables"/></returns>
public static RgbToYCbCrTables Create()
{
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.
/// </summary>
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
internal struct YCbCrForwardConverter<TPixel>
internal ref struct YCbCrForwardConverter<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -53,12 +53,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <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"/>)
/// </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();
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 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>
/// 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>
/// <param name="x">The x position index in the row</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];
/// <summary>
/// The DC HUffman tables
/// The DC Huffman tables
/// </summary>
private HuffmanTables dcHuffmanTables;
/// <summary>
/// The AC HUffman tables
/// The AC Huffman tables
/// </summary>
private HuffmanTables acHuffmanTables;
@ -856,10 +856,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void ProcessStartOfScanMarker()
{
int selectorsCount = this.InputStream.ReadByte();
int componentIndex = -1;
for (int i = 0; i < selectorsCount; i++)
{
componentIndex = -1;
int componentIndex = -1;
int selector = this.InputStream.ReadByte();
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="codeLengths">The codelengths</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)
=> 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
/// </summary>
/// <returns>The <see cref="ushort"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16()
{
this.InputStream.Read(this.markerBuffer, 0, 2);
@ -936,12 +935,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image<TPixel> PostProcessIntoImage<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);
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++)
{
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.
this.buffer[(3 * i) + 7] = subsamples[i];
this.buffer[(3 * i) + 8] = chroma[i];
this.buffer[i3 + 7] = subsamples[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>
/// 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>
/// <param name="meta">The metadata.</param>
/// <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);
// 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;
for (; x < offset; x++)
{

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

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

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

@ -56,10 +56,10 @@ namespace SixLabors.ImageSharp.Formats.Png
Text = 0x74455874U,
/// <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)
/// or a single transparent color (for grayscale and true color images).
/// </summary>
PaletteAlpha = 0x74524E53U
Transparency = 0x74524E53U
}
}

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

@ -124,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
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>
/// The next chunk of data to return
/// </summary>
@ -213,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Png
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{
deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData);
}
break;
@ -222,11 +197,11 @@ namespace SixLabors.ImageSharp.Formats.Png
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
case PngChunkType.PaletteAlpha:
case PngChunkType.Transparency:
byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha);
this.AssignTransparentMarkers(alpha, pngMetaData);
break;
case PngChunkType.Text:
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>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing 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>
{
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
this.DecodeInterlacedPixelData(dataStream, image);
this.DecodeInterlacedPixelData(dataStream, image, pngMetaData);
}
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>
/// <param name="compressedStream">The compressed pixel data stream.</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>
{
while (this.currentRow < this.header.Height)
@ -555,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessDefilteredScanline(scanlineSpan, image);
this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData);
this.SwapBuffers();
this.currentRow++;
@ -569,7 +546,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</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>
{
while (true)
@ -626,7 +604,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
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();
@ -654,7 +632,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</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>
{
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
@ -674,9 +653,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.header,
scanlineSpan,
rowSpan,
this.hasTrans,
this.luminance16Trans,
this.luminanceTrans);
pngMetaData.HasTrans,
pngMetaData.TransparentGray16.GetValueOrDefault(),
pngMetaData.TransparentGray8.GetValueOrDefault());
break;
@ -702,19 +681,21 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline(
this.configuration,
this.header,
scanlineSpan,
rowSpan,
this.bytesPerPixel,
this.bytesPerSample,
this.hasTrans,
this.rgb48Trans,
this.rgb24Trans);
pngMetaData.HasTrans,
pngMetaData.TransparentRgb48.GetValueOrDefault(),
pngMetaData.TransparentRgb24.GetValueOrDefault());
break;
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline(
this.configuration,
this.header,
scanlineSpan,
rowSpan,
@ -733,9 +714,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</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="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>
{
// Trim the first marker byte from the buffer
@ -755,9 +737,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan,
pixelOffset,
increment,
this.hasTrans,
this.luminance16Trans,
this.luminanceTrans);
pngMetaData.HasTrans,
pngMetaData.TransparentGray16.GetValueOrDefault(),
pngMetaData.TransparentGray8.GetValueOrDefault());
break;
@ -794,9 +776,9 @@ namespace SixLabors.ImageSharp.Formats.Png
increment,
this.bytesPerPixel,
this.bytesPerSample,
this.hasTrans,
this.rgb48Trans,
this.rgb24Trans);
pngMetaData.HasTrans,
pngMetaData.TransparentRgb48.GetValueOrDefault(),
pngMetaData.TransparentRgb24.GetValueOrDefault());
break;
@ -820,7 +802,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Decodes and assigns marker colors that identify transparent pixels in non indexed images
/// </summary>
/// <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)
{
@ -832,16 +815,16 @@ namespace SixLabors.ImageSharp.Formats.Png
ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2));
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
this.rgb48Trans = new Rgb48(rc, gc, bc);
this.hasTrans = true;
pngMetaData.TransparentRgb48 = new Rgb48(rc, gc, bc);
pngMetaData.HasTrans = true;
return;
}
byte r = ReadByteLittleEndian(alpha, 0);
byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4);
this.rgb24Trans = new Rgb24(r, g, b);
this.hasTrans = true;
pngMetaData.TransparentRgb24 = new Rgb24(r, g, b);
pngMetaData.HasTrans = true;
}
}
else if (this.pngColorType == PngColorType.Grayscale)
@ -850,14 +833,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (this.header.BitDepth == 16)
{
this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2));
pngMetaData.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)));
}
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.
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@ -42,6 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The configuration instance for the decoding operation
/// </summary>
private Configuration configuration;
/// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks.
/// </summary>
@ -200,6 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
this.width = image.Width;
this.height = image.Height;
@ -236,7 +243,8 @@ namespace SixLabors.ImageSharp.Formats.Png
}
// 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);
bits = Math.Max(bits, quantizedBits);
@ -282,6 +290,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePaletteChunk(stream, quantized);
}
if (pngMetaData.HasTrans)
{
this.WriteTransparencyChunk(stream, pngMetaData);
}
this.WritePhysicalChunk(stream, metaData);
this.WriteGammaChunk(stream);
this.WriteExifChunk(stream, metaData);
@ -312,27 +325,27 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
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);
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan);
if (this.pngColorType.Equals(PngColorType.Grayscale))
{
// TODO: Research and add support for grayscale plus tRNS
if (this.use16Bit)
{
// 16 bit grayscale
Rgb48 rgb = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
using (IMemoryOwner<Gray16> luminanceBuffer = this.memoryAllocator.Allocate<Gray16>(rowSpan.Length))
{
Unsafe.Add(ref rowSpanRef, x).ToRgb48(ref rgb);
ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
Span<Gray16> luminanceSpan = luminanceBuffer.GetSpan();
ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan);
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
@ -340,30 +353,29 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth == 8)
{
// 8 bit grayscale
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
Unsafe.Add(ref rowSpanRef, x).ToRgb24(ref rgb);
Unsafe.Add(ref rawScanlineSpanRef, x) = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
}
PixelOperations<TPixel>.Instance.ToGray8Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
rowSpan.Length);
}
else
{
// 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);
Span<byte> tempSpan = temp.GetSpan();
ref byte tempSpanRef = ref MemoryMarshal.GetReference(tempSpan);
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
Unsafe.Add(ref rowSpanRef, x).ToRgb24(ref rgb);
float luminance = ((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)) / scaleFactor;
Unsafe.Add(ref tempSpanRef, x) = (byte)luminance;
this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth);
}
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
PixelOperations<TPixel>.Instance.ToGray8Bytes(
this.configuration,
rowSpan,
tempSpan,
rowSpan.Length);
this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
}
@ -373,23 +385,33 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.use16Bit)
{
// 16 bit grayscale + alpha
Rgba64 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 4)
// TODO: Should we consider in the future a GrayAlpha32 type.
using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
{
Unsafe.Add(ref rowSpanRef, x).ToRgba64(ref rgba);
ushort luminance = (ushort)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A);
Span<Rgba64> rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
PixelOperations<TPixel>.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
// 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
{
// 8 bit grayscale + alpha
// TODO: Should we consider in the future a GrayAlpha16 type.
Rgba32 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
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;
}
}
@ -411,29 +433,43 @@ namespace SixLabors.ImageSharp.Formats.Png
case 4:
{
// 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width);
PixelOperations<TPixel>.Instance.ToRgba32Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
this.width);
break;
}
case 3:
{
// 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
this.width);
break;
}
case 8:
{
// 16 bit Rgba
Rgba64 rgba = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
{
Unsafe.Add(ref rowSpanRef, x).ToRgba64(ref rgba);
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);
Span<Rgba64> rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
PixelOperations<TPixel>.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
// 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;
@ -442,14 +478,20 @@ namespace SixLabors.ImageSharp.Formats.Png
default:
{
// 16 bit Rgb
Rgb48 rgb = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
using (IMemoryOwner<Rgb48> rgbBuffer = this.memoryAllocator.Allocate<Rgb48>(rowSpan.Length))
{
Unsafe.Add(ref rowSpanRef, x).ToRgb48(ref rgb);
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);
Span<Rgb48> rgbSpan = rgbBuffer.GetSpan();
ref Rgb48 rgbRef = ref MemoryMarshal.GetReference(rgbSpan);
PixelOperations<TPixel>.Instance.ToRgb48(this.configuration, rowSpan, rgbSpan);
// 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;
@ -624,7 +666,6 @@ namespace SixLabors.ImageSharp.Formats.Png
TPixel[] palette = quantized.Palette;
int paletteLength = Math.Min(palette.Length, 256);
int colorTableLength = paletteLength * 3;
Rgba32 rgba = default;
bool anyAlpha = false;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
@ -634,6 +675,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
Span<byte> quantizedSpan = quantized.GetPixelSpan();
Rgba32 rgba = default;
for (int i = 0; i < paletteLength; i++)
{
if (quantizedSpan.IndexOf((byte)i) > -1)
@ -662,7 +705,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// Write the transparency data
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>
/// Writes the pixel information to the stream.
/// </summary>
@ -851,20 +939,21 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="source">The source span in 8 bits.</param>
/// <param name="result">The resultant span in <paramref name="bits"/>.</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 resultRef = ref MemoryMarshal.GetReference(result);
byte mask = (byte)(0xFF >> (8 - bits));
byte shift0 = (byte)(8 - bits);
int shift = 8 - bits;
byte mask = (byte)(0xFF >> shift);
byte shift0 = (byte)shift;
int v = 0;
int resultOffset = 0;
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;
if (shift == 0)
@ -907,4 +996,4 @@ namespace SixLabors.ImageSharp.Formats.Png
return scanlineLength / mod;
}
}
}
}

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

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
@ -24,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.BitDepth = other.BitDepth;
this.ColorType = other.ColorType;
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>
@ -42,6 +49,31 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
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/>
public IDeepCloneable DeepClone() => new PngMetaData(this);
}

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

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// 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>
internal static class PngScanlineProcessor
{
@ -19,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
bool hasTrans,
ushort luminance16Trans,
byte luminanceTrans)
Gray16 luminance16Trans,
Gray8 luminanceTrans)
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = default;
@ -32,30 +33,19 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (header.BitDepth == 16)
{
Rgb48 rgb48 = default;
for (int x = 0, o = 0; x < header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
pixel.FromGray16(new Gray16(luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
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++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
pixel.FromGray8(new Gray8(luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -72,14 +62,15 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance;
rgba64.G = 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;
}
}
else
{
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
for (int x = 0; x < header.Width; x++)
{
@ -87,9 +78,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.R = luminance;
rgba32.G = 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;
}
}
@ -102,8 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int pixelOffset,
int increment,
bool hasTrans,
ushort luminance16Trans,
byte luminanceTrans)
Gray16 luminance16Trans,
Gray8 luminanceTrans)
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = default;
@ -115,30 +106,19 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (header.BitDepth == 16)
{
Rgb48 rgb48 = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
rgb48.R = luminance;
rgb48.G = luminance;
rgb48.B = luminance;
pixel.PackFromRgb48(rgb48);
pixel.FromGray16(new Gray16(luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
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++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
pixel.PackFromRgba32(rgba32);
pixel.FromGray8(new Gray8(luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -155,14 +135,15 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance;
rgba64.G = 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;
}
}
else
{
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < header.Width; x += increment)
{
@ -170,9 +151,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.R = luminance;
rgba32.G = 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;
}
}
@ -202,26 +183,25 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = luminance;
rgba64.A = alpha;
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
Rgba32 rgba32 = default;
int bps = bytesPerSample;
for (int x = 0; x < header.Width; x++)
{
int offset = x * bytesPerPixel;
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.G = luminance;
rgba32.B = luminance;
rgba32.A = alpha;
pixel.PackFromRgba32(rgba32);
pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -253,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = luminance;
rgba64.A = alpha;
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -270,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.B = luminance;
rgba32.A = alpha;
pixel.PackFromRgba32(rgba32);
pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -303,20 +283,18 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
pixel.PackFromRgba32(rgba);
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
// TODO: We should have PackFromRgb24.
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = 0; x < header.Width; 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;
}
}
@ -350,25 +328,25 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index);
pixel.PackFromRgba32(rgba);
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
var rgba = new Rgba32(0, 0, 0, byte.MaxValue);
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, 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;
}
}
}
public static void ProcessRgbScanline<TPixel>(
Configuration configuration,
in PngHeader header,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
@ -380,7 +358,6 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (!hasTrans)
@ -394,13 +371,13 @@ namespace SixLabors.ImageSharp.Formats.Png
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
pixel.PackFromRgb48(rgb48);
pixel.FromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineSpan, rowSpan, header.Width);
PixelOperations<TPixel>.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width);
}
return;
@ -419,7 +396,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -434,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba32.Rgb = rgb24;
rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba32);
pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@ -472,7 +449,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.Rgb = rgb48;
rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue;
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
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.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
pixel.PackFromRgb48(rgb48);
pixel.FromRgb48(rgb48);
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.A = rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue;
pixel.PackFromRgba32(rgba);
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
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)
{
rgba.R = Unsafe.Add(ref scanlineSpanRef, o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample);
rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample));
rgb.R = Unsafe.Add(ref scanlineSpanRef, o);
rgb.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample);
rgb.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample));
pixel.PackFromRgba32(rgba);
pixel.FromRgb24(rgb);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
}
public static void ProcessRgbaScanline<TPixel>(
Configuration configuration,
in PngHeader header,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
@ -543,13 +521,13 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample));
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
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.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample));
pixel.PackFromRgba64(rgba64);
pixel.FromRgba64(rgba64);
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.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * bytesPerSample));
pixel.PackFromRgba32(rgba);
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}

2
src/ImageSharp/IImageInfo.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <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
/// </summary>
public interface IImageInfo

24
src/ImageSharp/Image.Decode.cs

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp
/// </content>
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>
/// By reading the header on the provided stream this calculates the images format.
/// </summary>

5
src/ImageSharp/Image.FromBytes.cs

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

24
src/ImageSharp/Image.WrapMemory.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
public static partial class Image
{
/// <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.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp
}
/// <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.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp
}
/// <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.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
/// </summary>
@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp
}
/// <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.
/// 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"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <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="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
@ -105,15 +105,15 @@ namespace SixLabors.ImageSharp
}
/// <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.
/// 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"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <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="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp
}
/// <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.
/// 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"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <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="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>

4
src/ImageSharp/ImageExtensions.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
if (format is null)
{
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)
{
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
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)
{
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.
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
@ -23,7 +21,6 @@ namespace SixLabors.ImageSharp
public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel>
{
private readonly Configuration configuration;
private bool isDisposed;
/// <summary>
@ -86,7 +83,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.configuration = configuration;
this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(width, height);
this.MetaData = metaData ?? new ImageFrameMetaData();
@ -120,7 +117,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData));
this.configuration = configuration;
this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D<TPixel>(memorySource, width, height);
this.MetaData = metaData;
@ -136,7 +133,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source));
this.configuration = configuration;
this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan());
@ -148,6 +145,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public MemoryAllocator MemoryAllocator { get; }
/// <summary>
/// Gets the <see cref="Configuration"/> instance associated with this <see cref="ImageFrame{TPixel}"/>.
/// </summary>
internal Configuration Configuration { get; }
/// <summary>
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary>
@ -250,13 +252,13 @@ namespace SixLabors.ImageSharp
}
/// <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>
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> Clone() => this.Clone(this.configuration);
internal ImageFrame<TPixel> Clone() => this.Clone(this.Configuration);
/// <summary>
/// Clones the current instance.
@ -271,7 +273,7 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="ImageFrame{TPixel2}"/></returns>
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>
/// 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());
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
ParallelHelper.IterateRows(
this.Bounds(),
configuration,
(rows, tempRowBuffer) =>
(rows) =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y);
Span<Vector4> tempRowSpan = tempRowBuffer.Span;
PixelOperations<TPixel>.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
PixelOperations<TPixel2>.Instance.PackFromScaledVector4(
tempRowSpan,
targetRow,
targetRow.Length);
PixelOperations<TPixel>.Instance.To(configuration, sourceRow, targetRow);
}
});

95
src/ImageSharp/ImageSharp.csproj

@ -5,7 +5,7 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<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>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName>
@ -31,27 +31,32 @@
<Features>IOperation</Features>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' OR '$(TargetFramework)' == 'net472' ">
<DefineConstants>$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<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" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
</ItemGroup>
<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.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
@ -72,14 +77,46 @@
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Block8x8F.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.tt">
<None Update="PixelFormats\PixelOperations{TPixel}.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>PixelOperations{TPixel}.Generated.cs</LastGenOutput>
</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>
<LastGenOutput>Rgba32.PixelOperations.Generated.cs</LastGenOutput>
</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">
<LastGenOutput>PorterDuffFunctions.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
@ -105,16 +142,56 @@
<AutoGen>True</AutoGen>
<DependentUpon>Block8x8F.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.cs">
<Compile Update="PixelFormats\PixelOperations{TPixel}.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PixelOperations{TPixel}.Generated.tt</DependentUpon>
</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>
<AutoGen>True</AutoGen>
<DependentUpon>Rgba32.PixelOperations.Generated.tt</DependentUpon>
</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">
<DesignTime>True</DesignTime>
<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">
<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);
/// <summary>
/// Clones the current image with the given configueation.
/// Clones the current image with the given configuration.
/// </summary>
/// <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>

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

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
this.Parts = ExifParts.All;
this.data = data;
this.InvalidTags = new List<ExifTag>();
this.InvalidTags = Array.Empty<ExifTag>();
}
/// <summary>
@ -63,7 +63,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.Parts = other.Parts;
this.thumbnailLength = other.thumbnailLength;
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)
{
this.values = new List<ExifValue>(other.Values.Count);
@ -289,7 +293,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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.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.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives;
@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
internal sealed class ExifReader
{
private readonly List<ExifTag> invalidTags = new List<ExifTag>();
private List<ExifTag> invalidTags;
private readonly byte[] exifData;
private int position;
private Endianness endianness = Endianness.BigEndian;
@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags ?? (IReadOnlyList<ExifTag>)Array.Empty<ExifTag>();
/// <summary>
/// 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 unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
private string ConvertToString(ReadOnlySpan<byte> buffer)
{
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
if (newIndex > int.MaxValue)
{
this.invalidTags.Add(tag);
this.AddInvalidTag(tag);
exifValue = default;
@ -349,7 +348,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.RemainingLength < size)
{
this.invalidTags.Add(tag);
this.AddInvalidTag(tag);
this.position = oldIndex;
exifValue = default;
@ -372,13 +372,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
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)
where TEnum : struct
{
var enumValue = (TEnum)(object)value;
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
if (EnumHelper<TEnum>.IsDefined(value))
{
return enumValue;
return Unsafe.As<int, TEnum>(ref value);
}
return defaultValue;
@ -547,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(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>
/// Which parts will be written.
/// </summary>
private ExifParts allowedParts;
private IList<ExifValue> values;
private readonly ExifParts allowedParts;
private readonly IList<ExifValue> values;
private List<int> dataOffsets;
private readonly List<int> ifdIndexes;
private readonly List<int> exifIndexes;

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

Loading…
Cancel
Save