Browse Source

Merge branch 'master' into af/resizekernel-optimizations

pull/781/head
Anton Firszov 7 years ago
parent
commit
2bae0c3e03
  1. 36
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  2. 8
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  3. 2
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  4. 6
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  5. 103
      src/ImageSharp/Advanced/AotCompilerTools.cs
  6. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
  7. 9
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  8. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  9. 2
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  10. 4
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  11. 9
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  12. 2
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  13. 31
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  14. 9
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  15. 10
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  16. 4
      src/ImageSharp/Formats/IImageFormat.cs
  17. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  18. 13
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  19. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  20. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  21. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  22. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  23. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  24. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  25. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  26. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  27. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  28. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
  29. 2
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  30. 7
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  31. 7
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  32. 2
      src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
  33. 2
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  34. 2
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  35. 4
      src/ImageSharp/Formats/Png/PngChunkType.cs
  36. 95
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  37. 59
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  38. 32
      src/ImageSharp/Formats/Png/PngMetaData.cs
  39. 19
      src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
  40. 2
      src/ImageSharp/IImageInfo.cs
  41. 5
      src/ImageSharp/Image.FromBytes.cs
  42. 24
      src/ImageSharp/Image.WrapMemory.cs
  43. 4
      src/ImageSharp/ImageExtensions.cs
  44. 2
      src/ImageSharp/Image{TPixel}.cs
  45. 4
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  46. 8
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs
  47. 10
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
  48. 10
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs
  49. 4
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs
  50. 42
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs
  51. 10
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs
  52. 28
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs
  53. 2
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  54. 409
      src/ImageSharp/PixelFormats/ColorConstants.cs
  55. 29
      src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
  56. 2
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  57. 2
      src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
  58. 2
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
  59. 13
      src/ImageSharp/Processing/KnownQuantizers.cs
  60. 31
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  61. 4
      src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs
  62. 16
      src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs
  63. 6
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
  64. 2
      src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
  65. 13
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
  66. 19
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  67. 52
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
  68. 110
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  69. 21
      src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs
  70. 47
      src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
  71. 48
      src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
  72. 2
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
  73. 13
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
  74. 12
      src/ImageSharp/Processing/ResizeHelper.cs
  75. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
  76. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
  77. 4
      tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
  78. 2
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  79. 18
      tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs
  80. 3
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  81. 33
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  82. 4
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  83. 2
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
  84. 2
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
  85. 67
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  86. 58
      tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs
  87. 79
      tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs
  88. 58
      tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs
  89. 3
      tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
  90. 34
      tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
  91. 3
      tests/ImageSharp.Tests/TestImages.cs
  92. 2
      tests/Images/External
  93. BIN
      tests/Images/Input/Png/gray-2-tRNS.png
  94. BIN
      tests/Images/Input/Png/gray-4-tRNS.png
  95. BIN
      tests/Images/Input/Png/gray-8-tRNS.png

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

8
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,
@ -139,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++)
{

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

@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x);
// constrain the spans to eachother
// constrain the spans to each other
if (destinationRow.Length > scanline.Length)
{
destinationRow = destinationRow.Slice(0, scanline.Length);

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

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

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

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp
/// <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 compoonent value.</param>
/// <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);

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

@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp
var bVec = new Vector<float>(256.0f / 255.0f);
var magicFloat = new Vector<float>(32768.0f);
var magicInt = new Vector<uint>(1191182336); // reinterpreded value of 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));

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

@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisable by {shouldBeDivisibleBy}!");
$"length should be divisible by {shouldBeDivisibleBy}!");
}
[Conditional("DEBUG")]
@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisable by {shouldBeDivisibleBy}!");
$"length should be divisible by {shouldBeDivisibleBy}!");
}
}
}

9
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>
@ -310,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;
}
}
}

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

31
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
{

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

@ -23,7 +23,7 @@ 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;
@ -142,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++)
{
@ -158,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
using (QuantizedFrame<TPixel> paletteQuantized
= palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration(), () => quantized.Palette).QuantizeFrame(frame))
using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@ -422,7 +421,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// 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;
int pixelCount = image.Palette.Length;

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)
{

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

@ -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>
@ -543,15 +543,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
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();
}
@ -571,7 +570,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// 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);
}

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

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

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

@ -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)
{

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

@ -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 =
{

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

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>

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

@ -22,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;

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

@ -384,7 +384,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
else
{
this.DecodeBlockProgressiveAC(
component,
ref block,
ref acHuffmanTable,
ref fastACRef);
@ -514,7 +513,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
private void DecodeBlockProgressiveAC(
JpegComponent component,
ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
@ -757,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
{
@ -914,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;

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>

7
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++)

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

95
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;
@ -708,9 +687,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan,
this.bytesPerPixel,
this.bytesPerSample,
this.hasTrans,
this.rgb48Trans,
this.rgb24Trans);
pngMetaData.HasTrans,
pngMetaData.TransparentRgb48.GetValueOrDefault(),
pngMetaData.TransparentRgb24.GetValueOrDefault());
break;
@ -735,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
@ -757,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;
@ -796,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;
@ -822,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)
{
@ -834,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)
@ -852,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;
}
}
}

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

@ -290,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);
@ -326,7 +331,6 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.pngColorType.Equals(PngColorType.Grayscale))
{
// TODO: Research and add support for grayscale plus tRNS
if (this.use16Bit)
{
// 16 bit grayscale
@ -701,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);
}
}
}
@ -749,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>
@ -896,9 +945,9 @@ namespace SixLabors.ImageSharp.Formats.Png
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;
@ -947,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);
}

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

@ -20,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;
@ -62,7 +62,7 @@ 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.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor);
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
for (int x = 0; x < header.Width; x++)
{
@ -93,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;
@ -135,7 +135,7 @@ 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.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor);
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
for (int x = pixelOffset; x < header.Width; x += increment)
{
@ -190,12 +190,11 @@ namespace SixLabors.ImageSharp.Formats.Png
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;

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

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

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>

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

@ -567,8 +567,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsDefined(int value)
{
return Array.BinarySearch(Values, 0, Values.Length, value) > 0;
return Array.BinarySearch(Values, value) >= 0;
}
}
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs

@ -41,10 +41,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
public IccResponseCurve ReadResponseCurve(int channelCount)
{
var type = (IccCurveMeasurementEncodings)this.ReadUInt32();
uint[] measurment = new uint[channelCount];
uint[] measurement = new uint[channelCount];
for (int i = 0; i < channelCount; i++)
{
measurment[i] = this.ReadUInt32();
measurement[i] = this.ReadUInt32();
}
Vector3[] xyzValues = new Vector3[channelCount];
@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
IccResponseNumber[][] response = new IccResponseNumber[channelCount][];
for (int i = 0; i < channelCount; i++)
{
response[i] = new IccResponseNumber[measurment[i]];
for (uint j = 0; j < measurment[i]; j++)
response[i] = new IccResponseNumber[measurement[i]];
for (uint j = 0; j < measurement[i]; j++)
{
response[i][j] = this.ReadResponseNumber();
}

10
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs

@ -628,16 +628,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
int start = this.currentIndex - 8; // 8 is the tag header size
ushort channelCount = this.ReadUInt16();
ushort measurmentCount = this.ReadUInt16();
ushort measurementCount = this.ReadUInt16();
uint[] offset = new uint[measurmentCount];
for (int i = 0; i < measurmentCount; i++)
uint[] offset = new uint[measurementCount];
for (int i = 0; i < measurementCount; i++)
{
offset[i] = this.ReadUInt32();
}
var curves = new IccResponseCurve[measurmentCount];
for (int i = 0; i < measurmentCount; i++)
var curves = new IccResponseCurve[measurementCount];
for (int i = 0; i < measurementCount; i++)
{
this.currentIndex = (int)(start + offset[i]);
curves[i] = this.ReadResponseCurve(channelCount);

10
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs

@ -39,15 +39,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary>
/// This profile provides the relevant information to perform a transformation
/// between colour encodings and the PCS. This type of profile is based on
/// modelling rather than device measurement or characterization data.
/// between color encodings and the PCS. This type of profile is based on
/// modeling rather than device measurement or characterization data.
/// ColorSpace profiles may be embedded in images.
/// </summary>
ColorSpace = 0x73706163, // spac
/// <summary>
/// This profile represents abstract transforms and does not represent any
/// device model. Colour transformations using Abstract profiles are performed
/// device model. Color transformations using Abstract profiles are performed
/// from PCS to PCS. Abstract profiles cannot be embedded in images.
/// </summary>
Abstract = 0x61627374, // abst
@ -55,8 +55,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary>
/// NamedColor profiles can be thought of as sibling profiles to device profiles.
/// For a given device there would be one or more device profiles to handle
/// process colour conversions and one or more named colour profiles to handle
/// named colours.
/// process color conversions and one or more named color profiles to handle
/// named colors.
/// </summary>
NamedColor = 0x6E6D636C, // nmcl
}

4
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs

@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
NotEmbedded = 0,
/// <summary>
/// Profile cannot be used independently of the embedded colour data
/// Profile cannot be used independently of the embedded color data
/// </summary>
NotIndependent = 1 << 1,
/// <summary>
/// Profile can be used independently of the embedded colour data
/// Profile can be used independently of the embedded color data
/// </summary>
Independent = 0,
}

42
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs

@ -19,18 +19,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
Unknown,
/// <summary>
/// A2B0 - This tag defines a colour transform from Device, Colour Encoding or PCS, to PCS, or a colour transform
/// A2B0 - This tag defines a color transform from Device, Color Encoding or PCS, to PCS, or a color transform
/// from Device 1 to Device 2, using lookup table tag element structures
/// </summary>
AToB0 = 0x41324230,
/// <summary>
/// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures
/// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures
/// </summary>
AToB1 = 0x41324231,
/// <summary>
/// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures
/// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures
/// </summary>
AToB2 = 0x41324232,
@ -46,40 +46,40 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
BlueTrc = 0x62545243,
/// <summary>
/// B2A0 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures
/// B2A0 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures
/// </summary>
BToA0 = 0x42324130,
/// <summary>
/// B2A1 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures.
/// B2A1 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures.
/// </summary>
BToA1 = 0x42324131,
/// <summary>
/// B2A2 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures.
/// B2A2 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures.
/// </summary>
BToA2 = 0x42324132,
/// <summary>
/// B2D0 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D0 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA0 tag.
/// </summary>
BToD0 = 0x42324430,
/// <summary>
/// B2D1 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D1 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA1 tag.
/// </summary>
BToD1 = 0x42324431,
/// <summary>
/// B2D2 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D2 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA2 tag.
/// </summary>
BToD2 = 0x42324432,
/// <summary>
/// B2D3 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D3 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA1 tag.
/// </summary>
BToD3 = 0x42324433,
@ -97,8 +97,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
CharTarget = 0x74617267,
/// <summary>
/// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ colour, measured using the actual illumination
/// conditions and relative to the actual adopted white, to an nCIEXYZ colour relative to the PCS adopted white
/// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ color, measured using the actual illumination
/// conditions and relative to the actual adopted white, to an nCIEXYZ color relative to the PCS adopted white
/// </summary>
ChromaticAdaptation = 0x63686164,
@ -166,33 +166,33 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DeviceSettings = 0x64657673,
/// <summary>
/// D2B0 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B0 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB0 tag
/// </summary>
DToB0 = 0x44324230,
/// <summary>
/// D2B1 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B1 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB1 = 0x44324230,
/// <summary>
/// D2B2 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B2 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB2 = 0x44324230,
/// <summary>
/// D2B3 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B3 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB3 = 0x44324230,
/// <summary>
/// gamt - This tag provides a table in which PCS values are the input and a single
/// output value for each input value is the output. If the output value is 0, the PCS colour is in-gamut.
/// If the output is non-zero, the PCS colour is out-of-gamut
/// output value for each input value is the output. If the output value is 0, the PCS color is in-gamut.
/// If the output is non-zero, the PCS color is out-of-gamut
/// </summary>
Gamut = 0x67616D74,
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
GreenTrc = 0x67545243,
/// <summary>
/// lumi - This tag contains the absolute luminance of emissive devices in candelas per square metre as described by the Y channel.
/// lumi - This tag contains the absolute luminance of emissive devices in candelas per square meter as described by the Y channel.
/// </summary>
Luminance = 0x6C756d69,
@ -240,8 +240,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
NamedColor = 0x6E636f6C,
/// <summary>
/// ncl2 - This tag contains the named colour information providing a PCS and optional device representation
/// for a list of named colours.
/// ncl2 - This tag contains the named color information providing a PCS and optional device representation
/// for a list of named colors.
/// </summary>
NamedColor2 = 0x6E636C32,

10
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs

@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
/// <summary>
/// In perceptual transforms the PCS values represent hypothetical
/// measurements of a colour reproduction on the reference reflective
/// measurements of a color reproduction on the reference reflective
/// medium. By extension, for the perceptual intent, the PCS represents
/// the appearance of that reproduction as viewed in the reference viewing
/// environment by a human observer adapted to that environment. The exact
/// colour rendering of the perceptual intent is vendor specific.
/// color rendering of the perceptual intent is vendor specific.
/// </summary>
Perceptual = 0,
@ -27,15 +27,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MediaRelativeColorimetric = 1,
/// <summary>
/// The exact colour rendering of the saturation intent is vendor
/// The exact color rendering of the saturation intent is vendor
/// specific and involves compromises such as trading off
/// preservation of hue in order to preserve the vividness of pure colours.
/// preservation of hue in order to preserve the vividness of pure colors.
/// </summary>
Saturation = 2,
/// <summary>
/// Transformations for this intent shall leave the chromatically
/// adapted nCIEXYZ tristimulus values of the in-gamut colours unchanged.
/// adapted nCIEXYZ tristimulus values of the in-gamut colors unchanged.
/// </summary>
AbsoluteColorimetric = 3,
}

