Browse Source

Merge remote-tracking branch 'origin/master' into af/resize-sandbox

# Conflicts:
#	tests/ImageSharp.Benchmarks/General/ArrayCopy.cs
af/merge-core
Anton Firszov 7 years ago
parent
commit
eb21b1dd6e
  1. 24
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  2. 13
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  3. 7
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 5
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
  5. 3
      src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs
  6. 2
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs
  7. 416
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
  8. 9
      tests/ImageSharp.Benchmarks/BenchmarkBase.cs
  9. 2
      tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs
  10. 4
      tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs
  11. 1
      tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs
  12. 4
      tests/ImageSharp.Benchmarks/Codecs/ImageBenchmarkTests.cs
  13. 40
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
  14. 8
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
  15. 18
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
  16. 18
      tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
  17. 12
      tests/ImageSharp.Benchmarks/Color/ColorEquality.cs
  18. 14
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs
  19. 15
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs
  20. 15
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs
  21. 14
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs
  22. 32
      tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs
  23. 15
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  24. 17
      tests/ImageSharp.Benchmarks/Config.cs
  25. 38
      tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
  26. 51
      tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
  27. 35
      tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
  28. 26
      tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
  29. 32
      tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs
  30. 49
      tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs
  31. 24
      tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs
  32. 28
      tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
  33. 14
      tests/ImageSharp.Benchmarks/General/Array2D.cs
  34. 14
      tests/ImageSharp.Benchmarks/General/ArrayReverse.cs
  35. 4
      tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs
  36. 5
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs
  37. 2
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs
  38. 10
      tests/ImageSharp.Benchmarks/General/Vector4Constants.cs
  39. 8
      tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs
  40. 12
      tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs
  41. 14
      tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs
  42. 9
      tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs
  43. 8
      tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs
  44. 10
      tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs
  45. 6
      tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs
  46. 12
      tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs
  47. 11
      tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs
  48. 6
      tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
  49. 13
      tests/ImageSharp.Benchmarks/Program.cs
  50. 42
      tests/ImageSharp.Benchmarks/Samplers/Crop.cs
  51. 6
      tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs
  52. 13
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  53. 1
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  54. 5
      tests/ImageSharp.Benchmarks/Samplers/Rotate.cs
  55. 5
      tests/ImageSharp.Benchmarks/Samplers/Skew.cs
  56. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  57. 168
      tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
  58. 2
      tests/ImageSharp.Tests/TestImages.cs
  59. 3
      tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg
  60. 3
      tests/Images/Input/Png/low-variance.png

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

@ -95,8 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette. // Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = QuantizedFrame<TPixel> quantized = null;
this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()).QuantizeFrame(image.Frames.RootFrame); using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
{
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
}
// Get the number of bits. // Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
@ -133,7 +136,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Clean up. // Clean up.
quantized?.Dispose(); quantized?.Dispose();
quantized = null;
// TODO: Write extension etc // TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer); stream.WriteByte(GifConstants.EndIntroducer);
@ -158,7 +160,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
else else
{ {
using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame)) using (IFrameQuantizer<TPixel> palleteFrameQuantizer = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()))
using (QuantizedFrame<TPixel> paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame))
{ {
this.WriteImageData(paletteQuantized, stream); this.WriteImageData(paletteQuantized, stream);
} }
@ -181,14 +184,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength
&& frameMetadata.ColorTableLength > 0) && frameMetadata.ColorTableLength > 0)
{ {
quantized = this.quantizer.CreateFrameQuantizer<TPixel>( using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration(), frameMetadata.ColorTableLength))
image.GetConfiguration(), {
frameMetadata.ColorTableLength).QuantizeFrame(frame); quantized = frameQuantizer.QuantizeFrame(frame);
}
} }
else else
{ {
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()) using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
.QuantizeFrame(frame); {
quantized = frameQuantizer.QuantizeFrame(frame);
}
} }
} }

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

@ -401,15 +401,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (this.ComponentCount == 3) if (this.ComponentCount == 3)
{ {
if (this.adobe.Equals(default) || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.YCbCr;
}
if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{ {
return JpegColorSpace.RGB; return JpegColorSpace.RGB;
} }
// Some images are poorly encoded and contain incorrect colorspace transform metadata.
// We ignore that and always fall back to the default colorspace.
return JpegColorSpace.YCbCr;
} }
if (this.ComponentCount == 4) if (this.ComponentCount == 4)
@ -419,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
: JpegColorSpace.Cmyk; : JpegColorSpace.Cmyk;
} }
JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}"); JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {this.ComponentCount}");
return default; return default;
} }

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

@ -243,8 +243,11 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
// Create quantized frame returning the palette and set the bit depth. // Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()) using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
.QuantizeFrame(image.Frames.RootFrame); {
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
}
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
bits = Math.Max(bits, quantizedBits); bits = Math.Max(bits, quantizedBits);

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

@ -98,6 +98,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
return quantizedFrame; return quantizedFrame;
} }
/// <inheritdoc/>
public virtual void Dispose()
{
}
/// <summary> /// <summary>
/// Execute the first pass through the pixels in the image to create the palette. /// Execute the first pass through the pixels in the image to create the palette.
/// </summary> /// </summary>

3
src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs

@ -1,6 +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.
using System;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering;
@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Provides methods to allow the execution of the quantization process on an image frame. /// Provides methods to allow the execution of the quantization process on an image frame.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IFrameQuantizer<TPixel> public interface IFrameQuantizer<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>

