Browse Source

Merge branch 'master' into master

pull/764/head
James Jackson-South 8 years ago
committed by GitHub
parent
commit
9fdd90d814
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  2. 4
      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. 14
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  6. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  7. 2
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  8. 2
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  9. 14
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  10. 4
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  11. 2
      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. 4
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  16. 4
      src/ImageSharp/Formats/IImageFormat.cs
  17. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  18. 181
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  19. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  20. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  21. 79
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  22. 21
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  23. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  24. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  25. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  26. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  27. 11
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  28. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  29. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  30. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  31. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
  32. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  33. 66
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  34. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
  35. 2
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  36. 18
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  37. 2
      src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
  38. 2
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  39. 2
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  40. 2
      src/ImageSharp/IImageInfo.cs
  41. 24
      src/ImageSharp/Image.Decode.cs
  42. 24
      src/ImageSharp/Image.WrapMemory.cs
  43. 4
      src/ImageSharp/ImageExtensions.cs
  44. 2
      src/ImageSharp/Image{TPixel}.cs
  45. 23
      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. 13
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
  64. 19
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  65. 52
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
  66. 110
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  67. 21
      src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs
  68. 47
      src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
  69. 48
      src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
  70. 13
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
  71. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
  72. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
  73. 4
      tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
  74. 133
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
  75. 404
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
  76. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
  77. 53
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
  78. 7
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
  79. 59
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  80. 35
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs
  81. 48
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs
  82. 119
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
  83. 86
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs
  84. 96
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs
  85. 107
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs
  86. 32
      tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
  87. 74
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  88. 2
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  89. 5
      tests/ImageSharp.Sandbox46/Program.cs
  90. 21
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
  91. 35
      tests/ImageSharp.Tests/ComplexIntegrationTests.cs
  92. 3
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  93. 33
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  94. 4
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  95. 22
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
  96. 42
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  97. 7
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
  98. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
  99. 25
      tests/ImageSharp.Tests/Image/ImageTests.cs
  100. 58
      tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs

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

@ -82,11 +82,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
// we need to offset the pixel grid to account for when we outline a path. // we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to alline with the pixel grid. // region to align with the pixel grid.
float offset = 0.5f; float offset = 0.5f;
if (this.Options.Antialias) if (this.Options.Antialias)
{ {
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth; subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4) if (subpixelCount < 4)
{ {
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int pointsFound = region.Scan(subPixel + offset, buffer, configuration); int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0) if (pointsFound == 0)
{ {
// nothing on this line skip // nothing on this line, skip
continue; continue;
} }

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

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// <param name="font">The font we want to render with</param> /// <param name="font">The font we want to render with</param>
/// <param name="brush">The brush to source pixel colors from.</param> /// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param> /// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawign the text from.</param> /// <param name="location">The location on the image to start drawing the text from.</param>
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location) public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
{ {
Guard.NotNull(text, nameof(text)); Guard.NotNull(text, nameof(text));
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{ {
base.BeforeImageApply(source, sourceRectangle); base.BeforeImageApply(source, sourceRectangle);
// do everythign at the image level as we are deligating the processing down to other processors // do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{ {
ApplyKerning = this.Options.ApplyKerning, ApplyKerning = this.Options.ApplyKerning,

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); 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) if (destinationRow.Length > scanline.Length)
{ {
destinationRow = destinationRow.Slice(0, scanline.Length); destinationRow = destinationRow.Slice(0, scanline.Length);

6
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

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

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

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

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

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent. /// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent.
/// </summary> /// </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> /// <returns>The <see cref="ushort"/></returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257); public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);

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

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

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 bVec = new Vector<float>(256.0f / 255.0f);
var magicFloat = new Vector<float>(32768.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); var mask = new Vector<uint>(255);
ref Octet.OfByte sourceBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(source)); ref Octet.OfByte sourceBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(source));

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

@ -25,6 +25,20 @@ namespace SixLabors.ImageSharp
false; false;
#endif #endif
/// <summary>
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ConvertToSingle(
Vector<short> source,
out Vector<float> dest1,
out Vector<float> dest2)
{
Vector.Widen(source, out Vector<int> i1, out Vector<int> i2);
dest1 = Vector.ConvertToSingle(i1);
dest2 = Vector.ConvertToSingle(i2);
}
/// <summary> /// <summary>
/// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder). /// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary> /// </summary>

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

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

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

@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 data. /// Looks up color values and builds the image from de-compressed RLE8 data.
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/> /// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>

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

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

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

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private GifGraphicControlExtension graphicsControlExtension; private GifGraphicControlExtension graphicsControlExtension;
/// <summary> /// <summary>
/// The image desciptor. /// The image descriptor.
/// </summary> /// </summary>
private GifImageDescriptor imageDescriptor; private GifImageDescriptor imageDescriptor;
@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension(); this.ReadApplicationExtension();
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte(); this.SkipBlock(); // Not supported by any known decoder.
this.Skip(plainLength); // Not supported by any known decoder.
break; break;
} }
} }
@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (stream.ReadByte()) switch (stream.ReadByte())
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.SkipBlock(); // Skip graphic control extension block
// Skip graphic control extension block
this.Skip(0);
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments();
@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension(); this.ReadApplicationExtension();
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte(); this.SkipBlock(); // Not supported by any known decoder.
this.Skip(plainLength); // Not supported by any known decoder.
break; break;
} }
} }
@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Could be XMP or something else not supported yet. // Could be XMP or something else not supported yet.
// Back up and skip. // Back up and skip.
this.stream.Position -= appLength + 1; this.stream.Position -= appLength + 1;
this.Skip(appLength); this.SkipBlock(appLength);
return; return;
} }
this.Skip(appLength); // Not supported by any known decoder. this.SkipBlock(appLength); // Not supported by any known decoder.
} }
/// <summary> /// <summary>
/// Skips the designated number of bytes in the stream. /// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// </summary> /// </summary>
/// <param name="length">The number of bytes to skip.</param> private void SkipBlock(int blockSize = 0)
private void Skip(int length)
{ {
this.stream.Skip(length); if (blockSize > 0)
{
this.stream.Skip(blockSize);
}
int flag; int flag;
while ((flag = this.stream.ReadByte()) != 0) while ((flag = this.stream.ReadByte()) > 0)
{ {
this.stream.Skip(flag); this.stream.Skip(flag);
} }
@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor); this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
// Skip any remaining blocks // Skip any remaining blocks
this.Skip(0); this.SkipBlock();
} }
finally finally
{ {

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

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
internal sealed class GifEncoderCore internal sealed class GifEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during procesing operations. /// Used for allocating memory during processing operations.
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream) private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser); var palleteQuantizer = new PaletteQuantizer<TPixel>(quantized.Palette, this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++) for (int i = 0; i < image.Frames.Count; i++)
{ {
@ -158,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
else else
{ {
using (QuantizedFrame<TPixel> paletteQuantized using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
= palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration(), () => quantized.Palette).QuantizeFrame(frame))
{ {
this.WriteImageData(paletteQuantized, stream); this.WriteImageData(paletteQuantized, stream);
} }
@ -422,7 +421,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream) private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// 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 colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
int pixelCount = image.Palette.Length; int pixelCount = image.Palette.Length;

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

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}; };
/// <summary> /// <summary>
/// The maximium number of bits/code. /// The maximum number of bits/code.
/// </summary> /// </summary>
private const int MaxBits = 12; private const int MaxBits = 12;
@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk. /// flush the packet to disk.
/// </summary> /// </summary>
/// <param name="c">The character to add.</param> /// <param name="c">The character to add.</param>
/// <param name="accumulatorsRef">The reference to the storage for packat accumulators</param> /// <param name="accumulatorsRef">The reference to the storage for packet accumulators</param>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream) private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)