28
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// This is an optional tag which specifies the laydown order in which colorants
/// will be printed on an n-colorant device. The laydown order may be the same
/// as the channel generation order listed in the colorantTableTag or the channel
/// order of a colour encoding type such as CMYK, in which case this tag is not
/// order of a color encoding type such as CMYK, in which case this tag is not
/// needed. When this is not the case (for example, ink-towers sometimes use
/// the order KCMY), this tag may be used to specify the laydown order of the
/// colorants
@ -59,25 +59,25 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DateTime = 0x6474696D,
/// <summary>
/// This structure represents a colour transform using tables with 16-bit
/// This structure represents a color transform using tables with 16-bit
/// precision. This type contains four processing elements: a 3 × 3 matrix
/// (which shall be the identity matrix unless the input colour space is
/// (which shall be the identity matrix unless the input color space is
/// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables
/// </summary>
Lut16 = 0x6D667432,
/// <summary>
/// This structure represents a colour transform using tables of 8-bit
/// This structure represents a color transform using tables of 8-bit
/// precision. This type contains four processing elements: a 3 × 3 matrix
/// (which shall be the identity matrix unless the input colour space is
/// (which shall be the identity matrix unless the input color space is
/// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables.
/// </summary>
Lut8 = 0x6D667431,
/// <summary>
/// This structure represents a colour transform. The type contains up
/// This structure represents a color transform. The type contains up
/// to five processing elements which are stored in the AToBTag tag
/// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
LutAToB = 0x6D414220,
/// <summary>
/// This structure represents a colour transform. The type contains
/// This structure represents a color transform. The type contains
/// up to five processing elements which are stored in the BToATag
/// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiLocalizedUnicode = 0x6D6C7563,
/// <summary>
/// This structure represents a colour transform, containing a sequence
/// This structure represents a color transform, containing a sequence
/// of processing elements. The processing elements contained in the
/// structure are defined in the structure itself, allowing for a flexible
/// structure. Currently supported processing elements are: a set of one
@ -123,15 +123,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiProcessElements = 0x6D706574,
/// <summary>
/// This type is a count value and array of structures that provide colour
/// coordinates for colour names. For each named colour, a PCS and optional
/// device representation of the colour are given. Both representations are
/// This type is a count value and array of structures that provide color
/// coordinates for color names. For each named color, a PCS and optional
/// device representation of the color are given. Both representations are
/// 16-bit values and PCS values shall be relative colorimetric. The device
/// representation corresponds to the header’s "data colour space" field.
/// representation corresponds to the header’s "data color space" field.
/// This representation should be consistent with the "number of device
/// coordinates" field in the namedColor2Type. If this field is 0, device
/// coordinates are not provided. The PCS representation corresponds to the
/// header's PCS field. The PCS representation is always provided. Colour
/// header's PCS field. The PCS representation is always provided. Color
/// names are fixed-length, 32-byte fields including null termination. In
/// order to maintain maximum portability, it is strongly recommended that
/// special characters of the 7-bit ASCII set not be used.
@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// so that corrections can be made for variation in the device without
/// having to produce a new profile. The mechanism can be used by applications
/// to allow users with relatively inexpensive and readily available
/// instrumentation to apply corrections to individual output colour
/// instrumentation to apply corrections to individual output color
/// channels in order to achieve consistent results.
/// </summary>
ResponseCurveSet16 = 0x72637332,