2
src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
IFrameQuantizer<TPixel> executor = this.Quantizer.CreateFrameQuantizer<TPixel>(configuration); using (IFrameQuantizer<TPixel> executor = this.Quantizer.CreateFrameQuantizer<TPixel>(configuration))
using (QuantizedFrame<TPixel> quantized = executor.QuantizeFrame(source)) using (QuantizedFrame<TPixel> quantized = executor.QuantizeFrame(source))
{ {
int paletteCount = quantized.Palette.Length - 1; int paletteCount = quantized.Palette.Length - 1;

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

@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
// TODO: Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case?
// (T, R, G, B, A, M2) could be grouped together! Investigate a ColorMoment struct.
namespace SixLabors.ImageSharp.Processing.Processors.Quantization namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
/// <summary> /// <summary>
@ -36,20 +38,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
internal sealed class WuFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel> internal sealed class WuFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// TODO: The WuFrameQuantizer<TPixel> code is rising several questions: // The following two variables determine the amount of bits to preserve when calculating the histogram.
// - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes ) JS. I'm afraid so. // Reducing the value of these numbers the granularity of the color maps produced, making it much faster
// - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case? // and using much less memory but potentially less accurate. Current results are very good though!
// (T, R, G, B, A, M2) could be grouped together!
// - It's a frequently used class, we need tests! (So we can optimize safely.) There are tests in the original!!! We should just adopt them!
// https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant/blob/master/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant.Tests/WuColorQuantizerTests.cs
/// <summary> /// <summary>
/// The index bits. /// The index bits. 6 in original code.
/// </summary> /// </summary>
private const int IndexBits = 5; private const int IndexBits = 5;
/// <summary> /// <summary>
/// The index alpha bits. Keep separate for now to allow easy adjustment. /// The index alpha bits. 3 in original code.
/// </summary> /// </summary>
private const int IndexAlphaBits = 5; private const int IndexAlphaBits = 5;
@ -64,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1; private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1;
/// <summary> /// <summary>
/// The table length. Now 1185921. /// The table length. Now 1185921. originally 2471625.
/// </summary> /// </summary>
private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount;
@ -96,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary> /// <summary>
/// Moment of <c>c^2*P(c)</c>. /// Moment of <c>c^2*P(c)</c>.
/// </summary> /// </summary>
private IMemoryOwner<float> m2; private IMemoryOwner<double> m2;
/// <summary> /// <summary>
/// Color space tag. /// Color space tag.
@ -149,28 +148,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
MemoryAllocator memoryAllocator = image.MemoryAllocator; MemoryAllocator memoryAllocator = image.MemoryAllocator;
try this.vwt = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean);
{ this.vmr = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean);
this.vwt = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); this.vmg = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean);
this.vmr = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); this.vmb = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean);
this.vmg = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); this.vma = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean);
this.vmb = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); this.m2 = memoryAllocator.Allocate<double>(TableLength, AllocationOptions.Clean);
this.vma = memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); this.tag = memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean);
this.m2 = memoryAllocator.Allocate<float>(TableLength, AllocationOptions.Clean);
this.tag = memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean); return base.QuantizeFrame(image);
}
return base.QuantizeFrame(image);
} /// <inheritdoc/>
finally public override void Dispose()
{ {
this.vwt?.Dispose(); this.vwt?.Dispose();
this.vmr?.Dispose(); this.vmr?.Dispose();
this.vmg?.Dispose(); this.vmg?.Dispose();
this.vmb?.Dispose(); this.vmb?.Dispose();
this.vma?.Dispose(); this.vma?.Dispose();
this.m2?.Dispose(); this.m2?.Dispose();
this.tag?.Dispose(); this.tag?.Dispose();
}
} }
internal TPixel[] AotGetPalette() => this.GetPalette(); internal TPixel[] AotGetPalette() => this.GetPalette();
@ -275,9 +273,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetPaletteIndex(int r, int g, int b, int a) private static int GetPaletteIndex(int r, int g, int b, int a)
{ {
return (r << ((IndexBits * 2) + IndexAlphaBits)) + (r << (IndexBits + IndexAlphaBits + 1)) return (r << ((IndexBits * 2) + IndexAlphaBits))
+ (g << (IndexBits + IndexAlphaBits)) + (r << (IndexBits * 2)) + (r << (IndexBits + 1)) + (r << (IndexBits + IndexAlphaBits + 1))
+ (g << IndexBits) + ((r + g + b) << IndexAlphaBits) + r + g + b + a; + (g << (IndexBits + IndexAlphaBits))
+ (r << (IndexBits * 2))
+ (r << (IndexBits + 1))
+ (g << IndexBits)
+ ((r + g + b) << IndexAlphaBits)
+ r + g + b + a;
} }
/// <summary> /// <summary>
@ -288,26 +291,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <returns>The result.</returns> /// <returns>The result.</returns>
private static float Volume(ref Box cube, Span<long> moment) private static float Volume(ref Box cube, Span<long> moment)
{ {
return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] return moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
} }
/// <summary> /// <summary>
/// Computes part of Volume(cube, moment) that doesn't depend on r1, g1, or b1 (depending on direction). /// Computes part of Volume(cube, moment) that doesn't depend on RMax, GMax, BMax, or AMax (depending on direction).
/// </summary> /// </summary>
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <param name="direction">The direction.</param> /// <param name="direction">The direction.</param>
@ -319,47 +322,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
// Red // Red
case 3: case 3:
return -moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] return -moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
// Green // Green
case 2: case 2:
return -moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] return -moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
// Blue // Blue
case 1: case 1:
return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] return -moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
// Alpha // Alpha
case 0: case 0:
return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] return -moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
default: default:
throw new ArgumentOutOfRangeException(nameof(direction)); throw new ArgumentOutOfRangeException(nameof(direction));
@ -367,7 +370,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
} }
/// <summary> /// <summary>
/// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction). /// Computes remainder of Volume(cube, moment), substituting position for RMax, GMax, BMax, or AMax (depending on direction).
/// </summary> /// </summary>
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <param name="direction">The direction.</param> /// <param name="direction">The direction.</param>
@ -380,47 +383,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
// Red // Red
case 3: case 3:
return moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A1)] return moment[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A0)] - moment[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A1)] - moment[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(position, cube.G0, cube.B1, cube.A1)] - moment[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(position, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)]; - moment[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMin)];
// Green // Green
case 2: case 2:
return moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A1)] return moment[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMin)]
- moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, position, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, position, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)]; - moment[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMin)];
// Blue // Blue
case 1: case 1:
return moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A1)] return moment[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMax)]
- moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A0)] - moment[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMin)]
- moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A1)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMax)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A0)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMin)]
- moment[GetPaletteIndex(cube.R0, cube.G1, position, cube.A1)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMax)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, position, cube.A0)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMin)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A1)] + moment[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMax)]
- moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)]; - moment[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMin)];
// Alpha // Alpha
case 0: case 0:
return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, position)] return moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, position)]
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, position)] - moment[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, position)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, position)] - moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, position)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, position)] + moment[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, position)]
- moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, position)] - moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, position)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, position)] + moment[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, position)]
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B1, position)] + moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, position)]
- moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, position)]; - moment[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, position)];
default: default:
throw new ArgumentOutOfRangeException(nameof(direction)); throw new ArgumentOutOfRangeException(nameof(direction));
@ -440,7 +443,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Span<long> vmgSpan = this.vmg.GetSpan(); Span<long> vmgSpan = this.vmg.GetSpan();
Span<long> vmbSpan = this.vmb.GetSpan(); Span<long> vmbSpan = this.vmb.GetSpan();
Span<long> vmaSpan = this.vma.GetSpan(); Span<long> vmaSpan = this.vma.GetSpan();
Span<float> m2Span = this.m2.GetSpan(); Span<double> m2Span = this.m2.GetSpan();
// Build up the 3-D color histogram // Build up the 3-D color histogram
// Loop through each row // Loop through each row
@ -489,34 +492,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Span<long> vmgSpan = this.vmg.GetSpan(); Span<long> vmgSpan = this.vmg.GetSpan();
Span<long> vmbSpan = this.vmb.GetSpan(); Span<long> vmbSpan = this.vmb.GetSpan();
Span<long> vmaSpan = this.vma.GetSpan(); Span<long> vmaSpan = this.vma.GetSpan();
Span<float> m2Span = this.m2.GetSpan(); Span<double> m2Span = this.m2.GetSpan();
using (IMemoryOwner<long> volume = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<long> volume = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeR = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<long> volumeR = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeG = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<long> volumeG = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeB = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<long> volumeB = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeA = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<long> volumeA = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<float> volume2 = memoryAllocator.Allocate<float>(IndexCount * IndexAlphaCount)) using (IMemoryOwner<double> volume2 = memoryAllocator.Allocate<double>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> area = memoryAllocator.Allocate<long>(IndexAlphaCount)) using (IMemoryOwner<long> area = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaR = memoryAllocator.Allocate<long>(IndexAlphaCount)) using (IMemoryOwner<long> areaR = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaG = memoryAllocator.Allocate<long>(IndexAlphaCount)) using (IMemoryOwner<long> areaG = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaB = memoryAllocator.Allocate<long>(IndexAlphaCount)) using (IMemoryOwner<long> areaB = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaA = memoryAllocator.Allocate<long>(IndexAlphaCount)) using (IMemoryOwner<long> areaA = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<float> area2 = memoryAllocator.Allocate<float>(IndexAlphaCount)) using (IMemoryOwner<double> area2 = memoryAllocator.Allocate<double>(IndexAlphaCount))
{ {
Span<long> volumeSpan = volume.GetSpan(); Span<long> volumeSpan = volume.GetSpan();
Span<long> volumeRSpan = volumeR.GetSpan(); Span<long> volumeRSpan = volumeR.GetSpan();
Span<long> volumeGSpan = volumeG.GetSpan(); Span<long> volumeGSpan = volumeG.GetSpan();
Span<long> volumeBSpan = volumeB.GetSpan(); Span<long> volumeBSpan = volumeB.GetSpan();
Span<long> volumeASpan = volumeA.GetSpan(); Span<long> volumeASpan = volumeA.GetSpan();
Span<float> volume2Span = volume2.GetSpan(); Span<double> volume2Span = volume2.GetSpan();
Span<long> areaSpan = area.GetSpan(); Span<long> areaSpan = area.GetSpan();
Span<long> areaRSpan = areaR.GetSpan(); Span<long> areaRSpan = areaR.GetSpan();
Span<long> areaGSpan = areaG.GetSpan(); Span<long> areaGSpan = areaG.GetSpan();
Span<long> areaBSpan = areaB.GetSpan(); Span<long> areaBSpan = areaB.GetSpan();
Span<long> areaASpan = areaA.GetSpan(); Span<long> areaASpan = areaA.GetSpan();
Span<float> area2Span = area2.GetSpan(); Span<double> area2Span = area2.GetSpan();
for (int r = 1; r < IndexCount; r++) for (int r = 1; r < IndexCount; r++)
{ {
@ -543,7 +546,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
long lineG = 0; long lineG = 0;
long lineB = 0; long lineB = 0;
long lineA = 0; long lineA = 0;
float line2 = 0; double line2 = 0;
for (int a = 1; a < IndexAlphaCount; a++) for (int a = 1; a < IndexAlphaCount; a++)
{ {
@ -592,35 +595,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <returns>The <see cref="float"/>.</returns> /// <returns>The <see cref="float"/>.</returns>
private float Variance(ref Box cube) private double Variance(ref Box cube)
{ {
float dr = Volume(ref cube, this.vmr.GetSpan()); float dr = Volume(ref cube, this.vmr.GetSpan());
float dg = Volume(ref cube, this.vmg.GetSpan()); float dg = Volume(ref cube, this.vmg.GetSpan());
float db = Volume(ref cube, this.vmb.GetSpan()); float db = Volume(ref cube, this.vmb.GetSpan());
float da = Volume(ref cube, this.vma.GetSpan()); float da = Volume(ref cube, this.vma.GetSpan());
Span<float> m2Span = this.m2.GetSpan(); Span<double> m2Span = this.m2.GetSpan();
float xx = double moment =
m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)]
- m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)]
- m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + m2Span[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)]
- m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)]
- m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - m2Span[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)]
- m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)]
- m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - m2Span[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)]
- m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)]
- m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + m2Span[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)];
var vector = new Vector4(dr, dg, db, da); var vector = new Vector4(dr, dg, db, da);
return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.GetSpan())); return moment - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.GetSpan()));
} }
/// <summary> /// <summary>
@ -714,10 +717,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
float wholeA = Volume(ref set1, this.vma.GetSpan()); float wholeA = Volume(ref set1, this.vma.GetSpan());
float wholeW = Volume(ref set1, this.vwt.GetSpan()); float wholeW = Volume(ref set1, this.vwt.GetSpan());
float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); float maxr = this.Maximize(ref set1, 3, set1.RMin + 1, set1.RMax, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); float maxg = this.Maximize(ref set1, 2, set1.GMin + 1, set1.GMax, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxb = this.Maximize(ref set1, 1, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); float maxb = this.Maximize(ref set1, 1, set1.BMin + 1, set1.BMax, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxa = this.Maximize(ref set1, 0, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); float maxa = this.Maximize(ref set1, 0, set1.AMin + 1, set1.AMax, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW);
int dir; int dir;
@ -743,48 +746,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
dir = 0; dir = 0;
} }
set2.R1 = set1.R1; set2.RMax = set1.RMax;
set2.G1 = set1.G1; set2.GMax = set1.GMax;
set2.B1 = set1.B1; set2.BMax = set1.BMax;
set2.A1 = set1.A1; set2.AMax = set1.AMax;
switch (dir) switch (dir)
{ {
// Red // Red
case 3: case 3:
set2.R0 = set1.R1 = cutr; set2.RMin = set1.RMax = cutr;
set2.G0 = set1.G0; set2.GMin = set1.GMin;
set2.B0 = set1.B0; set2.BMin = set1.BMin;
set2.A0 = set1.A0; set2.AMin = set1.AMin;
break; break;
// Green // Green
case 2: case 2:
set2.G0 = set1.G1 = cutg; set2.GMin = set1.GMax = cutg;
set2.R0 = set1.R0; set2.RMin = set1.RMin;
set2.B0 = set1.B0; set2.BMin = set1.BMin;
set2.A0 = set1.A0; set2.AMin = set1.AMin;
break; break;
// Blue // Blue
case 1: case 1:
set2.B0 = set1.B1 = cutb; set2.BMin = set1.BMax = cutb;
set2.R0 = set1.R0; set2.RMin = set1.RMin;
set2.G0 = set1.G0; set2.GMin = set1.GMin;
set2.A0 = set1.A0; set2.AMin = set1.AMin;
break; break;
// Alpha // Alpha
case 0: case 0:
set2.A0 = set1.A1 = cuta; set2.AMin = set1.AMax = cuta;
set2.R0 = set1.R0; set2.RMin = set1.RMin;
set2.G0 = set1.G0; set2.GMin = set1.GMin;
set2.B0 = set1.B0; set2.BMin = set1.BMin;
break; break;
} }
set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0); set1.Volume = (set1.RMax - set1.RMin) * (set1.GMax - set1.GMin) * (set1.BMax - set1.BMin) * (set1.AMax - set1.AMin);
set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0); set2.Volume = (set2.RMax - set2.RMin) * (set2.GMax - set2.GMin) * (set2.BMax - set2.BMin) * (set2.AMax - set2.AMin);
return true; return true;
} }
@ -798,13 +801,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
Span<byte> tagSpan = this.tag.GetSpan(); Span<byte> tagSpan = this.tag.GetSpan();
for (int r = cube.R0 + 1; r <= cube.R1; r++) for (int r = cube.RMin + 1; r <= cube.RMax; r++)
{ {
for (int g = cube.G0 + 1; g <= cube.G1; g++) for (int g = cube.GMin + 1; g <= cube.GMax; g++)
{ {
for (int b = cube.B0 + 1; b <= cube.B1; b++) for (int b = cube.BMin + 1; b <= cube.BMax; b++)
{ {
for (int a = cube.A0 + 1; a <= cube.A1; a++) for (int a = cube.AMin + 1; a <= cube.AMax; a++)
{ {
tagSpan[GetPaletteIndex(r, g, b, a)] = label; tagSpan[GetPaletteIndex(r, g, b, a)] = label;
} }
@ -819,12 +822,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private void BuildCube() private void BuildCube()
{ {
this.colorCube = new Box[this.colors]; this.colorCube = new Box[this.colors];
float[] vv = new float[this.colors]; double[] vv = new double[this.colors];
ref Box cube = ref this.colorCube[0]; ref Box cube = ref this.colorCube[0];
cube.R0 = cube.G0 = cube.B0 = cube.A0 = 0; cube.RMin = cube.GMin = cube.BMin = cube.AMin = 0;
cube.R1 = cube.G1 = cube.B1 = IndexCount - 1; cube.RMax = cube.GMax = cube.BMax = IndexCount - 1;
cube.A1 = IndexAlphaCount - 1; cube.AMax = IndexAlphaCount - 1;
int next = 0; int next = 0;
@ -839,13 +842,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
} }
else else
{ {
vv[next] = 0F; vv[next] = 0D;
i--; i--;
} }
next = 0; next = 0;
float temp = vv[0]; double temp = vv[0];
for (int k = 1; k <= i; k++) for (int k = 1; k <= i; k++)
{ {
if (vv[k] > temp) if (vv[k] > temp)
@ -855,7 +858,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
} }
} }
if (temp <= 0F) if (temp <= 0D)
{ {
this.colors = i + 1; this.colors = i + 1;
break; break;
@ -897,52 +900,83 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary> /// <summary>
/// Represents a box color cube. /// Represents a box color cube.
/// </summary> /// </summary>
private struct Box private struct Box : IEquatable<Box>
{ {
/// <summary> /// <summary>
/// Gets or sets the min red value, exclusive. /// Gets or sets the min red value, exclusive.
/// </summary> /// </summary>
public int R0; public int RMin;
/// <summary> /// <summary>
/// Gets or sets the max red value, inclusive. /// Gets or sets the max red value, inclusive.
/// </summary> /// </summary>
public int R1; public int RMax;
/// <summary> /// <summary>
/// Gets or sets the min green value, exclusive. /// Gets or sets the min green value, exclusive.
/// </summary> /// </summary>
public int G0; public int GMin;
/// <summary> /// <summary>
/// Gets or sets the max green value, inclusive. /// Gets or sets the max green value, inclusive.
/// </summary> /// </summary>
public int G1; public int GMax;
/// <summary> /// <summary>
/// Gets or sets the min blue value, exclusive. /// Gets or sets the min blue value, exclusive.
/// </summary> /// </summary>
public int B0; public int BMin;
/// <summary> /// <summary>
/// Gets or sets the max blue value, inclusive. /// Gets or sets the max blue value, inclusive.
/// </summary> /// </summary>
public int B1; public int BMax;
/// <summary> /// <summary>
/// Gets or sets the min alpha value, exclusive. /// Gets or sets the min alpha value, exclusive.
/// </summary> /// </summary>
public int A0; public int AMin;
/// <summary> /// <summary>
/// Gets or sets the max alpha value, inclusive. /// Gets or sets the max alpha value, inclusive.
/// </summary> /// </summary>
public int A1; public int AMax;
/// <summary> /// <summary>
/// Gets or sets the volume. /// Gets or sets the volume.
/// </summary> /// </summary>
public int Volume; public int Volume;
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Box box && this.Equals(box);
/// <inheritdoc/>
public bool Equals(Box other) =>
this.RMin == other.RMin
&& this.RMax == other.RMax
&& this.GMin == other.GMin
&& this.GMax == other.GMax
&& this.BMin == other.BMin
&& this.BMax == other.BMax
&& this.AMin == other.AMin
&& this.AMax == other.AMax
&& this.Volume == other.Volume;
/// <inheritdoc/>
public override int GetHashCode()
{
HashCode hash = default;
hash.Add(this.RMin);
hash.Add(this.RMax);
hash.Add(this.GMin);
hash.Add(this.GMax);
hash.Add(this.BMin);
hash.Add(this.BMax);
hash.Add(this.AMin);
hash.Add(this.AMax);
hash.Add(this.Volume);
return hash.ToHashCode();
}
} }
} }
} }

9
tests/ImageSharp.Benchmarks/BenchmarkBase.cs

@ -1,7 +1,8 @@
namespace SixLabors.ImageSharp.Benchmarks // Copyright (c) Six Labors and contributors.
{ // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
namespace SixLabors.ImageSharp.Benchmarks
{
/// <summary> /// <summary>
/// The image benchmark base class. /// The image benchmark base class.
/// </summary> /// </summary>
@ -15,4 +16,4 @@
// Add Image Formats // Add Image Formats
} }
} }
} }

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