4
src/ImageSharp/Formats/IImageFormat.cs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{ {
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
ref Vector4Pair yBase = ref Vector4Pair yBase =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));

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

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
} }
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisible by 8.
/// </summary> /// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result) internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{ {

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 internal abstract partial class JpegColorConverter
{ {
/// <summary> /// <summary>
/// The avalilable converters /// The available converters
/// </summary> /// </summary>
private static readonly JpegColorConverter[] Converters = private static readonly JpegColorConverter[] Converters =
{ {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2
src/ImageSharp/IImageInfo.cs

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

24
src/ImageSharp/Image.Decode.cs

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

24
src/ImageSharp/Image.WrapMemory.cs

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

4
src/ImageSharp/ImageExtensions.cs

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

2
src/ImageSharp/Image{TPixel}.cs

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

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

@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0]; private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer) private string ConvertToString(ReadOnlySpan<byte> buffer)
{ {
int nullCharIndex = buffer.IndexOf((byte)0); int nullCharIndex = buffer.IndexOf((byte)0);
@ -382,13 +382,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags.Add(tag); this.invalidTags.Add(tag);
} }
[MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue) private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
where TEnum : struct where TEnum : struct
{ {
var enumValue = (TEnum)(object)value; if (EnumHelper<TEnum>.IsDefined(value))
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
{ {
return enumValue; return Unsafe.As<int, TEnum>(ref value);
} }
return defaultValue; return defaultValue;
@ -557,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer) ? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer); : BinaryPrimitives.ReadInt16LittleEndian(buffer);
} }
private class EnumHelper<TEnum>
where TEnum : struct
{
private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>()
.Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsDefined(int value)
{
return Array.BinarySearch(Values, value) >= 0;
}
}
} }
} }

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) public IccResponseCurve ReadResponseCurve(int channelCount)
{ {
var type = (IccCurveMeasurementEncodings)this.ReadUInt32(); var type = (IccCurveMeasurementEncodings)this.ReadUInt32();
uint[] measurment = new uint[channelCount]; uint[] measurement = new uint[channelCount];
for (int i = 0; i < channelCount; i++) for (int i = 0; i < channelCount; i++)
{ {
measurment[i] = this.ReadUInt32(); measurement[i] = this.ReadUInt32();
} }
Vector3[] xyzValues = new Vector3[channelCount]; Vector3[] xyzValues = new Vector3[channelCount];
@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
IccResponseNumber[][] response = new IccResponseNumber[channelCount][]; IccResponseNumber[][] response = new IccResponseNumber[channelCount][];
for (int i = 0; i < channelCount; i++) for (int i = 0; i < channelCount; i++)
{ {
response[i] = new IccResponseNumber[measurment[i]]; response[i] = new IccResponseNumber[measurement[i]];
for (uint j = 0; j < measurment[i]; j++) for (uint j = 0; j < measurement[i]; j++)
{ {
response[i][j] = this.ReadResponseNumber(); 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 int start = this.currentIndex - 8; // 8 is the tag header size
ushort channelCount = this.ReadUInt16(); ushort channelCount = this.ReadUInt16();
ushort measurmentCount = this.ReadUInt16(); ushort measurementCount = this.ReadUInt16();
uint[] offset = new uint[measurmentCount]; uint[] offset = new uint[measurementCount];
for (int i = 0; i < measurmentCount; i++) for (int i = 0; i < measurementCount; i++)
{ {
offset[i] = this.ReadUInt32(); offset[i] = this.ReadUInt32();
} }
var curves = new IccResponseCurve[measurmentCount]; var curves = new IccResponseCurve[measurementCount];
for (int i = 0; i < measurmentCount; i++) for (int i = 0; i < measurementCount; i++)
{ {
this.currentIndex = (int)(start + offset[i]); this.currentIndex = (int)(start + offset[i]);
curves[i] = this.ReadResponseCurve(channelCount); 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> /// <summary>
/// This profile provides the relevant information to perform a transformation /// This profile provides the relevant information to perform a transformation
/// between colour encodings and the PCS. This type of profile is based on /// between color encodings and the PCS. This type of profile is based on
/// modelling rather than device measurement or characterization data. /// modeling rather than device measurement or characterization data.
/// ColorSpace profiles may be embedded in images. /// ColorSpace profiles may be embedded in images.
/// </summary> /// </summary>
ColorSpace = 0x73706163, // spac ColorSpace = 0x73706163, // spac
/// <summary> /// <summary>
/// This profile represents abstract transforms and does not represent any /// 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. /// from PCS to PCS. Abstract profiles cannot be embedded in images.
/// </summary> /// </summary>
Abstract = 0x61627374, // abst Abstract = 0x61627374, // abst
@ -55,8 +55,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary> /// <summary>
/// NamedColor profiles can be thought of as sibling profiles to device profiles. /// 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 /// 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 /// process color conversions and one or more named color profiles to handle
/// named colours. /// named colors.
/// </summary> /// </summary>
NamedColor = 0x6E6D636C, // nmcl NamedColor = 0x6E6D636C, // nmcl
} }

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

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

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

@ -19,18 +19,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
Unknown, Unknown,
/// <summary> /// <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 /// from Device 1 to Device 2, using lookup table tag element structures
/// </summary> /// </summary>
AToB0 = 0x41324230, AToB0 = 0x41324230,
/// <summary> /// <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> /// </summary>
AToB1 = 0x41324231, AToB1 = 0x41324231,
/// <summary> /// <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> /// </summary>
AToB2 = 0x41324232, AToB2 = 0x41324232,
@ -46,40 +46,40 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
BlueTrc = 0x62545243, BlueTrc = 0x62545243,
/// <summary> /// <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> /// </summary>
BToA0 = 0x42324130, BToA0 = 0x42324130,
/// <summary> /// <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> /// </summary>
BToA1 = 0x42324131, BToA1 = 0x42324131,
/// <summary> /// <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> /// </summary>
BToA2 = 0x42324132, BToA2 = 0x42324132,
/// <summary> /// <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. /// provides a means to override the BToA0 tag.
/// </summary> /// </summary>
BToD0 = 0x42324430, BToD0 = 0x42324430,
/// <summary> /// <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. /// provides a means to override the BToA1 tag.
/// </summary> /// </summary>
BToD1 = 0x42324431, BToD1 = 0x42324431,
/// <summary> /// <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. /// provides a means to override the BToA2 tag.
/// </summary> /// </summary>
BToD2 = 0x42324432, BToD2 = 0x42324432,
/// <summary> /// <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. /// provides a means to override the BToA1 tag.
/// </summary> /// </summary>
BToD3 = 0x42324433, BToD3 = 0x42324433,
@ -97,8 +97,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
CharTarget = 0x74617267, CharTarget = 0x74617267,
/// <summary> /// <summary>
/// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ colour, measured using the actual illumination /// 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 colour relative to the PCS adopted white /// conditions and relative to the actual adopted white, to an nCIEXYZ color relative to the PCS adopted white
/// </summary> /// </summary>
ChromaticAdaptation = 0x63686164, ChromaticAdaptation = 0x63686164,
@ -166,33 +166,33 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DeviceSettings = 0x64657673, DeviceSettings = 0x64657673,
/// <summary> /// <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 /// input range, output range and transform, and provides a means to override the AToB0 tag
/// </summary> /// </summary>
DToB0 = 0x44324230, DToB0 = 0x44324230,
/// <summary> /// <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 /// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary> /// </summary>
DToB1 = 0x44324230, DToB1 = 0x44324230,
/// <summary> /// <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 /// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary> /// </summary>
DToB2 = 0x44324230, DToB2 = 0x44324230,
/// <summary> /// <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 /// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary> /// </summary>
DToB3 = 0x44324230, DToB3 = 0x44324230,
/// <summary> /// <summary>
/// gamt - This tag provides a table in which PCS values are the input and a single /// 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. /// 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 colour is out-of-gamut /// If the output is non-zero, the PCS color is out-of-gamut
/// </summary> /// </summary>
Gamut = 0x67616D74, Gamut = 0x67616D74,
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
GreenTrc = 0x67545243, GreenTrc = 0x67545243,
/// <summary> /// <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> /// </summary>
Luminance = 0x6C756d69, Luminance = 0x6C756d69,
@ -240,8 +240,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
NamedColor = 0x6E636f6C, NamedColor = 0x6E636f6C,
/// <summary> /// <summary>
/// ncl2 - This tag contains the named colour information providing a PCS and optional device representation /// ncl2 - This tag contains the named color information providing a PCS and optional device representation
/// for a list of named colours. /// for a list of named colors.
/// </summary> /// </summary>
NamedColor2 = 0x6E636C32, NamedColor2 = 0x6E636C32,

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

@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{ {
/// <summary> /// <summary>
/// In perceptual transforms the PCS values represent hypothetical /// 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 /// medium. By extension, for the perceptual intent, the PCS represents
/// the appearance of that reproduction as viewed in the reference viewing /// the appearance of that reproduction as viewed in the reference viewing
/// environment by a human observer adapted to that environment. The exact /// 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> /// </summary>
Perceptual = 0, Perceptual = 0,
@ -27,15 +27,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MediaRelativeColorimetric = 1, MediaRelativeColorimetric = 1,
/// <summary> /// <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 /// 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> /// </summary>
Saturation = 2, Saturation = 2,
/// <summary> /// <summary>
/// Transformations for this intent shall leave the chromatically /// 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> /// </summary>
AbsoluteColorimetric = 3, 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 /// 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 /// 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 /// 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 /// 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 /// the order KCMY), this tag may be used to specify the laydown order of the
/// colorants /// colorants
@ -59,25 +59,25 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DateTime = 0x6474696D, DateTime = 0x6474696D,
/// <summary> /// <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 /// 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 /// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables /// lookup table, and a set of one-dimensional output tables
/// </summary> /// </summary>
Lut16 = 0x6D667432, Lut16 = 0x6D667432,
/// <summary> /// <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 /// 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 /// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables. /// lookup table, and a set of one-dimensional output tables.
/// </summary> /// </summary>
Lut8 = 0x6D667431, Lut8 = 0x6D667431,
/// <summary> /// <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 /// to five processing elements which are stored in the AToBTag tag
/// in the following order: a set of one-dimensional curves, a 3 × 3 /// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a /// matrix with offset terms, a set of one-dimensional curves, a
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
LutAToB = 0x6D414220, LutAToB = 0x6D414220,
/// <summary> /// <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 /// up to five processing elements which are stored in the BToATag
/// in the following order: a set of one-dimensional curves, a 3 × 3 /// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a /// matrix with offset terms, a set of one-dimensional curves, a
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiLocalizedUnicode = 0x6D6C7563, MultiLocalizedUnicode = 0x6D6C7563,
/// <summary> /// <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 /// of processing elements. The processing elements contained in the
/// structure are defined in the structure itself, allowing for a flexible /// structure are defined in the structure itself, allowing for a flexible
/// structure. Currently supported processing elements are: a set of one /// structure. Currently supported processing elements are: a set of one
@ -123,15 +123,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiProcessElements = 0x6D706574, MultiProcessElements = 0x6D706574,
/// <summary> /// <summary>
/// This type is a count value and array of structures that provide colour /// This type is a count value and array of structures that provide color
/// coordinates for colour names. For each named colour, a PCS and optional /// coordinates for color names. For each named color, a PCS and optional
/// device representation of the colour are given. Both representations are /// device representation of the color are given. Both representations are
/// 16-bit values and PCS values shall be relative colorimetric. The device /// 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 /// This representation should be consistent with the "number of device
/// coordinates" field in the namedColor2Type. If this field is 0, device /// coordinates" field in the namedColor2Type. If this field is 0, device
/// coordinates are not provided. The PCS representation corresponds to the /// 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 /// names are fixed-length, 32-byte fields including null termination. In
/// order to maintain maximum portability, it is strongly recommended that /// order to maintain maximum portability, it is strongly recommended that
/// special characters of the 7-bit ASCII set not be used. /// 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 /// 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 /// having to produce a new profile. The mechanism can be used by applications
/// to allow users with relatively inexpensive and readily available /// 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. /// channels in order to achieve consistent results.
/// </summary> /// </summary>
ResponseCurveSet16 = 0x72637332, 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 /// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param> /// </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) public static TPixel FromHex(string hex)
{ {
Guard.NotNullOrWhiteSpace(hex, nameof(hex)); Guard.NotNullOrWhiteSpace(hex, nameof(hex));

409
src/ImageSharp/PixelFormats/ColorConstants.cs

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

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

@ -14,9 +14,10 @@ namespace SixLabors.ImageSharp.PixelFormats
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Thread-safe backing field for <see cref="WebSafePalette"/>. /// Thread-safe backing field for the constant palettes.
/// </summary> /// </summary>
private static readonly Lazy<TPixel[]> WebSafePaletteLazy = new Lazy<TPixel[]>(GetWebSafePalette, true); private static readonly Lazy<TPixel[]> WebSafePaletteLazy = new Lazy<TPixel[]>(GetWebSafePalette, true);
private static readonly Lazy<TPixel[]> WernerPaletteLazy = new Lazy<TPixel[]>(GetWernerPalette, true);
/// <summary> /// <summary>
/// Represents a <see paramref="TPixel"/> matching the W3C definition that has an hex value of #F0F8FF. /// 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); public static readonly TPixel YellowGreen = ColorBuilder<TPixel>.FromRGBA(154, 205, 50, 255);
/// <summary> /// <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> /// </summary>
public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value; 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 converted = new TPixel[palette.Length];
var safe = new TPixel[constants.Length + 1];
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(constants.AsSpan()); Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(palette.AsSpan());
PixelOperations<TPixel>.Instance.FromRgba32Bytes( PixelOperations<TPixel>.Instance.FromRgba32Bytes(
Configuration.Default, Configuration.Default,
constantsBytes, constantsBytes,
safe, converted,
constants.Length); palette.Length);
return safe;
return converted;
} }
} }
} }

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

@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
// Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the // 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 // 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. // packs/unpacks the pixel without and conversion so we employ custom methods do do this.
if (typeof(TDestinationPixel) == typeof(Gray16)) 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] // packedRgba = [aa bb gg rr]
// tmp1 = [aa 00 gg 00] // tmp1 = [aa 00 gg 00]
// tmp2 = [00 bb 00 rr] // 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] // tmp1 + tmp3 = [aa rr gg bb]
uint tmp1 = packedRgba & 0xFF00FF00; uint tmp1 = packedRgba & 0xFF00FF00;
uint tmp2 = packedRgba & 0x00FF00FF; 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); Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer); 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: // but we are always reading/writing at different positions:
SimdUtils.BulkConvertByteToNormalizedFloat( SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer), MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer),