2
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA heax string.</returns>
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA hex string.</returns>
public static TPixel FromHex(string hex)
{
Guard.NotNullOrWhiteSpace(hex, nameof(hex));

409
src/ImageSharp/PixelFormats/ColorConstants.cs

@ -11,157 +11,268 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
/// </summary>
public static readonly Rgba32[] WebSafeColors = GetWebSafeColors();
public static readonly Rgba32[] WebSafeColors =
{
Rgba32.AliceBlue,
Rgba32.AntiqueWhite,
Rgba32.Aqua,
Rgba32.Aquamarine,
Rgba32.Azure,
Rgba32.Beige,
Rgba32.Bisque,
Rgba32.Black,
Rgba32.BlanchedAlmond,
Rgba32.Blue,
Rgba32.BlueViolet,
Rgba32.Brown,
Rgba32.BurlyWood,
Rgba32.CadetBlue,
Rgba32.Chartreuse,
Rgba32.Chocolate,
Rgba32.Coral,
Rgba32.CornflowerBlue,
Rgba32.Cornsilk,
Rgba32.Crimson,
Rgba32.Cyan,
Rgba32.DarkBlue,
Rgba32.DarkCyan,
Rgba32.DarkGoldenrod,
Rgba32.DarkGray,
Rgba32.DarkGreen,
Rgba32.DarkKhaki,
Rgba32.DarkMagenta,
Rgba32.DarkOliveGreen,
Rgba32.DarkOrange,
Rgba32.DarkOrchid,
Rgba32.DarkRed,
Rgba32.DarkSalmon,
Rgba32.DarkSeaGreen,
Rgba32.DarkSlateBlue,
Rgba32.DarkSlateGray,
Rgba32.DarkTurquoise,
Rgba32.DarkViolet,
Rgba32.DeepPink,
Rgba32.DeepSkyBlue,
Rgba32.DimGray,
Rgba32.DodgerBlue,
Rgba32.Firebrick,
Rgba32.FloralWhite,
Rgba32.ForestGreen,
Rgba32.Fuchsia,
Rgba32.Gainsboro,
Rgba32.GhostWhite,
Rgba32.Gold,
Rgba32.Goldenrod,
Rgba32.Gray,
Rgba32.Green,
Rgba32.GreenYellow,
Rgba32.Honeydew,
Rgba32.HotPink,
Rgba32.IndianRed,
Rgba32.Indigo,
Rgba32.Ivory,
Rgba32.Khaki,
Rgba32.Lavender,
Rgba32.LavenderBlush,
Rgba32.LawnGreen,
Rgba32.LemonChiffon,
Rgba32.LightBlue,
Rgba32.LightCoral,
Rgba32.LightCyan,
Rgba32.LightGoldenrodYellow,
Rgba32.LightGray,
Rgba32.LightGreen,
Rgba32.LightPink,
Rgba32.LightSalmon,
Rgba32.LightSeaGreen,
Rgba32.LightSkyBlue,
Rgba32.LightSlateGray,
Rgba32.LightSteelBlue,
Rgba32.LightYellow,
Rgba32.Lime,
Rgba32.LimeGreen,
Rgba32.Linen,
Rgba32.Magenta,
Rgba32.Maroon,
Rgba32.MediumAquamarine,
Rgba32.MediumBlue,
Rgba32.MediumOrchid,
Rgba32.MediumPurple,
Rgba32.MediumSeaGreen,
Rgba32.MediumSlateBlue,
Rgba32.MediumSpringGreen,
Rgba32.MediumTurquoise,
Rgba32.MediumVioletRed,
Rgba32.MidnightBlue,
Rgba32.MintCream,
Rgba32.MistyRose,
Rgba32.Moccasin,
Rgba32.NavajoWhite,
Rgba32.Navy,
Rgba32.OldLace,
Rgba32.Olive,
Rgba32.OliveDrab,
Rgba32.Orange,
Rgba32.OrangeRed,
Rgba32.Orchid,
Rgba32.PaleGoldenrod,
Rgba32.PaleGreen,
Rgba32.PaleTurquoise,
Rgba32.PaleVioletRed,
Rgba32.PapayaWhip,
Rgba32.PeachPuff,
Rgba32.Peru,
Rgba32.Pink,
Rgba32.Plum,
Rgba32.PowderBlue,
Rgba32.Purple,
Rgba32.RebeccaPurple,
Rgba32.Red,
Rgba32.RosyBrown,
Rgba32.RoyalBlue,
Rgba32.SaddleBrown,
Rgba32.Salmon,
Rgba32.SandyBrown,
Rgba32.SeaGreen,
Rgba32.SeaShell,
Rgba32.Sienna,
Rgba32.Silver,
Rgba32.SkyBlue,
Rgba32.SlateBlue,
Rgba32.SlateGray,
Rgba32.Snow,
Rgba32.SpringGreen,
Rgba32.SteelBlue,
Rgba32.Tan,
Rgba32.Teal,
Rgba32.Thistle,
Rgba32.Tomato,
Rgba32.Transparent,
Rgba32.Turquoise,
Rgba32.Violet,
Rgba32.Wheat,
Rgba32.White,
Rgba32.WhiteSmoke,
Rgba32.Yellow,
Rgba32.YellowGreen
};
/// <summary>
/// Returns an array of web safe colors.
/// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
/// <returns>The <see cref="T:Color[]"/></returns>
private static Rgba32[] GetWebSafeColors()
=> new Rgba32[]
{
Rgba32.AliceBlue,
Rgba32.AntiqueWhite,
Rgba32.Aqua,
Rgba32.Aquamarine,
Rgba32.Azure,
Rgba32.Beige,
Rgba32.Bisque,
Rgba32.Black,
Rgba32.BlanchedAlmond,
Rgba32.Blue,
Rgba32.BlueViolet,
Rgba32.Brown,
Rgba32.BurlyWood,
Rgba32.CadetBlue,
Rgba32.Chartreuse,
Rgba32.Chocolate,
Rgba32.Coral,
Rgba32.CornflowerBlue,
Rgba32.Cornsilk,
Rgba32.Crimson,
Rgba32.Cyan,
Rgba32.DarkBlue,
Rgba32.DarkCyan,
Rgba32.DarkGoldenrod,
Rgba32.DarkGray,
Rgba32.DarkGreen,
Rgba32.DarkKhaki,
Rgba32.DarkMagenta,
Rgba32.DarkOliveGreen,
Rgba32.DarkOrange,
Rgba32.DarkOrchid,
Rgba32.DarkRed,
Rgba32.DarkSalmon,
Rgba32.DarkSeaGreen,
Rgba32.DarkSlateBlue,
Rgba32.DarkSlateGray,
Rgba32.DarkTurquoise,
Rgba32.DarkViolet,
Rgba32.DeepPink,
Rgba32.DeepSkyBlue,
Rgba32.DimGray,
Rgba32.DodgerBlue,
Rgba32.Firebrick,
Rgba32.FloralWhite,
Rgba32.ForestGreen,
Rgba32.Fuchsia,
Rgba32.Gainsboro,
Rgba32.GhostWhite,
Rgba32.Gold,
Rgba32.Goldenrod,
Rgba32.Gray,
Rgba32.Green,
Rgba32.GreenYellow,
Rgba32.Honeydew,
Rgba32.HotPink,
Rgba32.IndianRed,
Rgba32.Indigo,
Rgba32.Ivory,
Rgba32.Khaki,
Rgba32.Lavender,
Rgba32.LavenderBlush,
Rgba32.LawnGreen,
Rgba32.LemonChiffon,
Rgba32.LightBlue,
Rgba32.LightCoral,
Rgba32.LightCyan,
Rgba32.LightGoldenrodYellow,
Rgba32.LightGray,
Rgba32.LightGreen,
Rgba32.LightPink,
Rgba32.LightSalmon,
Rgba32.LightSeaGreen,
Rgba32.LightSkyBlue,
Rgba32.LightSlateGray,
Rgba32.LightSteelBlue,
Rgba32.LightYellow,
Rgba32.Lime,
Rgba32.LimeGreen,
Rgba32.Linen,
Rgba32.Magenta,
Rgba32.Maroon,
Rgba32.MediumAquamarine,
Rgba32.MediumBlue,
Rgba32.MediumOrchid,
Rgba32.MediumPurple,
Rgba32.MediumSeaGreen,
Rgba32.MediumSlateBlue,
Rgba32.MediumSpringGreen,
Rgba32.MediumTurquoise,
Rgba32.MediumVioletRed,
Rgba32.MidnightBlue,
Rgba32.MintCream,
Rgba32.MistyRose,
Rgba32.Moccasin,
Rgba32.NavajoWhite,
Rgba32.Navy,
Rgba32.OldLace,
Rgba32.Olive,
Rgba32.OliveDrab,
Rgba32.Orange,
Rgba32.OrangeRed,
Rgba32.Orchid,
Rgba32.PaleGoldenrod,
Rgba32.PaleGreen,
Rgba32.PaleTurquoise,
Rgba32.PaleVioletRed,
Rgba32.PapayaWhip,
Rgba32.PeachPuff,
Rgba32.Peru,
Rgba32.Pink,
Rgba32.Plum,
Rgba32.PowderBlue,
Rgba32.Purple,
Rgba32.RebeccaPurple,
Rgba32.Red,
Rgba32.RosyBrown,
Rgba32.RoyalBlue,
Rgba32.SaddleBrown,
Rgba32.Salmon,
Rgba32.SandyBrown,
Rgba32.SeaGreen,
Rgba32.SeaShell,
Rgba32.Sienna,
Rgba32.Silver,
Rgba32.SkyBlue,
Rgba32.SlateBlue,
Rgba32.SlateGray,
Rgba32.Snow,
Rgba32.SpringGreen,
Rgba32.SteelBlue,
Rgba32.Tan,
Rgba32.Teal,
Rgba32.Thistle,
Rgba32.Tomato,
Rgba32.Transparent,
Rgba32.Turquoise,
Rgba32.Violet,
Rgba32.Wheat,
Rgba32.White,
Rgba32.WhiteSmoke,
Rgba32.Yellow,
Rgba32.YellowGreen
};
public static readonly Rgba32[] WernerColors =
{
Rgba32.FromHex("#f1e9cd"),
Rgba32.FromHex("#f2e7cf"),
Rgba32.FromHex("#ece6d0"),
Rgba32.FromHex("#f2eacc"),
Rgba32.FromHex("#f3e9ca"),
Rgba32.FromHex("#f2ebcd"),
Rgba32.FromHex("#e6e1c9"),
Rgba32.FromHex("#e2ddc6"),
Rgba32.FromHex("#cbc8b7"),
Rgba32.FromHex("#bfbbb0"),
Rgba32.FromHex("#bebeb3"),
Rgba32.FromHex("#b7b5ac"),
Rgba32.FromHex("#bab191"),
Rgba32.FromHex("#9c9d9a"),
Rgba32.FromHex("#8a8d84"),
Rgba32.FromHex("#5b5c61"),
Rgba32.FromHex("#555152"),
Rgba32.FromHex("#413f44"),
Rgba32.FromHex("#454445"),
Rgba32.FromHex("#423937"),
Rgba32.FromHex("#433635"),
Rgba32.FromHex("#252024"),
Rgba32.FromHex("#241f20"),
Rgba32.FromHex("#281f3f"),
Rgba32.FromHex("#1c1949"),
Rgba32.FromHex("#4f638d"),
Rgba32.FromHex("#383867"),
Rgba32.FromHex("#5c6b8f"),
Rgba32.FromHex("#657abb"),
Rgba32.FromHex("#6f88af"),
Rgba32.FromHex("#7994b5"),
Rgba32.FromHex("#6fb5a8"),
Rgba32.FromHex("#719ba2"),
Rgba32.FromHex("#8aa1a6"),
Rgba32.FromHex("#d0d5d3"),
Rgba32.FromHex("#8590ae"),
Rgba32.FromHex("#3a2f52"),
Rgba32.FromHex("#39334a"),
Rgba32.FromHex("#6c6d94"),
Rgba32.FromHex("#584c77"),
Rgba32.FromHex("#533552"),
Rgba32.FromHex("#463759"),
Rgba32.FromHex("#bfbac0"),
Rgba32.FromHex("#77747f"),
Rgba32.FromHex("#4a475c"),
Rgba32.FromHex("#b8bfaf"),
Rgba32.FromHex("#b2b599"),
Rgba32.FromHex("#979c84"),
Rgba32.FromHex("#5d6161"),
Rgba32.FromHex("#61ac86"),
Rgba32.FromHex("#a4b6a7"),
Rgba32.FromHex("#adba98"),
Rgba32.FromHex("#93b778"),
Rgba32.FromHex("#7d8c55"),
Rgba32.FromHex("#33431e"),
Rgba32.FromHex("#7c8635"),
Rgba32.FromHex("#8e9849"),
Rgba32.FromHex("#c2c190"),
Rgba32.FromHex("#67765b"),
Rgba32.FromHex("#ab924b"),
Rgba32.FromHex("#c8c76f"),
Rgba32.FromHex("#ccc050"),
Rgba32.FromHex("#ebdd99"),
Rgba32.FromHex("#ab9649"),
Rgba32.FromHex("#dbc364"),
Rgba32.FromHex("#e6d058"),
Rgba32.FromHex("#ead665"),
Rgba32.FromHex("#d09b2c"),
Rgba32.FromHex("#a36629"),
Rgba32.FromHex("#a77d35"),
Rgba32.FromHex("#f0d696"),
Rgba32.FromHex("#d7c485"),
Rgba32.FromHex("#f1d28c"),
Rgba32.FromHex("#efcc83"),
Rgba32.FromHex("#f3daa7"),
Rgba32.FromHex("#dfa837"),
Rgba32.FromHex("#ebbc71"),
Rgba32.FromHex("#d17c3f"),
Rgba32.FromHex("#92462f"),
Rgba32.FromHex("#be7249"),
Rgba32.FromHex("#bb603c"),
Rgba32.FromHex("#c76b4a"),
Rgba32.FromHex("#a75536"),
Rgba32.FromHex("#b63e36"),
Rgba32.FromHex("#b5493a"),
Rgba32.FromHex("#cd6d57"),
Rgba32.FromHex("#711518"),
Rgba32.FromHex("#e9c49d"),
Rgba32.FromHex("#eedac3"),
Rgba32.FromHex("#eecfbf"),
Rgba32.FromHex("#ce536b"),
Rgba32.FromHex("#b74a70"),
Rgba32.FromHex("#b7757c"),
Rgba32.FromHex("#612741"),
Rgba32.FromHex("#7a4848"),
Rgba32.FromHex("#3f3033"),
Rgba32.FromHex("#8d746f"),
Rgba32.FromHex("#4d3635"),
Rgba32.FromHex("#6e3b31"),
Rgba32.FromHex("#864735"),
Rgba32.FromHex("#553d3a"),
Rgba32.FromHex("#613936"),
Rgba32.FromHex("#7a4b3a"),
Rgba32.FromHex("#946943"),
Rgba32.FromHex("#c39e6d"),
Rgba32.FromHex("#513e32"),
Rgba32.FromHex("#8b7859"),
Rgba32.FromHex("#9b856b"),
Rgba32.FromHex("#766051"),
Rgba32.FromHex("#453b32")
};
}
}