@ -54,4 +54,4 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
} }
} }
} }
} }

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

@ -1,7 +1,5 @@
// <copyright file="DecodePng.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

1
tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs

@ -11,7 +11,6 @@ using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
[Config(typeof(Config.ShortClr))] [Config(typeof(Config.ShortClr))]
public class DecodePng : BenchmarkBase public class DecodePng : BenchmarkBase
{ {

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

@ -1,7 +1,5 @@
// <copyright file="ImageBenchmarkTests.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
// This file contains small, cheap and "unit test" benchmarks to test MultiImageBenchmarkBase. // This file contains small, cheap and "unit test" benchmarks to test MultiImageBenchmarkBase.
// Need this because there are no real test cases for the common benchmark utility stuff. // Need this because there are no real test cases for the common benchmark utility stuff.

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

@ -364,22 +364,30 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); 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)); ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X); var xyLeft = new Vector4(sLeft.X)
xyLeft.Z = sLeft.Y; {
xyLeft.W = sLeft.Y; Z = sLeft.Y,
W = sLeft.Y
var zwLeft = new Vector4(sLeft.Z); };
zwLeft.Z = sLeft.W;
zwLeft.W = sLeft.W; var zwLeft = new Vector4(sLeft.Z)
{
var xyRight = new Vector4(sRight.X); Z = sLeft.W,
xyRight.Z = sRight.Y; W = sLeft.W
xyRight.W = sRight.Y; };
var zwRight = new Vector4(sRight.Z); var xyRight = new Vector4(sRight.X)
zwRight.Z = sRight.W; {
zwRight.W = sRight.W; Z = sRight.Y,
W = sRight.Y
};
var zwRight = new Vector4(sRight.Z)
{
Z = sRight.W,
W = sRight.W
};
dTopLeft = xyLeft; dTopLeft = xyLeft;
Unsafe.Add(ref dTopLeft, 1) = zwLeft; Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref dTopLeft, 2) = xyRight; Unsafe.Add(ref dTopLeft, 2) = xyRight;

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