13
src/ImageSharp/Processing/KnownQuantizers.cs

@ -12,20 +12,23 @@ namespace SixLabors.ImageSharp.Processing
{ {
/// <summary> /// <summary>
/// Gets the adaptive Octree quantizer. Fast with good quality. /// Gets the adaptive Octree quantizer. Fast with good quality.
/// The quantizer only supports a single alpha value.
/// </summary> /// </summary>
public static IQuantizer Octree { get; } = new OctreeQuantizer(); public static IQuantizer Octree { get; } = new OctreeQuantizer();
/// <summary> /// <summary>
/// Gets the Xiaolin Wu's Color Quantizer which generates high quality output. /// Gets the Xiaolin Wu's Color Quantizer which generates high quality output.
/// The quantizer supports multiple alpha values.
/// </summary> /// </summary>
public static IQuantizer Wu { get; } = new WuQuantizer(); public static IQuantizer Wu { get; } = new WuQuantizer();
/// <summary> /// <summary>
/// Gets the palette based, Using the collection of web-safe colors. /// Gets the palette based quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
/// The quantizer supports multiple alpha values.
/// </summary> /// </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>(size, 1)
: new DenseMatrix<float>(1, size); : new DenseMatrix<float>(1, size);
float sum = 0F; kernel.Fill(1.0F / size);
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;
}
}
return kernel; 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 namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{ {
/// <summary> /// <summary>
/// Contains the eight matrices used for Kirsh edge detection /// Contains the eight matrices used for Kirsch edge detection
/// </summary> /// </summary>
internal static class KirshKernels internal static class KirschKernels
{ {
/// <summary> /// <summary>
/// Gets the North gradient operator /// Gets the North gradient operator

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

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

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

@ -15,11 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
public class OctreeQuantizer : IQuantizer public class OctreeQuantizer : IQuantizer
{ {
/// <summary>
/// The default maximum number of colors to use when quantizing the image.
/// </summary>
public const int DefaultMaxColors = 256;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class. /// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary> /// </summary>
@ -42,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param> /// <param name="dither">Whether to apply dithering to the output image</param>
public OctreeQuantizer(bool dither) public OctreeQuantizer(bool dither)
: this(GetDiffuser(dither), DefaultMaxColors) : this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{ {
} }
@ -51,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param> /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public OctreeQuantizer(IErrorDiffuser diffuser) 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) public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
{ {
this.Diffuser = diffuser; this.Diffuser = diffuser;
this.MaxColors = maxColors.Clamp(1, DefaultMaxColors); this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -84,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors) public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
maxColors = maxColors.Clamp(1, DefaultMaxColors); maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new OctreeFrameQuantizer<TPixel>(this, 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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
@ -23,27 +22,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
private readonly TPixel[] palette; private readonly TPixel[] palette;
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private readonly Vector4[] paletteVector;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class. /// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="quantizer">The palette quantizer.</param> /// <param name="quantizer">The palette quantizer.</param>
/// <param name="colors">An array of all colors in the palette.</param> /// <param name="colors">An array of all colors in the palette.</param>
public PaletteFrameQuantizer(Configuration configuration, PaletteQuantizer quantizer, TPixel[] colors) public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors)
: base(quantizer, true) : base(quantizer, true) => this.palette = colors;
{
// 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);
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void SecondPass( 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 namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
/// <summary> /// <summary>
/// Allows the quantization of images pixels using web safe colors defined in the CSS Color Module Level 4. /// Allows the quantization of images pixels using color palettes.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/> Override this class to provide your own palette. /// Override this class to provide your own palette.
/// <para> /// <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> /// </para>
/// </summary> /// </summary>
public class PaletteQuantizer : IQuantizer public abstract class PaletteQuantizer : IQuantizer
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class. /// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary> /// </summary>
public PaletteQuantizer() protected PaletteQuantizer()
: this(true) : this(true)
{ {
} }
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class. /// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary> /// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param> /// <param name="dither">Whether to apply dithering to the output image</param>
public PaletteQuantizer(bool dither) protected PaletteQuantizer(bool dither)
: this(GetDiffuser(dither)) : this(GetDiffuser(dither))
{ {
} }
@ -37,42 +37,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class. /// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary> /// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param> /// <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 /> /// <inheritdoc />
public IErrorDiffuser Diffuser { get; } public IErrorDiffuser Diffuser { get; }
/// <inheritdoc /> /// <inheritdoc />
public virtual IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration) public abstract IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>;
=> this.CreateFrameQuantizer(configuration, () => NamedColors<TPixel>.WebSafePalette);
/// <inheritdoc/> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
TPixel[] websafe = NamedColors<TPixel>.WebSafePalette; int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length));
int max = Math.Min(maxColors, websafe.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; 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);
}
}

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

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

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

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public void GifCore() public void GifCore()
{ {
// Try to get as close to System.Drawing's output as possible // 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()) using (var memoryStream = new MemoryStream())
{ {
this.bmpCore.SaveAsGif(memoryStream, options); 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) => this.ForEachImageSharpImage((img, ms) =>
{ {
// Try to get as close to System.Drawing's output as possible // 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; 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()) using (var memoryStream = new MemoryStream())
{ {
var options = new PngEncoder { Quantizer = KnownQuantizers.Palette }; var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe };
this.bmpCore.SaveAsPng(memoryStream, options); this.bmpCore.SaveAsPng(memoryStream, options);
} }
} }
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
using (var memoryStream = new MemoryStream()) 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); this.bmpCore.SaveAsPng(memoryStream, options);
} }
} }

133
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs

@ -0,0 +1,133 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_CopyTo1x1
{
private Block8x8F block;
private Buffer2D<float> buffer;
private BufferArea<float> destArea;
[GlobalSetup]
public void Setup()
{
if (!SimdUtils.IsAvx2CompatibleArchitecture)
{
throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support.");
}
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
this.destArea = this.buffer.GetArea(200, 100, 64, 64);
}
[Benchmark(Baseline = true)]
public void Original()
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this.block);
ref byte destBase = ref Unsafe.As<float, byte>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
CopyRowImpl(ref selfBase, ref destBase, destStride, 2);
CopyRowImpl(ref selfBase, ref destBase, destStride, 3);
CopyRowImpl(ref selfBase, ref destBase, destStride, 4);
CopyRowImpl(ref selfBase, ref destBase, destStride, 5);
CopyRowImpl(ref selfBase, ref destBase, destStride, 6);
CopyRowImpl(ref selfBase, ref destBase, destStride, 7);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row)
{
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float));
ref byte d = ref Unsafe.Add(ref destBase, row * destStride);
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
[Benchmark]
public void UseVector8()
{
ref Block8x8F s = ref this.block;
ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
Vector<float> row2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
Vector<float> row3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
Vector<float> row4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
Vector<float> row5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
d0 = row0;
d1 = row1;
d2 = row2;
d3 = row3;
d4 = row4;
d5 = row5;
d6 = row6;
d7 = row7;
}
[Benchmark]
public void UseVector8_V2()
{
ref Block8x8F s = ref this.block;
ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
}
// RESULTS:
//
// Method | Mean | Error | StdDev | Scaled |
// -------------- |---------:|----------:|----------:|-------:|
// Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 |
// UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 |
// UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 |
//
// Conclusion:
// Doesn't worth to bother with this
}
}