29
src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs

@ -14,9 +14,10 @@ namespace SixLabors.ImageSharp.PixelFormats
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Thread-safe backing field for <see cref="WebSafePalette"/>.
/// Thread-safe backing field for the constant palettes.
/// </summary>
private static readonly Lazy<TPixel[]> WebSafePaletteLazy = new Lazy<TPixel[]>(GetWebSafePalette, true);
private static readonly Lazy<TPixel[]> WernerPaletteLazy = new Lazy<TPixel[]>(GetWernerPalette, true);
/// <summary>
/// Represents a <see paramref="TPixel"/> matching the W3C definition that has an hex value of #F0F8FF.
@ -729,22 +730,32 @@ namespace SixLabors.ImageSharp.PixelFormats
public static readonly TPixel YellowGreen = ColorBuilder<TPixel>.FromRGBA(154, 205, 50, 255);
/// <summary>
/// Gets a <see cref="T:TPixel[]"/> matching the W3C definition of web safe colors.
/// Gets a <see cref="T:TPixel[]"/> collection of web safe, colors as defined in the CSS Color Module Level 4.
/// </summary>
public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value;
private static TPixel[] GetWebSafePalette()
/// <summary>
/// Gets a <see cref="T:TPixel[]"/> collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public static TPixel[] WernerPalette => WernerPaletteLazy.Value;
private static TPixel[] GetWebSafePalette() => GetPalette(ColorConstants.WebSafeColors);
private static TPixel[] GetWernerPalette() => GetPalette(ColorConstants.WernerColors);
private static TPixel[] GetPalette(Rgba32[] palette)
{
Rgba32[] constants = ColorConstants.WebSafeColors;
var safe = new TPixel[constants.Length + 1];
var converted = new TPixel[palette.Length];
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(constants.AsSpan());
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(palette.AsSpan());
PixelOperations<TPixel>.Instance.FromRgba32Bytes(
Configuration.Default,
constantsBytes,
safe,
constants.Length);
return safe;
converted,
palette.Length);
return converted;
}
}
}

2
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
// Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the
// standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 alogrithm.
// standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm.
// One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and
// packs/unpacks the pixel without and conversion so we employ custom methods do do this.
if (typeof(TDestinationPixel) == typeof(Gray16))

2
src/ImageSharp/PixelFormats/Utils/PixelConverter.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// packedRgba = [aa bb gg rr]
// tmp1 = [aa 00 gg 00]
// tmp2 = [00 bb 00 rr]
// tmp3=ROTL(16, tmp2) = [00 rr 00 bb]
// tmp3=ROTL(16, tmp2) = [00 rr 00 bb]
// tmp1 + tmp3 = [aa rr gg bb]
uint tmp1 = packedRgba & 0xFF00FF00;
uint tmp2 = packedRgba & 0x00FF00FF;

2
src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
// 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
// 'destVectors' and 'lastQuarterOfDestBuffer' are overlapping buffers,
// but we are always reading/writing at different positions:
SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer),

13
src/ImageSharp/Processing/KnownQuantizers.cs

@ -12,20 +12,23 @@ namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Gets the adaptive Octree quantizer. Fast with good quality.
/// The quantizer only supports a single alpha value.
/// </summary>
public static IQuantizer Octree { get; } = new OctreeQuantizer();
/// <summary>
/// Gets the Xiaolin Wu's Color Quantizer which generates high quality output.
/// The quantizer supports multiple alpha values.
/// </summary>
public static IQuantizer Wu { get; } = new WuQuantizer();
/// <summary>
/// Gets the palette based, Using the collection of web-safe colors.
/// The quantizer supports multiple alpha values.
/// Gets the palette based quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
/// </summary>
public static IQuantizer Palette { get; } = new PaletteQuantizer();
public static IQuantizer WebSafe { get; } = new WebSafePaletteQuantizer();
/// <summary>
/// Gets the palette based quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public static IQuantizer Werner { get; } = new WernerPaletteQuantizer();
}
}

31
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -66,36 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
? new DenseMatrix<float>(size, 1)
: new DenseMatrix<float>(1, size);
float sum = 0F;
for (int i = 0; i < size; i++)
{
float x = 1;
sum += x;
if (horizontal)
{
kernel[0, i] = x;
}
else
{
kernel[i, 0] = x;
}
}
// Normalize kernel so that the sum of all weights equals 1
if (horizontal)
{
for (int i = 0; i < size; i++)
{
kernel[0, i] = kernel[0, i] / sum;
}
}
else
{
for (int i = 0; i < size; i++)
{
kernel[i, 0] = kernel[i, 0] / sum;
}
}
kernel.Fill(1.0F / size);
return kernel;
}

4
src/ImageSharp/Processing/Processors/Convolution/KirshKernels.cs → src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs

@ -6,9 +6,9 @@ using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Contains the eight matrices used for Kirsh edge detection
/// Contains the eight matrices used for Kirsch edge detection
/// </summary>
internal static class KirshKernels
internal static class KirschKernels
{
/// <summary>
/// Gets the North gradient operator

16
src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs

@ -23,27 +23,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc/>
public override DenseMatrix<float> North => KirshKernels.KirschNorth;
public override DenseMatrix<float> North => KirschKernels.KirschNorth;
/// <inheritdoc/>
public override DenseMatrix<float> NorthWest => KirshKernels.KirschNorthWest;
public override DenseMatrix<float> NorthWest => KirschKernels.KirschNorthWest;
/// <inheritdoc/>
public override DenseMatrix<float> West => KirshKernels.KirschWest;
public override DenseMatrix<float> West => KirschKernels.KirschWest;
/// <inheritdoc/>
public override DenseMatrix<float> SouthWest => KirshKernels.KirschSouthWest;
public override DenseMatrix<float> SouthWest => KirschKernels.KirschSouthWest;
/// <inheritdoc/>
public override DenseMatrix<float> South => KirshKernels.KirschSouth;
public override DenseMatrix<float> South => KirschKernels.KirschSouth;
/// <inheritdoc/>
public override DenseMatrix<float> SouthEast => KirshKernels.KirschSouthEast;
public override DenseMatrix<float> SouthEast => KirschKernels.KirschSouthEast;
/// <inheritdoc/>
public override DenseMatrix<float> East => KirshKernels.KirschEast;
public override DenseMatrix<float> East => KirschKernels.KirschEast;
/// <inheritdoc/>
public override DenseMatrix<float> NorthEast => KirshKernels.KirschNorthEast;
public override DenseMatrix<float> NorthEast => KirschKernels.KirschNorthEast;
}
}

6
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs

@ -139,8 +139,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
protected byte GetTransparentIndex()
{
// Transparent pixels are much more likely to be found at the end of a palette.
int index = this.paletteVector.Length - 1;
for (int i = this.paletteVector.Length - 1; i >= 0; i--)
int paletteVectorLengthMinus1 = this.paletteVector.Length - 1;
int index = paletteVectorLengthMinus1;
for (int i = paletteVectorLengthMinus1; i >= 0; i--)
{
ref Vector4 candidate = ref this.paletteVector[i];
if (candidate.Equals(default))

2
src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs

@ -136,6 +136,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
internal TPixel[] AotGetPalette() => this.GetPalette();
/// <inheritdoc/>
protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors);

13
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs

@ -15,11 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class OctreeQuantizer : IQuantizer
{
/// <summary>
/// The default maximum number of colors to use when quantizing the image.
/// </summary>
public const int DefaultMaxColors = 256;
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
@ -42,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public OctreeQuantizer(bool dither)
: this(GetDiffuser(dither), DefaultMaxColors)
: this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@ -51,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public OctreeQuantizer(IErrorDiffuser diffuser)
: this(diffuser, DefaultMaxColors)
: this(diffuser, QuantizerConstants.MaxColors)
{
}
@ -63,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
/// <inheritdoc />
@ -84,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
maxColors = maxColors.Clamp(1, DefaultMaxColors);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new OctreeFrameQuantizer<TPixel>(this, maxColors);
}

19
src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -23,27 +22,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
private readonly TPixel[] palette;
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private readonly Vector4[] paletteVector;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="quantizer">The palette quantizer.</param>
/// <param name="colors">An array of all colors in the palette.</param>
public PaletteFrameQuantizer(Configuration configuration, PaletteQuantizer quantizer, TPixel[] colors)
: base(quantizer, true)
{
// TODO: Why is this value constrained? Gif has limitations but theoretically
// we might want to reduce the palette of an image to greater than that limitation.
Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 256, nameof(colors));
this.palette = colors;
this.paletteVector = new Vector4[this.palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(configuration, this.palette, this.paletteVector);
}
public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors)
: base(quantizer, true) => this.palette = colors;
/// <inheritdoc/>
protected override void SecondPass(

52
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs

@ -8,18 +8,18 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Allows the quantization of images pixels using web safe colors defined in the CSS Color Module Level 4.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/> Override this class to provide your own palette.
/// Allows the quantization of images pixels using color palettes.
/// Override this class to provide your own palette.
/// <para>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering and the <see cref="NamedColors{TPixel}.WebSafePalette"/>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering.
/// </para>
/// </summary>
public class PaletteQuantizer : IQuantizer
public abstract class PaletteQuantizer : IQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
public PaletteQuantizer()
protected PaletteQuantizer()
: this(true)
{
}
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public PaletteQuantizer(bool dither)
protected PaletteQuantizer(bool dither)
: this(GetDiffuser(dither))
{
}
@ -37,42 +37,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
/// <inheritdoc />
public virtual IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
where TPixel : struct, IPixel<TPixel>
=> this.CreateFrameQuantizer(configuration, () => NamedColors<TPixel>.WebSafePalette);
public abstract IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
where TPixel : struct, IPixel<TPixel>;
/// <inheritdoc/>
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
public abstract IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="palette">The color palette.</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/></returns>
protected IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, TPixel[] palette, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
TPixel[] websafe = NamedColors<TPixel>.WebSafePalette;
int max = Math.Min(maxColors, websafe.Length);
int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length));
if (max != websafe.Length)
if (max != palette.Length)
{
return this.CreateFrameQuantizer(configuration, () => NamedColors<TPixel>.WebSafePalette.AsSpan(0, max).ToArray());
return new PaletteFrameQuantizer<TPixel>(this, palette.AsSpan(0, max).ToArray());
}
return this.CreateFrameQuantizer(configuration, () => websafe);
return new PaletteFrameQuantizer<TPixel>(this, palette);
}
/// <summary>
/// Gets the palette to use to quantize the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations</param>
/// <param name="paletteFunction">The method to return the palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/></returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, Func<TPixel[]> paletteFunction)
where TPixel : struct, IPixel<TPixel>
=> new PaletteFrameQuantizer<TPixel>(configuration, this, paletteFunction.Invoke());
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

110
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs

@ -0,0 +1,110 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A generic palette quantizer.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class PaletteQuantizer<TPixel> : IQuantizer
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
public PaletteQuantizer(TPixel[] palette)
: this(palette, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
/// <param name="dither">Whether to apply dithering to the output image</param>
public PaletteQuantizer(TPixel[] palette, bool dither)
: this(palette, GetDiffuser(dither))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser)
{
Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette));
this.Palette = palette;
this.Diffuser = diffuser;
}
/// <inheritdoc/>
public IErrorDiffuser Diffuser { get; }
/// <summary>
/// Gets the palette.
/// </summary>
public TPixel[] Palette { get; }
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration)
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration);
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration, int maxColors)
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration, maxColors);
/// <inheritdoc/>
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration)
{
if (!typeof(TPixel).Equals(typeof(TPixel1)))
{
throw new InvalidOperationException("Generic method type must be the same as class type.");
}
TPixel[] paletteRef = this.Palette;
return new PaletteFrameQuantizer<TPixel1>(this, Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef));
}
/// <inheritdoc/>
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration, int maxColors)
{
if (!typeof(TPixel).Equals(typeof(TPixel1)))
{
throw new InvalidOperationException("Generic method type must be the same as class type.");
}
TPixel[] paletteRef = this.Palette;
TPixel1[] castPalette = Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
int max = Math.Min(maxColors, castPalette.Length);
if (max != castPalette.Length)
{
return new PaletteFrameQuantizer<TPixel1>(this, castPalette.AsSpan(0, max).ToArray());
}
return new PaletteFrameQuantizer<TPixel1>(this, castPalette);
}
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

21
src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Contains color quantization specific constants.
/// </summary>
internal static class QuantizerConstants
{
/// <summary>
/// The minimum number of colors to use when quantizing an image.
/// </summary>
public const int MinColors = 1;
/// <summary>
/// The maximum number of colors to use when quantizing an image.
/// </summary>
public const int MaxColors = 256;
}
}