@ -5,6 +5,7 @@ using System.Drawing;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
@ -26,8 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
public Config() public Config()
{ {
// Uncomment if you want to use any of the diagnoser this.Add(MemoryDiagnoser.Default);
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
} }
public class ShortClr : Benchmarks.Config public class ShortClr : Benchmarks.Config
@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
public ShortClr() public ShortClr()
{ {
this.Add( this.Add(
//Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3), //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3),
Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3) Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3)
); );
} }
} }

18
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs

@ -1,18 +1,16 @@
// <copyright file="EncodeJpeg.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes;
using CoreImage = SixLabors.ImageSharp.Image; using CoreImage = SixLabors.ImageSharp.Image;
public class EncodeJpeg : BenchmarkBase public class EncodeJpeg : BenchmarkBase
@ -45,19 +43,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")]
public void JpegSystemDrawing() public void JpegSystemDrawing()
{ {
using (var memoryStream = new MemoryStream()) using (var stream = new MemoryStream())
{ {
this.bmpDrawing.Save(memoryStream, ImageFormat.Jpeg); this.bmpDrawing.Save(stream, ImageFormat.Jpeg);
} }
} }
[Benchmark(Description = "ImageSharp Jpeg")] [Benchmark(Description = "ImageSharp Jpeg")]
public void JpegCore() public void JpegCore()
{ {
using (var memoryStream = new MemoryStream()) using (var stream = new MemoryStream())
{ {
this.bmpCore.SaveAsJpeg(memoryStream); this.bmpCore.SaveAsJpeg(stream);
} }
} }
} }
} }

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