404
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs

@ -0,0 +1,404 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_CopyTo2x2
{
private Block8x8F block;
private Buffer2D<float> buffer;
private BufferArea<float> destArea;
[GlobalSetup]
public void Setup()
{
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
this.destArea = this.buffer.GetArea(200, 100, 128, 128);
}
[Benchmark(Baseline = true)]
public void Original()
{
ref float destBase = ref this.destArea.GetReferenceToOrigin();
int destStride = this.destArea.Stride;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride)
{
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
}
[Benchmark]
public void Original_V2()
{
ref float destBase = ref this.destArea.GetReferenceToOrigin();
int destStride = this.destArea.Stride;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride)
{
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref dest0, 0) = selfLeft.X;
Unsafe.Add(ref dest0, 1) = selfLeft.X;
Unsafe.Add(ref dest0, 2) = selfLeft.Y;
Unsafe.Add(ref dest0, 3) = selfLeft.Y;
Unsafe.Add(ref dest0, 4) = selfLeft.Z;
Unsafe.Add(ref dest0, 5) = selfLeft.Z;
Unsafe.Add(ref dest0, 6) = selfLeft.W;
Unsafe.Add(ref dest0, 7) = selfLeft.W;
ref float dest1 = ref Unsafe.Add(ref dest0, 8);
Unsafe.Add(ref dest1, 0) = selfRight.X;
Unsafe.Add(ref dest1, 1) = selfRight.X;
Unsafe.Add(ref dest1, 2) = selfRight.Y;
Unsafe.Add(ref dest1, 3) = selfRight.Y;
Unsafe.Add(ref dest1, 4) = selfRight.Z;
Unsafe.Add(ref dest1, 5) = selfRight.Z;
Unsafe.Add(ref dest1, 6) = selfRight.W;
Unsafe.Add(ref dest1, 7) = selfRight.W;
ref float dest2 = ref Unsafe.Add(ref dest0, destStride);
Unsafe.Add(ref dest2, 0) = selfLeft.X;
Unsafe.Add(ref dest2, 1) = selfLeft.X;
Unsafe.Add(ref dest2, 2) = selfLeft.Y;
Unsafe.Add(ref dest2, 3) = selfLeft.Y;
Unsafe.Add(ref dest2, 4) = selfLeft.Z;
Unsafe.Add(ref dest2, 5) = selfLeft.Z;
Unsafe.Add(ref dest2, 6) = selfLeft.W;
Unsafe.Add(ref dest2, 7) = selfLeft.W;
ref float dest3 = ref Unsafe.Add(ref dest2, 8);
Unsafe.Add(ref dest3, 0) = selfRight.X;
Unsafe.Add(ref dest3, 1) = selfRight.X;
Unsafe.Add(ref dest3, 2) = selfRight.Y;
Unsafe.Add(ref dest3, 3) = selfRight.Y;
Unsafe.Add(ref dest3, 4) = selfRight.Z;
Unsafe.Add(ref dest3, 5) = selfRight.Z;
Unsafe.Add(ref dest3, 6) = selfRight.W;
Unsafe.Add(ref dest3, 7) = selfRight.W;
}
[Benchmark]
public void UseVector2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
var xLeft = new Vector2(sLeft.X);
var yLeft = new Vector2(sLeft.Y);
var zLeft = new Vector2(sLeft.Z);
var wLeft = new Vector2(sLeft.W);
var xRight = new Vector2(sRight.X);
var yRight = new Vector2(sRight.Y);
var zRight = new Vector2(sRight.Z);
var wRight = new Vector2(sRight.W);
dTopLeft = xLeft;
Unsafe.Add(ref dTopLeft, 1) = yLeft;
Unsafe.Add(ref dTopLeft, 2) = zLeft;
Unsafe.Add(ref dTopLeft, 3) = wLeft;
dTopRight = xRight;
Unsafe.Add(ref dTopRight, 1) = yRight;
Unsafe.Add(ref dTopRight, 2) = zRight;
Unsafe.Add(ref dTopRight, 3) = wRight;
dBottomLeft = xLeft;
Unsafe.Add(ref dBottomLeft, 1) = yLeft;
Unsafe.Add(ref dBottomLeft, 2) = zLeft;
Unsafe.Add(ref dBottomLeft, 3) = wLeft;
dBottomRight = xRight;
Unsafe.Add(ref dBottomRight, 1) = yRight;
Unsafe.Add(ref dBottomRight, 2) = zRight;
Unsafe.Add(ref dBottomRight, 3) = wRight;
}
[Benchmark]
public void UseVector4()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
var xLeft = new Vector4(sLeft.X);
var yLeft = new Vector4(sLeft.Y);
var zLeft = new Vector4(sLeft.Z);
var wLeft = new Vector4(sLeft.W);
var xRight = new Vector4(sRight.X);
var yRight = new Vector4(sRight.Y);
var zRight = new Vector4(sRight.Z);
var wRight = new Vector4(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 3)) = wRight;
}
[Benchmark]
public void UseVector4_SafeRightCorner()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
var xLeft = new Vector4(sLeft.X);
var yLeft = new Vector4(sLeft.Y);
var zLeft = new Vector4(sLeft.Z);
var wLeft = new Vector4(sLeft.W);
var xRight = new Vector4(sRight.X);
var yRight = new Vector4(sRight.Y);
var zRight = new Vector4(sRight.Z);
var wRight = new Vector2(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
Unsafe.Add(ref dTopLeft, 7) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
Unsafe.Add(ref dBottomLeft, 7) = wRight;
}
[Benchmark]
public void UseVector4_V2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
int offset = 2 * row * destStride;
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X);
xyLeft.Z = sLeft.Y;
xyLeft.W = sLeft.Y;
var zwLeft = new Vector4(sLeft.Z);
zwLeft.Z = sLeft.W;
zwLeft.W = sLeft.W;
var xyRight = new Vector4(sRight.X);
xyRight.Z = sRight.Y;
xyRight.W = sRight.Y;
var zwRight = new Vector4(sRight.Z);
zwRight.Z = sRight.W;
zwRight.W = sRight.W;
dTopLeft = xyLeft;
Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref dTopLeft, 2) = xyRight;
Unsafe.Add(ref dTopLeft, 3) = zwRight;
dBottomLeft = xyLeft;
Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
Unsafe.Add(ref dBottomLeft, 2) = xyRight;
Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
// RESULTS:
// Method | Mean | Error | StdDev | Scaled | ScaledSD |
// --------------------------- |---------:|----------:|----------:|-------:|---------:|
// Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 |
// Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 |
// UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 |
// UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 |
// UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 |
// UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 |
}
}