47
src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
/// </summary>
public class WebSafePaletteQuantizer : PaletteQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
public WebSafePaletteQuantizer()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WebSafePaletteQuantizer(bool dither)
: base(dither)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WebSafePaletteQuantizer(IErrorDiffuser diffuser)
: base(diffuser)
{
}
/// <inheritdoc />
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WebSafePalette.Length);
/// <inheritdoc/>
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WebSafePalette, maxColors);
}
}

48
src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public class WernerPaletteQuantizer : PaletteQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
public WernerPaletteQuantizer()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WernerPaletteQuantizer(bool dither)
: base(dither)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WernerPaletteQuantizer(IErrorDiffuser diffuser)
: base(diffuser)
{
}
/// <inheritdoc />
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WernerPalette.Length);
/// <inheritdoc/>
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WernerPalette, maxColors);
}
}

2
src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs

@ -173,6 +173,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
internal TPixel[] AotGetPalette() => this.GetPalette();
/// <inheritdoc/>
protected override TPixel[] GetPalette()
{

13
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs

@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class WuQuantizer : IQuantizer
{
/// <summary>
/// The default maximum number of colors to use when quantizing the image.
/// </summary>
public const int DefaultMaxColors = 256;
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
@ -41,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WuQuantizer(bool dither)
: this(GetDiffuser(dither), DefaultMaxColors)
: this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@ -50,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WuQuantizer(IErrorDiffuser diffuser)
: this(diffuser, DefaultMaxColors)
: this(diffuser, QuantizerConstants.MaxColors)
{
}
@ -62,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
/// <inheritdoc />
@ -83,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
maxColors = maxColors.Clamp(1, DefaultMaxColors);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new WuFrameQuantizer<TPixel>(this, maxColors);
}

12
src/ImageSharp/Processing/ResizeHelper.cs

@ -333,25 +333,21 @@ namespace SixLabors.ImageSharp.Processing
return (new Size(sourceWidth, sourceWidth), new Rectangle(0, 0, sourceWidth, sourceHeight));
}
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
float percentWidth = MathF.Abs(width / (float)sourceWidth);
float sourceRatio = (float)sourceHeight / sourceWidth;
// Find the shortest distance to go.
int widthDiff = sourceWidth - width;
int heightDiff = sourceHeight - height;
if (widthDiff < heightDiff)
{
float sourceRatio = (float)sourceHeight / sourceWidth;
destinationHeight = (int)MathF.Round(width * sourceRatio);
height = destinationHeight;
destinationWidth = width;
}
else if (widthDiff > heightDiff)
{
destinationWidth = (int)MathF.Round(height / sourceRatio);
float sourceRatioInverse = (float)sourceWidth / sourceHeight;
destinationWidth = (int)MathF.Round(height * sourceRatioInverse);
destinationHeight = height;
width = destinationWidth;
}
@ -360,12 +356,14 @@ namespace SixLabors.ImageSharp.Processing
if (height > width)
{
destinationWidth = width;
float percentWidth = MathF.Abs(width / (float)sourceWidth);
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
height = destinationHeight;
}
else
{
destinationHeight = height;
float percentHeight = MathF.Abs(height / (float)sourceHeight);
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
width = destinationWidth;
}

2
tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public void GifCore()
{
// Try to get as close to System.Drawing's output as possible
var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
using (var memoryStream = new MemoryStream())
{
this.bmpCore.SaveAsGif(memoryStream, options);

2
tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
this.ForEachImageSharpImage((img, ms) =>
{
// Try to get as close to System.Drawing's output as possible
var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
img.Save(ms, options); return null;
});
}

4
tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
var options = new PngEncoder { Quantizer = KnownQuantizers.Palette };
var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
var options = new PngEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}

2
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -11,7 +11,7 @@
<Authors>James Jackson-South and contributors</Authors>
<Company>James Jackson-South</Company>
<RootNamespace>SixLabors.ImageSharp.Sandbox46</RootNamespace>
<LangVersion>7.2</LangVersion>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />

18
tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Advanced
{
public class AotCompilerTests
{
[Fact]
public void AotCompiler_NoExceptions()
{
AotCompilerTools.Seed<Rgba32>();
}
}
}

3
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -67,7 +67,8 @@ namespace SixLabors.ImageSharp.Tests
new TheoryData<string>
{
nameof(KnownQuantizers.Octree),
nameof(KnownQuantizers.Palette),
nameof(KnownQuantizers.WebSafe),
nameof(KnownQuantizers.Werner),
nameof(KnownQuantizers.Wu)
};

33
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -1,20 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using System.IO;
using SixLabors.ImageSharp.Advanced;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
using System.Collections.Generic;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
public class GifDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
@ -70,6 +70,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
}
}
[Fact]
public unsafe void Decode_NonTerminatedFinalFrame()
{
var testFile = TestFile.Create(TestImages.Gif.Rings);
int length = testFile.Bytes.Length - 2;
fixed (byte* data = testFile.Bytes.AsSpan(0, length))
{
using (var stream = new UnmanagedMemoryStream(data, length))
{
var decoder = new GifDecoder();
using (Image<Rgba32> image = decoder.Decode<Rgba32>(Configuration.Default, stream))
{
Assert.Equal((200, 200), (image.Width, image.Height));
}
}
}
}
[Theory]
[MemberData(nameof(RatioFiles))]
public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)

4
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
public class GifEncoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.001F);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0015F);
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
// Use the palette quantizer without dithering to ensure results
// are consistant
Quantizer = new PaletteQuantizer(false)
Quantizer = new WebSafePaletteQuantizer(false)
};
// Always save as we need to compare the encoded output.