@ -1,7 +1,5 @@
// <copyright file="MultiImageBenchmarkBase.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
@ -18,7 +16,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using System.Numerics; using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using SixLabors.ImageSharp.Tests; using SixLabors.ImageSharp.Tests;
using CoreImage = ImageSharp.Image; using CoreImage = ImageSharp.Image;
@ -30,7 +28,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public Config() public Config()
{ {
// Uncomment if you want to use any of the diagnoser // Uncomment if you want to use any of the diagnoser
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); this.Add(MemoryDiagnoser.Default);
} }
public class ShortClr : Benchmarks.Config public class ShortClr : Benchmarks.Config
@ -38,7 +36,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public ShortClr() public ShortClr()
{ {
this.Add( this.Add(
Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2) Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithIterationCount(2)
); );
} }
} }
@ -47,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
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>>();
protected Dictionary<string, System.Drawing.Bitmap> FileNamesToSystemDrawingImages = new Dictionary<string, System.Drawing.Bitmap>(); protected Dictionary<string, Bitmap> FileNamesToSystemDrawingImages = new Dictionary<string, System.Drawing.Bitmap>();
/// <summary> /// <summary>
/// The values of this enum separate input files into categories /// The values of this enum separate input files into categories
@ -152,7 +150,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
foreach (KeyValuePair<string, byte[]> kv in this.FileNames2Bytes) foreach (KeyValuePair<string, byte[]> kv in this.FileNames2Bytes)
{ {
using (MemoryStream memoryStream = new MemoryStream(kv.Value)) using (var memoryStream = new MemoryStream(kv.Value))
{ {
try try
{ {
@ -179,7 +177,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
byte[] bytes = kv.Value; byte[] bytes = kv.Value;
string fn = kv.Key; string fn = kv.Key;
using (MemoryStream ms1 = new MemoryStream(bytes)) using (var ms1 = new MemoryStream(bytes))
{ {
this.FileNamesToImageSharpImages[fn] = CoreImage.Load<Rgba32>(ms1); this.FileNamesToImageSharpImages[fn] = CoreImage.Load<Rgba32>(ms1);
@ -223,7 +221,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
protected void ForEachImageSharpImage(Func<Image<Rgba32>, MemoryStream, object> operation) protected void ForEachImageSharpImage(Func<Image<Rgba32>, MemoryStream, object> operation)
{ {
using (MemoryStream workStream = new MemoryStream()) using (var workStream = new MemoryStream())
{ {
this.ForEachImageSharpImage( this.ForEachImageSharpImage(

12
tests/ImageSharp.Benchmarks/Color/ColorEquality.cs

@ -1,14 +1,12 @@
// <copyright file="ColorEquality.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks
{
using SystemColor = System.Drawing.Color; using SystemColor = System.Drawing.Color;
public class ColorEquality public class ColorEquality

14
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs

@ -1,13 +1,13 @@
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using Colourful; using Colourful;
using Colourful.Conversion; using Colourful.Conversion;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces
{
public class ColorspaceCieXyzToCieLabConvert public class ColorspaceCieXyzToCieLabConvert
{ {
private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F);

15
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs

@ -1,13 +1,13 @@
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using Colourful; using Colourful;
using Colourful.Conversion; using Colourful.Conversion;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces
{
public class ColorspaceCieXyzToHunterLabConvert public class ColorspaceCieXyzToHunterLabConvert
{ {
private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F);
@ -18,7 +18,6 @@
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter();
[Benchmark(Baseline = true, Description = "Colourful Convert")] [Benchmark(Baseline = true, Description = "Colourful Convert")]
public double ColourfulConvert() public double ColourfulConvert()
{ {

15
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs

@ -1,13 +1,13 @@
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using Colourful; using Colourful;
using Colourful.Conversion; using Colourful.Conversion;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces
{
public class ColorspaceCieXyzToLmsConvert public class ColorspaceCieXyzToLmsConvert
{ {
private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F);
@ -18,7 +18,6 @@
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter();
[Benchmark(Baseline = true, Description = "Colourful Convert")] [Benchmark(Baseline = true, Description = "Colourful Convert")]
public double ColourfulConvert() public double ColourfulConvert()
{ {

14
tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs

@ -1,13 +1,13 @@
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using Colourful; using Colourful;
using Colourful.Conversion; using Colourful.Conversion;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces
{
public class ColorspaceCieXyzToRgbConvert public class ColorspaceCieXyzToRgbConvert
{ {
private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F);

32
tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs

@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Benchmarks
OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB); OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
// On-stack output: // On-stack output:
Result result = default(Result); Result result = default;
float* yPtr = (float*)&result.Y; float* yPtr = (float*)&result.Y;
float* cbPtr = (float*)&result.Cb; float* cbPtr = (float*)&result.Cb;
float* crPtr = (float*)&result.Cr; float* crPtr = (float*)&result.Cr;
@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.Benchmarks
OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB); OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
// On-stack output: // On-stack output:
Result result = default(Result); Result result = default;
float* yPtr = (float*)&result.Y; float* yPtr = (float*)&result.Y;
float* cbPtr = (float*)&result.Cb; float* cbPtr = (float*)&result.Cb;
float* crPtr = (float*)&result.Cr; float* crPtr = (float*)&result.Cr;
@ -194,15 +194,15 @@ namespace SixLabors.ImageSharp.Benchmarks
// Copy the input to the stack: // Copy the input to the stack:
// On-stack output: // On-stack output:
Result result = default(Result); Result result = default;
float* yPtr = (float*)&result.Y; float* yPtr = (float*)&result.Y;
float* cbPtr = (float*)&result.Cb; float* cbPtr = (float*)&result.Cb;
float* crPtr = (float*)&result.Cr; float* crPtr = (float*)&result.Cr;
// end of code-bloat block :) // end of code-bloat block :)
Vector<int> yCoeffs = new Vector<int>(ScaledCoeffs.Y); var yCoeffs = new Vector<int>(ScaledCoeffs.Y);
Vector<int> cbCoeffs = new Vector<int>(ScaledCoeffs.Cb); var cbCoeffs = new Vector<int>(ScaledCoeffs.Cb);
Vector<int> crCoeffs = new Vector<int>(ScaledCoeffs.Cr); var crCoeffs = new Vector<int>(ScaledCoeffs.Cr);
for (int i = 0; i < this.inputSourceRGB.Length; i++) for (int i = 0; i < this.inputSourceRGB.Length; i++)
{ {
@ -240,23 +240,23 @@ namespace SixLabors.ImageSharp.Benchmarks
// Copy the input to the stack: // Copy the input to the stack:
// On-stack output: // On-stack output:
Result result = default(Result); Result result = default;
float* yPtr = (float*)&result.Y; float* yPtr = (float*)&result.Y;
float* cbPtr = (float*)&result.Cb; float* cbPtr = (float*)&result.Cb;
float* crPtr = (float*)&result.Cr; float* crPtr = (float*)&result.Cr;
// end of code-bloat block :) // end of code-bloat block :)
Vector<int> yCoeffs = new Vector<int>(ScaledCoeffs.Y); var yCoeffs = new Vector<int>(ScaledCoeffs.Y);
Vector<int> cbCoeffs = new Vector<int>(ScaledCoeffs.Cb); var cbCoeffs = new Vector<int>(ScaledCoeffs.Cb);
Vector<int> crCoeffs = new Vector<int>(ScaledCoeffs.Cr); var crCoeffs = new Vector<int>(ScaledCoeffs.Cr);
Vector<int> leftY = new Vector<int>(ScaledCoeffs.SelectLeft.Y); var leftY = new Vector<int>(ScaledCoeffs.SelectLeft.Y);
Vector<int> leftCb = new Vector<int>(ScaledCoeffs.SelectLeft.Cb); var leftCb = new Vector<int>(ScaledCoeffs.SelectLeft.Cb);
Vector<int> leftCr = new Vector<int>(ScaledCoeffs.SelectLeft.Cr); var leftCr = new Vector<int>(ScaledCoeffs.SelectLeft.Cr);
Vector<int> rightY = new Vector<int>(ScaledCoeffs.SelectRight.Y); var rightY = new Vector<int>(ScaledCoeffs.SelectRight.Y);
Vector<int> rightCb = new Vector<int>(ScaledCoeffs.SelectRight.Cb); var rightCb = new Vector<int>(ScaledCoeffs.SelectRight.Cb);
Vector<int> rightCr = new Vector<int>(ScaledCoeffs.SelectRight.Cr); var rightCr = new Vector<int>(ScaledCoeffs.SelectRight.Cr);
for (int i = 0; i < this.inputSourceRGB.Length; i++) for (int i = 0; i < this.inputSourceRGB.Length; i++)
{ {

15
tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs

@ -1,13 +1,13 @@
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces using BenchmarkDotNet.Attributes;
{
using BenchmarkDotNet.Attributes;
using Colourful; using Colourful;
using Colourful.Conversion; using Colourful.Conversion;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces
{
public class RgbWorkingSpaceAdapt public class RgbWorkingSpaceAdapt
{ {
private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb);
@ -18,7 +18,6 @@
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB }; private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB };
[Benchmark(Baseline = true, Description = "Colourful Adapt")] [Benchmark(Baseline = true, Description = "Colourful Adapt")]
public RGBColor ColourfulConvert() public RGBColor ColourfulConvert()
{ {

17
tests/ImageSharp.Benchmarks/Config.cs

@ -1,20 +1,17 @@
// <copyright file="Config.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Jobs;
namespace SixLabors.ImageSharp.Benchmarks namespace SixLabors.ImageSharp.Benchmarks
{ {
using BenchmarkDotNet.Jobs;
public class Config : ManualConfig public class Config : ManualConfig
{ {
public Config() public Config()
{ {
// Uncomment if you want to use any of the diagnoser this.Add(MemoryDiagnoser.Default);
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
} }
public class ShortClr : Config public class ShortClr : Config
@ -22,9 +19,9 @@ namespace SixLabors.ImageSharp.Benchmarks
public ShortClr() public ShortClr()
{ {
this.Add( this.Add(
Job.Clr.WithLaunchCount(1).WithWarmupCount(3).WithTargetCount(3), Job.Clr.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3),
Job.Core.WithLaunchCount(1).WithWarmupCount(3).WithTargetCount(3) Job.Core.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)
); );
} }
} }
} }

38
tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs

@ -1,7 +1,5 @@
// <copyright file="DrawBeziers.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -19,27 +17,25 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true, Description = "System.Drawing Draw Beziers")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Beziers")]
public void DrawPathSystemDrawing() public void DrawPathSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (Graphics graphics = Graphics.FromImage(destination)) using (var pen = new Pen(Color.HotPink, 10))
{ {
graphics.InterpolationMode = InterpolationMode.Default; graphics.DrawBeziers(pen, new[] {
graphics.SmoothingMode = SmoothingMode.AntiAlias; new PointF(10, 500),
using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) new PointF(30, 10),
{ new PointF(240, 30),
graphics.DrawBeziers(pen, new[] { new PointF(300, 500)
new PointF(10, 500), });
new PointF(30, 10),
new PointF(240, 30),
new PointF(300, 500)
});
}
} }
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
} }
} }
} }
@ -47,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Beziers")] [Benchmark(Description = "ImageSharp Draw Beziers")]
public void DrawLinesCore() public void DrawLinesCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.DrawBeziers( image.Mutate(x => x.DrawBeziers(
Rgba32.HotPink, Rgba32.HotPink,
@ -59,9 +55,9 @@ namespace SixLabors.ImageSharp.Benchmarks
new Vector2(300, 500) new Vector2(300, 500)
})); }));
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(stream);
} }
} }
} }

51
tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs

@ -1,44 +1,41 @@
// <copyright file="DrawLines.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks using System.Drawing;
{ using System.Drawing.Drawing2D;
using System.Drawing; using System.IO;
using System.Drawing.Drawing2D; using System.Numerics;
using System.IO;
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Benchmarks
{
public class DrawLines : BenchmarkBase public class DrawLines : BenchmarkBase
{ {
[Benchmark(Baseline = true, Description = "System.Drawing Draw Lines")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Lines")]
public void DrawPathSystemDrawing() public void DrawPathSystemDrawing()
{ {
using (var destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
using (var graphics = Graphics.FromImage(destination)) graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(Color.HotPink, 10))
{ {
graphics.InterpolationMode = InterpolationMode.Default; graphics.DrawLines(pen, new[] {
graphics.SmoothingMode = SmoothingMode.AntiAlias; new PointF(10, 10),
using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) new PointF(550, 50),
{ new PointF(200, 400)
graphics.DrawLines(pen, new[] { });
new PointF(10, 10),
new PointF(550, 50),
new PointF(200, 400)
});
}
} }
using (var ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
} }
} }
} }
@ -57,9 +54,9 @@ namespace SixLabors.ImageSharp.Benchmarks
new Vector2(200, 400) new Vector2(200, 400)
})); }));
using (var ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(stream);
} }
} }
} }

35
tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs

@ -1,7 +1,5 @@
// <copyright file="Crop.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -19,26 +17,23 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true, Description = "System.Drawing Draw Polygon")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Polygon")]
public void DrawPolygonSystemDrawing() public void DrawPolygonSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.Default;
using (Graphics graphics = Graphics.FromImage(destination)) graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(Color.HotPink, 10))
{ {
graphics.InterpolationMode = InterpolationMode.Default; graphics.DrawPolygon(pen, new[] {
graphics.SmoothingMode = SmoothingMode.AntiAlias; new PointF(10, 10),
using (var pen = new Pen(System.Drawing.Color.HotPink, 10)) new PointF(550, 50),
{ new PointF(200, 400)
graphics.DrawPolygon(pen, new[] { });
new PointF(10, 10),
new PointF(550, 50),
new PointF(200, 400)
});
}
} }
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
} }
} }
} }
@ -46,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Polygon")] [Benchmark(Description = "ImageSharp Draw Polygon")]
public void DrawPolygonCore() public void DrawPolygonCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.DrawPolygon( image.Mutate(x => x.DrawPolygon(
Rgba32.HotPink, Rgba32.HotPink,
@ -57,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks
new Vector2(200, 400) new Vector2(200, 400)
})); }));
using (MemoryStream ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(ms);
} }