6
tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs → tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs

@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.General namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{ {
/// <summary> /// <summary>
/// The goal of this benchmark is to measure the following Jpeg-related scenario: /// The goal of this benchmark is to measure the following Jpeg-related scenario:
@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.General
private static readonly Vector4 MinusOne = new Vector4(-1); private static readonly Vector4 MinusOne = new Vector4(-1);
private static readonly Vector4 Half = new Vector4(0.5f); private static readonly Vector4 Half = new Vector4(0.5f);
private Block8x8F inputDividend = default(Block8x8F); private Block8x8F inputDividend;
private Block8x8F inputDivisior = default(Block8x8F); private Block8x8F inputDivisior;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()

53
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_LoadFromInt16
{
private Block8x8 source;
private Block8x8F dest = default;
[GlobalSetup]
public void Setup()
{
if (Vector<float>.Count != 8)
{
throw new NotSupportedException("Vector<float>.Count != 8");
}
for (short i = 0; i < Block8x8F.Size; i++)
{
this.source[i] = i;
}
}
[Benchmark(Baseline = true)]
public void Scalar()
{
this.dest.LoadFromInt16Scalar(ref this.source);
}
[Benchmark]
public void ExtendedAvx2()
{
this.dest.LoadFromInt16ExtendedAvx2(ref this.source);
}
// RESULT:
// Method | Mean | Error | StdDev | Scaled |
// ------------- |---------:|----------:|----------:|-------:|
// Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 |
// ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 |
}
}

7
tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs → tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs

@ -1,4 +1,7 @@
// ReSharper disable InconsistentNaming // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
using System; using System;
using System.Numerics; using System.Numerics;
@ -8,7 +11,7 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Benchmarks.General namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{ {
public class Block8x8F_Round public class Block8x8F_Round
{ {

59
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs

@ -1,59 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using CoreSize = SixLabors.Primitives.Size;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class DecodeJpeg : BenchmarkBase
{
private byte[] jpegBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)]
public string TestImage { get; set; }
[GlobalSetup]
public void ReadImages()
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
public Size JpegSystemDrawing()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = SDImage.FromStream(memoryStream))
{
return image.Size;
}
}
}
[Benchmark(Description = "Decode Jpeg - ImageSharp")]
public CoreSize JpegImageSharp()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder()))
{
return new CoreSize(image.Width, image.Height);
}
}
}
}
}