2
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Assert.Equal(PngChunkType.Palette, GetType("PLTE"));
Assert.Equal(PngChunkType.Data, GetType("IDAT"));
Assert.Equal(PngChunkType.End, GetType("IEND"));
Assert.Equal(PngChunkType.PaletteAlpha, GetType("tRNS"));
Assert.Equal(PngChunkType.Transparency, GetType("tRNS"));
Assert.Equal(PngChunkType.Text, GetType("tEXt"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs"));

2
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Theory]
[InlineData((uint)PngChunkType.Gamma)] // gAMA
[InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS
[InlineData((uint)PngChunkType.Transparency)] // tRNS
[InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks.
//[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this
public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType)

67
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -25,6 +25,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
{ TestImages.Png.Bpp1, PngBitDepth.Bit1 }
};
public static readonly TheoryData<string, PngBitDepth, PngColorType> PngTrnsFiles =
new TheoryData<string, PngBitDepth, PngColorType>
{
{ TestImages.Png.Gray1BitTrans, PngBitDepth.Bit1, PngColorType.Grayscale },
{ TestImages.Png.Gray2BitTrans, PngBitDepth.Bit2, PngColorType.Grayscale },
{ TestImages.Png.Gray4BitTrans, PngBitDepth.Bit4, PngColorType.Grayscale },
{ TestImages.Png.Gray8BitTrans, PngBitDepth.Bit8, PngColorType.Grayscale },
{ TestImages.Png.GrayTrns16BitInterlaced, PngBitDepth.Bit16, PngColorType.Grayscale },
{ TestImages.Png.Rgb24BppTrans, PngBitDepth.Bit8, PngColorType.Rgb },
{ TestImages.Png.Rgb48BppTrans, PngBitDepth.Bit16, PngColorType.Rgb }
};
/// <summary>
/// All types except Palette
/// </summary>
@ -249,6 +261,61 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
}
}
[Theory]
[MemberData(nameof(PngTrnsFiles))]
public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType)
{
var options = new PngEncoder();
var testFile = TestFile.Create(imagePath);
using (Image<Rgba32> input = testFile.CreateImage())
{
PngMetaData inMeta = input.MetaData.GetFormatMetaData(PngFormat.Instance);
Assert.True(inMeta.HasTrans);
using (var memStream = new MemoryStream())
{
input.Save(memStream, options);
memStream.Position = 0;
using (var output = Image.Load<Rgba32>(memStream))
{
PngMetaData outMeta = output.MetaData.GetFormatMetaData(PngFormat.Instance);
Assert.True(outMeta.HasTrans);
switch (pngColorType)
{
case PngColorType.Grayscale:
if (pngBitDepth.Equals(PngBitDepth.Bit16))
{
Assert.True(outMeta.TransparentGray16.HasValue);
Assert.Equal(inMeta.TransparentGray16, outMeta.TransparentGray16);
}
else
{
Assert.True(outMeta.TransparentGray8.HasValue);
Assert.Equal(inMeta.TransparentGray8, outMeta.TransparentGray8);
}
break;
case PngColorType.Rgb:
if (pngBitDepth.Equals(PngBitDepth.Bit16))
{
Assert.True(outMeta.TransparentRgb48.HasValue);
Assert.Equal(inMeta.TransparentRgb48, outMeta.TransparentRgb48);
}
else
{
Assert.True(outMeta.TransparentRgb24.HasValue);
Assert.Equal(inMeta.TransparentRgb24, outMeta.TransparentRgb24);
}
break;
}
}
}
}
}
private static void TestPngEncoderCore<TPixel>(
TestImageProvider<TPixel> provider,
PngColorType pngColorType,

58
tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs

@ -0,0 +1,58 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization
{
public class OctreeQuantizerTests
{
[Fact]
public void OctreeQuantizerConstructor()
{
var quantizer = new OctreeQuantizer(128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
quantizer = new OctreeQuantizer(false);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Null(quantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson, 128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
}
[Fact]
public void OctreeQuantizerCanCreateFrameQuantizer()
{
var quantizer = new OctreeQuantizer();
IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser);
quantizer = new OctreeQuantizer(false);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.False(frameQuantizer.Dither);
Assert.Null(frameQuantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser);
}
}
}

79
tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs

@ -0,0 +1,79 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization
{
public class PaletteQuantizerTests
{
private static readonly Rgba32[] Rgb = new Rgba32[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue };
[Fact]
public void PaletteQuantizerConstructor()
{
var quantizer = new PaletteQuantizer<Rgba32>(Rgb);
Assert.Equal(Rgb, quantizer.Palette);
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
quantizer = new PaletteQuantizer<Rgba32>(Rgb, false);
Assert.Equal(Rgb, quantizer.Palette);
Assert.Null(quantizer.Diffuser);
quantizer = new PaletteQuantizer<Rgba32>(Rgb, KnownDiffusers.Atkinson);
Assert.Equal(Rgb, quantizer.Palette);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
}
[Fact]
public void PaletteQuantizerCanCreateFrameQuantizer()
{
var quantizer = new PaletteQuantizer<Rgba32>(Rgb);
IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser);
quantizer = new PaletteQuantizer<Rgba32>(Rgb, false);
frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.False(frameQuantizer.Dither);
Assert.Null(frameQuantizer.Diffuser);
quantizer = new PaletteQuantizer<Rgba32>(Rgb, KnownDiffusers.Atkinson);
frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser);
}
[Fact]
public void PaletteQuantizerThrowsOnInvalidGenericMethodCall()
{
var quantizer = new PaletteQuantizer<Rgba32>(Rgb);
Assert.Throws<InvalidOperationException>(() => ((IQuantizer)quantizer).CreateFrameQuantizer<Rgb24>(Configuration.Default));
}
[Fact]
public void KnownQuantizersWebSafeTests()
{
IQuantizer quantizer = KnownQuantizers.WebSafe;
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
}
[Fact]
public void KnownQuantizersWernerTests()
{
IQuantizer quantizer = KnownQuantizers.Werner;
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
}
}
}

58
tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs

@ -0,0 +1,58 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization
{
public class WuQuantizerTests
{
[Fact]
public void WuQuantizerConstructor()
{
var quantizer = new WuQuantizer(128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
quantizer = new WuQuantizer(false);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Null(quantizer.Diffuser);
quantizer = new WuQuantizer(KnownDiffusers.Atkinson);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
quantizer = new WuQuantizer(KnownDiffusers.Atkinson, 128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
}
[Fact]
public void WuQuantizerCanCreateFrameQuantizer()
{
var quantizer = new WuQuantizer();
IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser);
quantizer = new WuQuantizer(false);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.False(frameQuantizer.Dither);
Assert.Null(frameQuantizer.Diffuser);
quantizer = new WuQuantizer(KnownDiffusers.Atkinson);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser);
}
}
}

3
tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs

@ -28,6 +28,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
};
@ -46,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
return;
}
const int ExecutionCount = 10;
const int ExecutionCount = 20;
if (!Vector.IsHardwareAccelerated)
{

34
tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs

@ -15,44 +15,22 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void QuantizersDitherByDefault()
{
var palette = new PaletteQuantizer();
var werner = new WernerPaletteQuantizer();
var websafe = new WebSafePaletteQuantizer();
var octree = new OctreeQuantizer();
var wu = new WuQuantizer();
Assert.NotNull(palette.Diffuser);
Assert.NotNull(werner.Diffuser);
Assert.NotNull(websafe.Diffuser);
Assert.NotNull(octree.Diffuser);
Assert.NotNull(wu.Diffuser);
Assert.True(palette.CreateFrameQuantizer<Rgba32>(this.Configuration).Dither);
Assert.True(werner.CreateFrameQuantizer<Rgba32>(this.Configuration).Dither);
Assert.True(websafe.CreateFrameQuantizer<Rgba32>(this.Configuration).Dither);
Assert.True(octree.CreateFrameQuantizer<Rgba32>(this.Configuration).Dither);
Assert.True(wu.CreateFrameQuantizer<Rgba32>(this.Configuration).Dither);
}
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)]
public void PaletteQuantizerYieldsCorrectTransparentPixel<TPixel>(
TestImageProvider<TPixel> provider,
bool dither)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
Assert.True(image[0, 0].Equals(default(TPixel)));
var quantizer = new PaletteQuantizer(dither);
foreach (ImageFrame<TPixel> frame in image.Frames)
{
QuantizedFrame<TPixel> quantized =
quantizer.CreateFrameQuantizer<TPixel>(this.Configuration).QuantizeFrame(frame);
int index = this.GetTransparentIndex(quantized);
Assert.Equal(index, quantized.GetPixelSpan()[0]);
}
}
}
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)]

3
tests/ImageSharp.Tests/TestImages.cs

@ -46,6 +46,9 @@ namespace SixLabors.ImageSharp.Tests
public const string PDSrc = "Png/pd-source.png";
public const string PDDest = "Png/pd-dest.png";
public const string Gray1BitTrans = "Png/gray-1-trns.png";
public const string Gray2BitTrans = "Png/gray-2-tRNS.png";
public const string Gray4BitTrans = "Png/gray-4-tRNS.png";
public const string Gray8BitTrans = "Png/gray-8-tRNS.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png";

2
tests/Images/External

@ -1 +1 @@
Subproject commit f41ae0327a3ab21ab2388c32160bda67debcc082
Subproject commit ed8a7b0b6fe1b2e2a7c822aa617103ae31192655

BIN
tests/Images/Input/Png/gray-2-tRNS.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

BIN
tests/Images/Input/Png/gray-4-tRNS.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

BIN
tests/Images/Input/Png/gray-8-tRNS.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Loading…
Cancel
Save