26
tests/ImageSharp.Benchmarks/Drawing/DrawText.cs

@ -1,7 +1,5 @@
// <copyright file="Crop.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -14,11 +12,9 @@ using SixLabors.ImageSharp.Processing.Processors.Text;
namespace SixLabors.ImageSharp.Benchmarks namespace SixLabors.ImageSharp.Benchmarks
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
public class DrawText : BenchmarkBase public class DrawText : BenchmarkBase
{ {
[Params(10, 100)] [Params(10, 100)]
public int TextIterations { get; set; } public int TextIterations { get; set; }
public string TextPhrase { get; set; } = "Hello World"; public string TextPhrase { get; set; } = "Hello World";
@ -28,26 +24,22 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true, Description = "System.Drawing Draw Text")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Text")]
public void DrawTextSystemDrawing() public void DrawTextSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.Default;
using (Graphics graphics = Graphics.FromImage(destination)) graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
{ {
graphics.InterpolationMode = InterpolationMode.Default; graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780));
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
{
graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780));
}
} }
} }
} }
[Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")] [Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")]
public void DrawTextCore() public void DrawTextCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))));
@ -57,7 +49,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Text - Nieve")] [Benchmark(Description = "ImageSharp Draw Text - Nieve")]
public void DrawTextCoreOld() public void DrawTextCoreOld()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)));

32
tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs

@ -1,7 +1,5 @@
// <copyright file="Crop.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -13,34 +11,28 @@ using SixLabors.ImageSharp.Processing.Processors.Text;
namespace SixLabors.ImageSharp.Benchmarks namespace SixLabors.ImageSharp.Benchmarks
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
public class DrawTextOutline : BenchmarkBase public class DrawTextOutline : BenchmarkBase
{ {
[Params(10, 100)] [Params(10, 100)]
public int TextIterations { get; set; } public int TextIterations { get; set; }
public string TextPhrase { get; set; } = "Hello World"; public string TextPhrase { get; set; } = "Hello World";
public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations)); public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations));
[Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")]
public void DrawTextSystemDrawing() public void DrawTextSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.Default;
using (Graphics graphics = Graphics.FromImage(destination)) graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(Color.HotPink, 10))
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
using (var gp = new GraphicsPath())
{ {
graphics.InterpolationMode = InterpolationMode.Default; gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat());
graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.DrawPath(pen, gp);
using (var pen = new Pen(System.Drawing.Color.HotPink, 10))
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
using (var gp = new GraphicsPath())
{
gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat());
graphics.DrawPath(pen, gp);
}
} }
} }
} }
@ -48,7 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Text Outline - Cached Glyphs")] [Benchmark(Description = "ImageSharp Draw Text Outline - Cached Glyphs")]
public void DrawTextCore() public void DrawTextCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))));
@ -58,7 +50,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Draw Text Outline - Nieve")] [Benchmark(Description = "ImageSharp Draw Text Outline - Nieve")]
public void DrawTextCoreOld() public void DrawTextCoreOld()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))); image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)));

49
tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs

@ -1,7 +1,5 @@
// <copyright file="FillPolygon.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -21,31 +19,30 @@ namespace SixLabors.ImageSharp.Benchmarks
public FillPolygon() public FillPolygon()
{ {
this.shape = new Polygon(new LinearLineSegment(new Vector2(10, 10), this.shape = new Polygon(new LinearLineSegment(
new Vector2(550, 50), new Vector2(10, 10),
new Vector2(200, 400))); new Vector2(550, 50),
new Vector2(200, 400)));
} }
[Benchmark(Baseline = true, Description = "System.Drawing Fill Polygon")] [Benchmark(Baseline = true, Description = "System.Drawing Fill Polygon")]
public void DrawSolidPolygonSystemDrawing() public void DrawSolidPolygonSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.FillPolygon(System.Drawing.Brushes.HotPink,
new[] {
new Point(10, 10),
new Point(550, 50),
new Point(200, 400)
});
using (Graphics graphics = Graphics.FromImage(destination)) using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.FillPolygon(System.Drawing.Brushes.HotPink,
new[]
{
new Point(10, 10),
new Point(550, 50),
new Point(200, 400)
});
}
using (MemoryStream ms = new MemoryStream())
{ {
destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
} }
} }
} }
@ -53,7 +50,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Polygon")] [Benchmark(Description = "ImageSharp Fill Polygon")]
public void DrawSolidPolygonCore() public void DrawSolidPolygonCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.FillPolygon( image.Mutate(x => x.FillPolygon(
Rgba32.HotPink, Rgba32.HotPink,
@ -63,9 +60,9 @@ namespace SixLabors.ImageSharp.Benchmarks
new Vector2(200, 400) new Vector2(200, 400)
})); }));
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(stream);
} }
} }
} }
@ -73,15 +70,15 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Polygon - cached shape")] [Benchmark(Description = "ImageSharp Fill Polygon - cached shape")]
public void DrawSolidPolygonCoreCahced() public void DrawSolidPolygonCoreCahced()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.Fill( image.Mutate(x => x.Fill(
Rgba32.HotPink, Rgba32.HotPink,
this.shape)); this.shape));
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(stream);
} }
} }
} }

24
tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs

@ -1,7 +1,5 @@
// <copyright file="Crop.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -15,22 +13,18 @@ using CoreSize = SixLabors.Primitives.Size;
namespace SixLabors.ImageSharp.Benchmarks namespace SixLabors.ImageSharp.Benchmarks
{ {
public class FillRectangle : BenchmarkBase public class FillRectangle : BenchmarkBase
{ {
[Benchmark(Baseline = true, Description = "System.Drawing Fill Rectangle")] [Benchmark(Baseline = true, Description = "System.Drawing Fill Rectangle")]
public Size FillRectangleSystemDrawing() public Size FillRectangleSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
graphics.InterpolationMode = InterpolationMode.Default;
using (Graphics graphics = Graphics.FromImage(destination)) graphics.SmoothingMode = SmoothingMode.AntiAlias;
{ graphics.FillRectangle(System.Drawing.Brushes.HotPink, new Rectangle(10, 10, 190, 140));
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.FillRectangle(System.Drawing.Brushes.HotPink, new Rectangle(10, 10, 190, 140));
}
return destination.Size; return destination.Size;
} }
} }
@ -38,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Rectangle")] [Benchmark(Description = "ImageSharp Fill Rectangle")]
public CoreSize FillRactangleCore() public CoreSize FillRactangleCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140))); image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140)));
@ -49,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")]
public CoreSize FillPolygonCore() public CoreSize FillPolygonCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.FillPolygon( image.Mutate(x => x.FillPolygon(
Rgba32.HotPink, Rgba32.HotPink,

28
tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs

@ -1,7 +1,5 @@
// <copyright file="FillWithPattern.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -21,19 +19,19 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true, Description = "System.Drawing Fill with Pattern")] [Benchmark(Baseline = true, Description = "System.Drawing Fill with Pattern")]
public void DrawPatternPolygonSystemDrawing() public void DrawPatternPolygonSystemDrawing()
{ {
using (Bitmap destination = new Bitmap(800, 800)) using (var destination = new Bitmap(800, 800))
using (var graphics = Graphics.FromImage(destination))
{ {
using (Graphics graphics = Graphics.FromImage(destination)) graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink))
{ {
graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush
using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush
}
} }
using (MemoryStream ms = new MemoryStream())
using (var stream = new MemoryStream())
{ {
destination.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
} }
} }
} }
@ -41,13 +39,13 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Fill with Pattern")] [Benchmark(Description = "ImageSharp Fill with Pattern")]
public void DrawPatternPolygon3Core() public void DrawPatternPolygon3Core()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink))); image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink)));
using (MemoryStream ms = new MemoryStream()) using (var stream = new MemoryStream())
{ {
image.SaveAsBmp(ms); image.SaveAsBmp(stream);
} }
} }
} }