35
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class DecodeJpegMultiple : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[]
{
"Jpg/baseline",
"Jpg/progressive",
};
protected override IEnumerable<string> SearchPatterns => new[] { "*.jpg" };
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")]
public void DecodeJpegImageSharp()
{
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder()));
}
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")]
public void DecodeJpegSystemDrawing()
{
this.ForEachStream(SDImage.FromStream);
}
}
}

48
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using SDImage = System.Drawing.Image;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// An expensive Jpeg benchmark, running on a wide range of input images, showing aggregate results.
/// </summary>
[Config(typeof(MultiImageBenchmarkBase.Config))]
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles =>
new[]
{
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
};
[Params(InputImageCategory.AllImages)]
public override InputImageCategory InputCategory { get; set; }
[Benchmark]
public void ImageSharp()
{
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder()));
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
this.ForEachStream(SDImage.FromStream);
}
}
}

119
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs

@ -0,0 +1,119 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using CoreSize = SixLabors.Primitives.Size;
using SDImage = System.Drawing.Image;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// Image-specific Jpeg benchmarks
/// </summary>
[Config(typeof(Config.ShortClr))]
public class DecodeJpeg_ImageSpecific
{
public class Config : ManualConfig
{
public Config()
{
// Uncomment if you want to use any of the diagnoser
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
}
public class ShortClr : Benchmarks.Config
{
public ShortClr()
{
this.Add(
//Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3),
Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3)
);
}
}
}
private byte[] jpegBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
// The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr"
// is almost the same as the result for Jpeg420Exif,
// which proves that the execution time for the most common YCbCr 420 path scales linearly.
//
// TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
)]
public string TestImage { get; set; }
[GlobalSetup]
public void ReadImages()
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
public Size JpegSystemDrawing()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = SDImage.FromStream(memoryStream))
{
return image.Size;
}
}
}
[Benchmark(Description = "Decode Jpeg - ImageSharp")]
public CoreSize JpegImageSharp()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder(){ IgnoreMetadata = true}))
{
return new CoreSize(image.Width, image.Height);
}
}
}
// RESULTS (2018 November 4):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:|
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB |
}
}

86
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs

@ -1,86 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using System;
using System.IO;
using SixLabors.ImageSharp.Tests;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using SixLabors.ImageSharp.Processing;
using SDImage = System.Drawing.Image;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class LoadResizeSave : BenchmarkBase
{
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] sourceBytes;
private byte[] destBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.Baseline.Jpeg420Exif
//, TestImages.Jpeg.Baseline.Calliphora
)]
public string TestImage { get; set; }
[Params(false, true)]
public bool EnableParallelExecution { get; set; }
[GlobalSetup]
public void Setup()
{
this.configuration.MaxDegreeOfParallelism =
this.EnableParallelExecution ? Environment.ProcessorCount : 1;
if (this.sourceBytes == null)
{
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
}
if (this.destBytes == null)
{
this.destBytes = new byte[this.sourceBytes.Length];
}
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
using (var sourceStream = new MemoryStream(this.sourceBytes))
using (var destStream = new MemoryStream(this.destBytes))
using (var source = SDImage.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var graphics = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
}
[Benchmark]
public void ImageSharp()
{
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
using (source)
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
}
}

96
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs

@ -0,0 +1,96 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(MultiImageBenchmarkBase.Config))]
public class LoadResizeSave_Aggregate : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles =>
new[]
{
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
};
[Params(InputImageCategory.AllImages)]
public override InputImageCategory InputCategory { get; set; }
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] destBytes;
public override void Setup()
{
base.Setup();
this.configuration.MaxDegreeOfParallelism = 1;
const int MaxOutputSizeInBytes = 2 * 1024 * 1024; // ~2 MB
this.destBytes = new byte[MaxOutputSizeInBytes];
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
this.ForEachStream(
sourceStream =>
{
using (var destStream = new MemoryStream(this.destBytes))
using (var source = System.Drawing.Image.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var g = Graphics.FromImage(destination))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
return null;
});
}
[Benchmark]
public void ImageSharp()
{
this.ForEachStream(
sourceStream =>
{
using (var source = Image.Load<Rgba32>(
this.configuration,
sourceStream,
new JpegDecoder { IgnoreMetadata = true }))
{
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
return null;
});
}
}
}

107
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs

@ -0,0 +1,107 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using System;
using System.IO;
using SixLabors.ImageSharp.Tests;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using SixLabors.ImageSharp.Processing;
using SDImage = System.Drawing.Image;
using SixLabors.ImageSharp.Formats.Jpeg;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class LoadResizeSave_ImageSpecific
{
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] sourceBytes;
private byte[] destBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
)]
public string TestImage { get; set; }
[Params(false, true)]
public bool ParallelExec { get; set; }
[GlobalSetup]
public void Setup()
{
this.configuration.MaxDegreeOfParallelism =
this.ParallelExec ? Environment.ProcessorCount : 1;
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
this.destBytes = new byte[this.sourceBytes.Length * 2];
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
using (var sourceStream = new MemoryStream(this.sourceBytes))
using (var destStream = new MemoryStream(this.destBytes))
using (var source = SDImage.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var g = Graphics.FromImage(destination))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
}
[Benchmark]
public void ImageSharp()
{
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
using (source)
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
// RESULTS (2018 October 31):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-ZPEZGV : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-SGOCJT : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | TestImage | ParallelExec | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
// -------------- |-------- |----------------------------- |------------- |----------:|----------:|----------:|-------:|---------:|---------:|----------:|
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | False | 64.88 ms | 3.735 ms | 0.2110 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | False | 129.53 ms | 23.423 ms | 1.3234 ms | 2.00 | 0.02 | - | 50.09 KB |
// | | | | | | | | | | |
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | False | 65.87 ms | 10.488 ms | 0.5926 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | False | 92.00 ms | 7.241 ms | 0.4091 ms | 1.40 | 0.01 | - | 46.36 KB |
// | | | | | | | | | | |
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | True | 64.23 ms | 5.998 ms | 0.3389 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | True | 82.63 ms | 29.320 ms | 1.6566 ms | 1.29 | 0.02 | - | 57.59 KB |
// | | | | | | | | | | |
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | True | 64.20 ms | 6.560 ms | 0.3707 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | True | 68.08 ms | 18.376 ms | 1.0383 ms | 1.06 | 0.01 | - | 50.49 KB |
}
}

32
tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs

@ -3,6 +3,9 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.Codecs namespace SixLabors.ImageSharp.Benchmarks.Codecs
@ -20,8 +23,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using CoreImage = ImageSharp.Image; using CoreImage = ImageSharp.Image;
public abstract class MultiImageBenchmarkBase : BenchmarkBase public abstract class MultiImageBenchmarkBase
{ {
public class Config : ManualConfig
{
public Config()
{
// Uncomment if you want to use any of the diagnoser
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
}
public class ShortClr : Benchmarks.Config
{
public ShortClr()
{
this.Add(
Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2)
);
}
}
}
protected Dictionary<string, byte[]> FileNamesToBytes = new Dictionary<string, byte[]>(); protected Dictionary<string, byte[]> FileNamesToBytes = new Dictionary<string, byte[]>();
protected Dictionary<string, Image<Rgba32>> FileNamesToImageSharpImages = new Dictionary<string, Image<Rgba32>>(); protected Dictionary<string, Image<Rgba32>> FileNamesToImageSharpImages = new Dictionary<string, Image<Rgba32>>();
@ -49,7 +71,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
/// <summary> /// <summary>
/// Gets the file names containing these strings are substrings are not processed by the benchmark. /// Gets the file names containing these strings are substrings are not processed by the benchmark.
/// </summary> /// </summary>
protected IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; protected virtual IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
/// <summary> /// <summary>
/// Enumerates folders containing files OR files to be processed by the benchmark. /// Enumerates folders containing files OR files to be processed by the benchmark.
@ -87,7 +109,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
protected abstract IEnumerable<string> InputImageSubfoldersOrFiles { get; } protected abstract IEnumerable<string> InputImageSubfoldersOrFiles { get; }
[GlobalSetup] [GlobalSetup]
public void ReadImages() public virtual void Setup()
{ {
if (!Vector.IsHardwareAccelerated) if (!Vector.IsHardwareAccelerated)
{ {
@ -107,11 +129,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
continue; continue;
} }
string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray();
string[] allFiles = string[] allFiles =
this.SearchPatterns.SelectMany( this.SearchPatterns.SelectMany(
f => f =>
Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) Directory.EnumerateFiles(path, f, SearchOption.AllDirectories)
.Where(fn => !this.ExcludeSubstringsInFileNames.Any(w => fn.ToLower().Contains(w)))).ToArray(); .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray();
foreach (string fn in allFiles) foreach (string fn in allFiles)
{ {

74
tests/ImageSharp.Benchmarks/Samplers/Resize.cs

@ -22,15 +22,17 @@ namespace SixLabors.ImageSharp.Benchmarks
private Bitmap sourceBitmap; private Bitmap sourceBitmap;
public const int SourceSize = 3032; [Params(3032)]
public int SourceSize { get; set; }
public const int DestSize = 400; [Params(400)]
public int DestSize { get; set; }
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
this.sourceImage = new Image<Rgba32>(this.Configuration, SourceSize, SourceSize); this.sourceImage = new Image<Rgba32>(this.Configuration, this.SourceSize, this.SourceSize);
this.sourceBitmap = new Bitmap(SourceSize, SourceSize); this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize);
} }
[GlobalCleanup] [GlobalCleanup]
@ -43,14 +45,17 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public int SystemDrawing() public int SystemDrawing()
{ {
using (var destination = new Bitmap(DestSize, DestSize)) using (var destination = new Bitmap(this.DestSize, this.DestSize))
{ {
using (var graphics = Graphics.FromImage(destination)) using (var g = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; g.CompositingMode = CompositingMode.SourceCopy;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(this.sourceBitmap, 0, 0, DestSize, DestSize); g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize);
} }
return destination.Width; return destination.Width;
@ -83,15 +88,60 @@ namespace SixLabors.ImageSharp.Benchmarks
{ {
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx) protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{ {
ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic); ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
} }
// RESULTS (2018 October):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B |
} }
public class Resize_BicubicCompand : ResizeBenchmarkBase public class Resize_BicubicCompand : ResizeBenchmarkBase
{ {
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx) protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{ {
ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic, true); ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true);
} }
// RESULTS (2018 October):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B |
} }
} }

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

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

5
tests/ImageSharp.Sandbox46/Program.cs

@ -4,6 +4,7 @@
// </copyright> // </copyright>
using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations;
using SixLabors.ImageSharp.Tests.ProfilingBenchmarks;
namespace SixLabors.ImageSharp.Sandbox46 namespace SixLabors.ImageSharp.Sandbox46
{ {
@ -62,8 +63,8 @@ namespace SixLabors.ImageSharp.Sandbox46
private static void RunDecodeJpegProfilingTests() private static void RunDecodeJpegProfilingTests()
{ {
Console.WriteLine("RunDecodeJpegProfilingTests..."); Console.WriteLine("RunDecodeJpegProfilingTests...");
var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput()); var benchmarks = new JpegBenchmarks(new ConsoleOutput());
foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) foreach (object[] data in JpegBenchmarks.DecodeJpegData)
{ {
string fileName = (string)data[0]; string fileName = (string)data[0];
benchmarks.DecodeJpeg(fileName); benchmarks.DecodeJpeg(fileName);

21
tests/ImageSharp.Tests/Common/SimdUtilsTests.cs

@ -257,6 +257,27 @@ namespace SixLabors.ImageSharp.Tests.Common
); );
} }
[Theory]
[InlineData(1234)]
public void ExtendedIntrinsics_ConvertToSingle(short scale)
{
int n = Vector<float>.Count;
short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale);
float[] fData = sData.Select(u => (float)u).ToArray();
var source = new Vector<short>(sData);
var expected1 = new Vector<float>(fData, 0);
var expected2 = new Vector<float>(fData, n);
// Act:
SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector<float> actual1, out Vector<float> actual2);
// Assert:
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
[Theory] [Theory]
[MemberData(nameof(ArbitraryArraySizes))] [MemberData(nameof(ArbitraryArraySizes))]
public void BulkConvertNormalizedFloatToByteClampOverflows(int count) public void BulkConvertNormalizedFloatToByteClampOverflows(int count)

35
tests/ImageSharp.Tests/ComplexIntegrationTests.cs

@ -1,35 +0,0 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
/// <summary>
/// Might be useful to catch complex bugs
/// </summary>
public class ComplexIntegrationTests
{
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)]
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)]
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)]
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)]
public void LoadResizeSave<TPixel>(TestImageProvider<TPixel> provider, int quality, JpegSubsample subsample)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })))
{
image.MetaData.ExifProfile = null; // Reduce the size of the file
JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality };
provider.Utility.TestName += $"{subsample}_Q{quality}";
provider.Utility.SaveTestOutputFile(image, "png");
provider.Utility.SaveTestOutputFile(image, "jpg", options);
}
}
}
}

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

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

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

@ -1,20 +1,20 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit; using Xunit;
using System.IO;
using SixLabors.ImageSharp.Advanced;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Gif namespace SixLabors.ImageSharp.Tests.Formats.Gif
{ {
using System.Collections.Generic;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
public class GifDecoderTests public class GifDecoderTests
{ {
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; 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] [Theory]
[MemberData(nameof(RatioFiles))] [MemberData(nameof(RatioFiles))]
public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) 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 public class GifEncoderTests
{ {
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; 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 = public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit> 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 // Use the palette quantizer without dithering to ensure results
// are consistant // are consistant
Quantizer = new PaletteQuantizer(false) Quantizer = new WebSafePaletteQuantizer(false)
}; };
// Always save as we need to compare the encoded output. // Always save as we need to compare the encoded output.

22
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

@ -7,14 +7,16 @@
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
public partial class Block8x8FTests : JpegFixture public partial class Block8x8FTests
{ {
public class CopyToBufferArea : JpegFixture public class CopyToBufferArea : JpegFixture
{ {
@ -37,17 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
} }
// TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. [Fact]
[Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] public void Copy1x1Scale()
//[Fact]
public void Unscaled()
{ {
Block8x8F block = CreateRandomFloatBlock(0, 100); Block8x8F block = CreateRandomFloatBlock(0, 100);
using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20)) using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean))
{ {
BufferArea<float> area = buffer.GetArea(5, 10, 8, 8); BufferArea<float> area = buffer.GetArea(5, 10, 8, 8);
block.CopyTo(area); block.Copy1x1Scale(area);
Assert.Equal(block[0, 0], buffer[5, 10]); Assert.Equal(block[0, 0], buffer[5, 10]);
Assert.Equal(block[1, 0], buffer[6, 10]); Assert.Equal(block[1, 0], buffer[6, 10]);
@ -59,22 +59,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
} }
// TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. [Theory]
[Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")]
//[Theory]
[InlineData(1, 1)] [InlineData(1, 1)]
[InlineData(1, 2)] [InlineData(1, 2)]
[InlineData(2, 1)] [InlineData(2, 1)]
[InlineData(2, 2)] [InlineData(2, 2)]
[InlineData(4, 2)] [InlineData(4, 2)]
[InlineData(4, 4)] [InlineData(4, 4)]
public void Scaled(int horizontalFactor, int verticalFactor) public void CopyTo(int horizontalFactor, int verticalFactor)
{ {
Block8x8F block = CreateRandomFloatBlock(0, 100); Block8x8F block = CreateRandomFloatBlock(0, 100);
var start = new Point(50, 50); var start = new Point(50, 50);
using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100)) using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100, AllocationOptions.Clean))
{ {
BufferArea<float> area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); BufferArea<float> area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor);
block.CopyTo(area, horizontalFactor, verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor);

42
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -408,5 +408,47 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(original[i] * 42f, actual[i]); Assert.Equal(original[i] * 42f, actual[i]);
} }
} }
[Fact]
public void LoadFromUInt16Scalar()
{
if (this.SkipOnNonAvx2Runner())
{
return;
}
short[] data = Create8x8ShortData();
var source = new Block8x8(data);
Block8x8F dest = default;
dest.LoadFromInt16Scalar(ref source);
for (int i = 0; i < Block8x8F.Size; i++)
{
Assert.Equal((float)data[i], dest[i]);
}
}
[Fact]
public void LoadFromUInt16ExtendedAvx2()
{
if (this.SkipOnNonAvx2Runner())
{
return;
}
short[] data = Create8x8ShortData();
var source = new Block8x8(data);
Block8x8F dest = default;
dest.LoadFromInt16ExtendedAvx2(ref source);
for (int i = 0; i < Block8x8F.Size; i++)
{
Assert.Equal((float)data[i], dest[i]);
}
}
} }
} }

7
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -58,7 +58,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{ {
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{ {
result[i * 8 + j] = (short)(i * 10 + j); short val = (short)(i * 10 + j);
if ((i + j) % 2 == 0)
{
val *= -1;
}
result[i * 8 + j] = val;
} }
} }
return result; return result;

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs

@ -67,9 +67,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
for (int y = 0; y < result.HeightInBlocks; y++) for (int y = 0; y < result.HeightInBlocks; y++)
{ {
Span<Block8x8> blockRow = c.SpectralBlocks.GetRowSpan(y);
for (int x = 0; x < result.WidthInBlocks; x++) for (int x = 0; x < result.WidthInBlocks; x++)
{ {
short[] data = c.GetBlockReference(x, y).ToArray(); short[] data = blockRow[x].ToArray();
result.MakeBlock(data, y, x); result.MakeBlock(data, y, x);
} }
} }

25
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Memory;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
} }
[Fact] [Fact]
public void Configuration_Width_Height_BackroundColor() public void Configuration_Width_Height_BackgroundColor()
{ {
Configuration configuration = Configuration.Default.Clone(); Configuration configuration = Configuration.Default.Clone();
Rgba32 color = Rgba32.Aquamarine; Rgba32 color = Rgba32.Aquamarine;
@ -61,6 +64,26 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(configuration, image.GetConfiguration()); Assert.Equal(configuration, image.GetConfiguration());
} }
} }
[Fact]
public void CreateUninitialized()
{
Configuration configuration = Configuration.Default.Clone();
byte dirtyValue = 123;
configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue);
var metadata = new ImageMetaData();
using (Image<Gray8> image = Image.CreateUninitialized<Gray8>(configuration, 21, 22, metadata))
{
Assert.Equal(21, image.Width);
Assert.Equal(22, image.Height);
Assert.Same(configuration, image.GetConfiguration());
Assert.Same(metadata, image.MetaData);
Assert.Equal(dirtyValue, image[5, 5].PackedValue);
}
}
} }
} }
} }

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

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

Loading…
Cancel
Save