14
tests/ImageSharp.Benchmarks/General/Array2D.cs

@ -1,16 +1,14 @@
// <copyright file="Array2D.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.General using System;
{
using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Benchmarks.General
{
/** /**
* Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | * Method | Count | Mean | Error | StdDev | Scaled | ScaledSD |
-------------------------------------------- |------ |---------:|---------:|---------:|-------:|---------:| -------------------------------------------- |------ |---------:|---------:|---------:|-------:|---------:|

14
tests/ImageSharp.Benchmarks/General/ArrayReverse.cs

@ -1,14 +1,12 @@
// <copyright file="ArrayReverse.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.General using System;
{
using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General
{
public class ArrayReverse public class ArrayReverse
{ {
[Params(4, 16, 32)] [Params(4, 16, 32)]
@ -58,4 +56,4 @@ namespace SixLabors.ImageSharp.Benchmarks.General
} }
} }
} }
} }

4
tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs

@ -1,7 +1,5 @@
// <copyright file="Clamp.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

5
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs

@ -1,7 +1,4 @@
// ReSharper disable InconsistentNaming using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;

2
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs

@ -3,8 +3,6 @@ using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
{ {
public class PixelConversion_ConvertToVector4 public class PixelConversion_ConvertToVector4

10
tests/ImageSharp.Benchmarks/General/Vector4Constants.cs

@ -1,10 +1,10 @@
namespace SixLabors.ImageSharp.Benchmarks.General using System;
{ using System.Numerics;
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General
{
/// <summary> /// <summary>
/// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them? /// Has it any effect on performance to store SIMD constants as static readonly fields? Is it OK to always inline them?
/// Spoiler: the difference seems to be statistically insignificant! /// Spoiler: the difference seems to be statistically insignificant!

8
tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs

@ -1,9 +1,9 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class BitwiseOrUInt32 public class BitwiseOrUInt32
{ {
private uint[] input; private uint[] input;

12
tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs

@ -1,9 +1,9 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class DivFloat public class DivFloat
{ {
private float[] input; private float[] input;
@ -41,11 +41,11 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
[Benchmark] [Benchmark]
public void Simd() public void Simd()
{ {
Vector<float> v = new Vector<float>(this.testValue); var v = new Vector<float>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{ {
Vector<float> a = new Vector<float>(this.input, i); var a = new Vector<float>(this.input, i);
a = a / v; a = a / v;
a.CopyTo(this.result, i); a.CopyTo(this.result, i);
} }

14
tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs

@ -1,9 +1,9 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class DivUInt32 public class DivUInt32
{ {
private uint[] input; private uint[] input;
@ -32,6 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
public void Standard() public void Standard()
{ {
uint v = this.testValue; uint v = this.testValue;
for (int i = 0; i < this.input.Length; i++) for (int i = 0; i < this.input.Length; i++)
{ {
this.result[i] = this.input[i] / v; this.result[i] = this.input[i] / v;
@ -41,11 +42,12 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
[Benchmark] [Benchmark]
public void Simd() public void Simd()
{ {
Vector<uint> v = new Vector<uint>(this.testValue); var v = new Vector<uint>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{ {
Vector<uint> a = new Vector<uint>(this.input, i); var a = new Vector<uint>(this.input, i);
a = a / v; a = a / v;
a.CopyTo(this.result, i); a.CopyTo(this.result, i);
} }

9
tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs

@ -1,10 +1,9 @@
namespace ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace ImageSharp.Benchmarks.General.Vectorization
{
public class DivFloat : SIMDBenchmarkBase<float>.Divide public class DivFloat : SIMDBenchmarkBase<float>.Divide
{ {
protected override float GetTestValue() => 42; protected override float GetTestValue() => 42;

8
tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs

@ -1,9 +1,9 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class MulFloat public class MulFloat
{ {
private float[] input; private float[] input;

10
tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs

@ -1,9 +1,9 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{
using System.Numerics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class MulUInt32 public class MulUInt32
{ {
private uint[] input; private uint[] input;
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
[Benchmark] [Benchmark]
public void Simd() public void Simd()
{ {
Vector<uint> v = new Vector<uint>(this.testValue); var v = new Vector<uint>(this.testValue);
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) for (int i = 0; i < this.input.Length; i += Vector<uint>.Count)
{ {

6
tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs

@ -1,8 +1,8 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
namespace ImageSharp.Benchmarks.General.Vectorization namespace ImageSharp.Benchmarks.General.Vectorization
{ {
using System.Numerics;
using BenchmarkDotNet.Attributes;
public class MulUInt32 : SIMDBenchmarkBase<uint>.Multiply public class MulUInt32 : SIMDBenchmarkBase<uint>.Multiply
{ {
protected override uint GetTestValue() => 42u; protected override uint GetTestValue() => 42u;

12
tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs

@ -1,10 +1,10 @@
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{ using System.Runtime.InteropServices;
using System.Numerics;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
{
public class ReinterpretUInt32AsFloat public class ReinterpretUInt32AsFloat
{ {
private uint[] input; private uint[] input;
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Standard() public void Standard()
{ {
UIntFloatUnion u = default(UIntFloatUnion); UIntFloatUnion u = default;
for (int i = 0; i < this.input.Length; i++) for (int i = 0; i < this.input.Length; i++)
{ {
u.i = this.input[i]; u.i = this.input[i];

11
tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs

@ -1,10 +1,10 @@
namespace ImageSharp.Benchmarks.General.Vectorization using System.Numerics;
{ using System.Runtime.CompilerServices;
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace ImageSharp.Benchmarks.General.Vectorization
{
public abstract class SIMDBenchmarkBase<T> public abstract class SIMDBenchmarkBase<T>
where T : struct where T : struct
{ {
@ -19,7 +19,6 @@ namespace ImageSharp.Benchmarks.General.Vectorization
protected virtual T GetTestValue() => default(T); protected virtual T GetTestValue() => default(T);
protected virtual Vector<T> GetTestVector() => new Vector<T>(this.GetTestValue()); protected virtual Vector<T> GetTestVector() => new Vector<T>(this.GetTestValue());
[Params(32)] [Params(32)]
public int InputSize { get; set; } public int InputSize { get; set; }

6
tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj

@ -16,10 +16,10 @@
<Compile Include="..\ImageSharp.Tests\TestUtilities\TestEnvironment.cs" Link="Tests\TestEnvironment.cs" /> <Compile Include="..\ImageSharp.Tests\TestUtilities\TestEnvironment.cs" Link="Tests\TestEnvironment.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.3" /> <PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Include="Colourful" Version="2.0.0" /> <PackageReference Include="Colourful" Version="2.0.2" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0007" /> <PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0007" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" /> <PackageReference Include="System.Drawing.Common" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" /> <ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />

13
tests/ImageSharp.Benchmarks/Program.cs

@ -1,15 +1,12 @@
// <copyright file="Program.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Reflection;
using BenchmarkDotNet.Running;
namespace SixLabors.ImageSharp.Benchmarks namespace SixLabors.ImageSharp.Benchmarks
{ {
using BenchmarkDotNet.Running;
using SixLabors.ImageSharp.Formats;
using System.Reflection;
public class Program public class Program
{ {
/// <summary> /// <summary>

42
tests/ImageSharp.Benchmarks/Samplers/Crop.cs

@ -1,46 +1,40 @@
// <copyright file="Crop.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks using System.Drawing;
{ using System.Drawing.Drawing2D;
using System.Drawing;
using System.Drawing.Drawing2D;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using CoreSize = SixLabors.Primitives.Size; using CoreSize = SixLabors.Primitives.Size;
namespace SixLabors.ImageSharp.Benchmarks
{
public class Crop : BenchmarkBase public class Crop : BenchmarkBase
{ {
[Benchmark(Baseline = true, Description = "System.Drawing Crop")] [Benchmark(Baseline = true, Description = "System.Drawing Crop")]
public Size CropSystemDrawing() public Size CropSystemDrawing()
{ {
using (Bitmap source = new Bitmap(800, 800)) using (var source = new Bitmap(800, 800))
using (var destination = new Bitmap(100, 100))
using (var graphics = Graphics.FromImage(destination))
{ {
using (Bitmap destination = new Bitmap(100, 100)) graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
{ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (Graphics graphics = Graphics.FromImage(destination)) graphics.CompositingQuality = CompositingQuality.HighQuality;
{ graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; return destination.Size;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel);
}
return destination.Size;
}
} }
} }
[Benchmark(Description = "ImageSharp Crop")] [Benchmark(Description = "ImageSharp Crop")]
public CoreSize CropResizeCore() public CoreSize CropResizeCore()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
image.Mutate(x => x.Crop(100, 100)); image.Mutate(x => x.Crop(100, 100));
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);

6
tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs

@ -1,7 +1,5 @@
// <copyright file="DetectEdges.cs" company="James Jackson-South"> // Copyright (c) Six Labors and contributors.
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -53,4 +51,4 @@ namespace SixLabors.ImageSharp.Benchmarks
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Sobel)); this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Sobel));
} }
} }
} }

13
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -4,7 +4,6 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@ -35,7 +34,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Glow - Bulk")] [Benchmark(Description = "ImageSharp Glow - Bulk")]
public CoreSize GlowBulk() public CoreSize GlowBulk()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
this.bulk.Apply(image, image.Bounds()); this.bulk.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp Glow - Parallel")] [Benchmark(Description = "ImageSharp Glow - Parallel")]
public CoreSize GLowSimple() public CoreSize GLowSimple()
{ {
using (Image<Rgba32> image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
this.parallel.Apply(image, image.Bounds()); this.parallel.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
@ -128,7 +127,7 @@ namespace SixLabors.ImageSharp.Benchmarks
int offsetX = x - startX; int offsetX = x - startX;
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TPixel packed = default(TPixel); TPixel packed = default;
packed.FromVector4( packed.FromVector4(
PremultipliedLerp( PremultipliedLerp(
sourceColor, sourceColor,
@ -166,9 +165,9 @@ namespace SixLabors.ImageSharp.Benchmarks
// https://en.wikipedia.org/wiki/Alpha_compositing // https://en.wikipedia.org/wiki/Alpha_compositing
// Vout = Vs + Vb (1 - Vsa) // Vout = Vs + Vb (1 - Vsa)
// Aout = Vsa + Vsb (1 - Vsa) // Aout = Vsa + Vsb (1 - Vsa)
Vector3 inverseW = new Vector3(1 - source.W); var inverseW = new Vector3(1 - source.W);
Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); var xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
Vector3 xyzS = new Vector3(source.X, source.Y, source.Z); var xyzS = new Vector3(source.X, source.Y, source.Z);
return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W))); return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
} }

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

@ -1,7 +1,6 @@
// 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.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Globalization; using System.Globalization;

5
tests/ImageSharp.Benchmarks/Samplers/Rotate.cs

@ -1,4 +1,7 @@
using BenchmarkDotNet.Attributes; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives; using SixLabors.Primitives;

5
tests/ImageSharp.Benchmarks/Samplers/Skew.cs

@ -1,4 +1,7 @@
using BenchmarkDotNet.Attributes; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives; using SixLabors.Primitives;

5
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

@ -31,9 +31,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.ExifGetString750Load, TestImages.Jpeg.Issues.ExifGetString750Load,
TestImages.Jpeg.Issues.ExifGetString750Transform, TestImages.Jpeg.Issues.ExifGetString750Transform,
// LibJpeg can open this despite the invalid desity units. // LibJpeg can open this despite the invalid density units.
TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B,
// LibJpeg can open this despite incorrect colorspace metadata.
TestImages.Jpeg.Issues.IncorrectColorspace855,
// High depth images // High depth images
TestImages.Jpeg.Baseline.Testorig12bit, TestImages.Jpeg.Baseline.Testorig12bit,
}; };

168
tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs

@ -0,0 +1,168 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Quantization
{
public class WuQuantizerTests
{
[Fact]
public void SinglePixelOpaque()
{
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (var image = new Image<Rgba32>(config, 1, 1, Rgba32.Black))
using (QuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
{
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
Assert.Equal(Rgba32.Black, result.Palette[0]);
Assert.Equal(0, result.GetPixelSpan()[0]);
}
}
[Fact]
public void SinglePixelTransparent()
{
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (var image = new Image<Rgba32>(config, 1, 1, default(Rgba32)))
using (QuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
{
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
Assert.Equal(default, result.Palette[0]);
Assert.Equal(0, result.GetPixelSpan()[0]);
}
}
[Fact]
public void GrayScale() => TestScale(c => new Rgba32(c, c, c, 128));
[Fact]
public void RedScale() => TestScale(c => new Rgba32(c, 0, 0, 128));
[Fact]
public void GreenScale() => TestScale(c => new Rgba32(0, c, 0, 128));
[Fact]
public void BlueScale() => TestScale(c => new Rgba32(0, 0, c, 128));
[Fact]
public void AlphaScale() => TestScale(c => new Rgba32(0, 0, 0, c));
[Fact]
public void Palette256()
{
using (var image = new Image<Rgba32>(1, 256))
{
for (int i = 0; i < 256; i++)
{
byte r = (byte)((i % 4) * 85);
byte g = (byte)(((i / 4) % 4) * 85);
byte b = (byte)(((i / 16) % 4) * 85);
byte a = (byte)((i / 64) * 85);
image[0, i] = new Rgba32(r, g, b, a);
}
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(config))
using (QuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(256, result.Palette.Length);
Assert.Equal(256, result.GetPixelSpan().Length);
var actualImage = new Image<Rgba32>(1, 256);
int paletteCount = result.Palette.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
{
Span<Rgba32> row = actualImage.GetPixelRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.GetPixelSpan();
int yy = y * actualImage.Width;
for (int x = 0; x < actualImage.Width; x++)
{
int i = x + yy;
row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
}
}
Assert.True(image.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan()));
}
}
}
[Theory]
[WithFile(TestImages.Png.LowColorVariance, PixelTypes.Rgba32)]
public void LowVariance<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
// See https://github.com/SixLabors/ImageSharp/issues/866
using (Image<TPixel> image = provider.GetImage())
{
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<TPixel> frameQuantizer = quantizer.CreateFrameQuantizer<TPixel>(config))
using (QuantizedFrame<TPixel> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(48, result.Palette.Length);
}
}
}
private static void TestScale(Func<byte, Rgba32> pixelBuilder)
{
using (var image = new Image<Rgba32>(1, 256))
using (var expectedImage = new Image<Rgba32>(1, 256))
using (var actualImage = new Image<Rgba32>(1, 256))
{
for (int i = 0; i < 256; i++)
{
byte c = (byte)i;
image[0, i] = pixelBuilder.Invoke(c);
}
for (int i = 0; i < 256; i++)
{
byte c = (byte)((i & ~7) + 4);
expectedImage[0, i] = pixelBuilder.Invoke(c);
}
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(config))
using (QuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(4 * 8, result.Palette.Length);
Assert.Equal(256, result.GetPixelSpan().Length);
int paletteCount = result.Palette.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
{
Span<Rgba32> row = actualImage.GetPixelRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.GetPixelSpan();
int yy = y * actualImage.Width;
for (int x = 0; x < actualImage.Width; x++)
{
int i = x + yy;
row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
}
}
}
Assert.True(expectedImage.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan()));
}
}
}
}

2
tests/ImageSharp.Tests/TestImages.cs

@ -53,6 +53,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Gray2BitTrans = "Png/gray-2-tRNS.png"; public const string Gray2BitTrans = "Png/gray-2-tRNS.png";
public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; public const string Gray4BitTrans = "Png/gray-4-tRNS.png";
public const string Gray8BitTrans = "Png/gray-8-tRNS.png"; public const string Gray8BitTrans = "Png/gray-8-tRNS.png";
public const string LowColorVariance = "Png/low-variance.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png"; public const string Filter0 = "Png/filter0.png";
@ -174,6 +175,7 @@ namespace SixLabors.ImageSharp.Tests
public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg";
public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg";
public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg"; public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg";
public const string IncorrectColorspace855 = "Jpg/issues/issue855-incorrect-colorspace.jpg";
public static class Fuzz public static class Fuzz
{ {

3
tests/Images/Input/Jpg/issues/issue855-incorrect-colorspace.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fabf628f032dce427a6a0e0f252404d66dc4ce2cd7ee2d7ec72a1fbe79c625d0
size 142656

3
tests/Images/Input/Png/low-variance.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:70c224ef674b546db0ec3281008bb6e2b879e95b0be5625f0af8aff980eee583
size 7844
Loading…
Cancel
Save