Browse Source

Optimize effor diffusion. Fix #757

pull/1039/head
James Jackson-South 7 years ago
parent
commit
37a74ecd98
  1. 2
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 4
      src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs
  3. 4
      src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs
  4. 12
      src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs
  5. 12
      src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs
  6. 96
      src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs
  7. 4
      src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs
  8. 12
      src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs
  9. 7
      src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs
  10. 14
      src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs
  11. 12
      src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs
  12. 14
      src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs
  13. 12
      src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs
  14. 16
      src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs
  15. 14
      src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs
  16. 3
      src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt
  17. 6
      src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
  18. 6
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  19. 2
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
  20. 49
      tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs

2
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Advanced
TPixel pixel = default;
using (var image = new ImageFrame<TPixel>(Configuration.Default, 1, 1))
{
test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0);
test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0);
}
}

4
src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -95,4 +95,4 @@ namespace SixLabors.ImageSharp.Processing.Dithering
Rectangle rectangle) =>
source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle);
}
}
}

4
src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// Performs binary threshold filtering against an image using error diffusion.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BinaryErrorDiffusionProcessor<TPixel> : ImageProcessor<TPixel>
internal sealed class BinaryErrorDiffusionProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly BinaryErrorDiffusionProcessor definition;
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
}
TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor;
diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY);
}
}
}

12
src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class AtkinsonDiffuser : ErrorDiffuser
{
private const float Divisor = 8F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> AtkinsonMatrix =
new float[,]
{
{ 0, 0, 1, 1 },
{ 1, 1, 1, 0 },
{ 0, 1, 0, 0 }
{ 0, 0, 1 / Divisor, 1 / Divisor },
{ 1 / Divisor, 1 / Divisor, 1 / Divisor, 0 },
{ 0, 1 / Divisor, 0, 0 }
};
/// <summary>
/// Initializes a new instance of the <see cref="AtkinsonDiffuser"/> class.
/// </summary>
public AtkinsonDiffuser()
: base(AtkinsonMatrix, 8)
: base(AtkinsonMatrix)
{
}
}

12
src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class BurksDiffuser : ErrorDiffuser
{
private const float Divisor = 32F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> BurksMatrix =
new float[,]
{
{ 0, 0, 0, 8, 4 },
{ 2, 4, 8, 4, 2 }
{ 0, 0, 0, 8 / Divisor, 4 / Divisor },
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="BurksDiffuser"/> class.
/// </summary>
public BurksDiffuser()
: base(BurksMatrix, 32)
: base(BurksMatrix)
{
}
}
}
}

96
src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -15,61 +15,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public abstract class ErrorDiffuser : IErrorDiffuser
{
/// <summary>
/// The vector to perform division.
/// </summary>
private readonly Vector4 divisorVector;
/// <summary>
/// The matrix width.
/// </summary>
private readonly int matrixHeight;
/// <summary>
/// The matrix height.
/// </summary>
private readonly int matrixWidth;
/// <summary>
/// The offset at which to start the dithering operation.
/// </summary>
private readonly int startingOffset;
/// <summary>
/// The diffusion matrix.
/// </summary>
private readonly DenseMatrix<float> matrix;
/// <summary>
/// Initializes a new instance of the <see cref="ErrorDiffuser"/> class.
/// </summary>
/// <param name="matrix">The dithering matrix.</param>
/// <param name="divisor">The divisor.</param>
internal ErrorDiffuser(in DenseMatrix<float> matrix, byte divisor)
internal ErrorDiffuser(in DenseMatrix<float> matrix)
{
Guard.MustBeGreaterThan(divisor, 0, nameof(divisor));
this.matrix = matrix;
this.matrixWidth = this.matrix.Columns;
this.matrixHeight = this.matrix.Rows;
this.divisorVector = new Vector4(divisor);
this.startingOffset = 0;
for (int i = 0; i < this.matrixWidth; i++)
for (int col = 0; col < matrix.Columns; col++)
{
// Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (matrix[0, i] != 0)
if (matrix[0, col] != 0)
{
this.startingOffset = (byte)(i - 1);
this.startingOffset = col - 1;
break;
}
}
this.matrix = matrix;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
[MethodImpl(InliningOptions.ShortMethod)]
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>
{
image[x, y] = transformed;
@ -82,45 +53,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
// Calculate the error
Vector4 error = source.ToVector4() - transformed.ToVector4();
this.DoDither(image, x, y, minX, minY, maxX, maxY, error);
this.DoDither(image, x, y, minX, maxX, maxY, error);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void DoDither<TPixel>(ImageFrame<TPixel> image, int x, int y, int minX, int minY, int maxX, int maxY, Vector4 error)
[MethodImpl(InliningOptions.ShortMethod)]
private void DoDither<TPixel>(ImageFrame<TPixel> image, int x, int y, int minX, int maxX, int maxY, Vector4 error)
where TPixel : struct, IPixel<TPixel>
{
int offset = this.startingOffset;
DenseMatrix<float> matrix = this.matrix;
// Loop through and distribute the error amongst neighboring pixels.
for (int row = 0; row < this.matrixHeight; row++)
for (int row = 0, targetY = y + row; row < matrix.Rows && targetY < maxY; row++)
{
int matrixY = y + row;
if (matrixY > minY && matrixY < maxY)
{
Span<TPixel> rowSpan = image.GetPixelRowSpan(matrixY);
Span<TPixel> rowSpan = image.GetPixelRowSpan(targetY);
for (int col = 0; col < this.matrixWidth; col++)
for (int col = 0; col < matrix.Columns; col++)
{
int targetX = x + (col - offset);
if (targetX > minX && targetX < maxX)
{
int matrixX = x + (col - this.startingOffset);
if (matrixX > minX && matrixX < maxX)
float coefficient = matrix[row, col];
if (coefficient == 0)
{
float coefficient = this.matrix[row, col];
// Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (coefficient == 0)
{
continue;
}
continue;
}
ref TPixel pixel = ref rowSpan[matrixX];
var offsetColor = pixel.ToVector4();
ref TPixel pixel = ref rowSpan[targetX];
var offsetColor = pixel.ToVector4();
Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
pixel.FromVector4(result);
}
Vector4 result = (error * coefficient) + offsetColor;
pixel.FromVector4(result);
}
}
}
}
}
}
}

4
src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// An <see cref="IImageProcessor{TPixel}"/> that dithers an image using error diffusion.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ErrorDiffusionPaletteProcessor<TPixel> : PaletteDitherProcessor<TPixel>
internal sealed class ErrorDiffusionPaletteProcessor<TPixel> : PaletteDitherProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
}
TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First;
this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY);
}
}
}

12
src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class FloydSteinbergDiffuser : ErrorDiffuser
{
private const float Divisor = 16F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> FloydSteinbergMatrix =
new float[,]
{
{ 0, 0, 7 },
{ 3, 5, 1 }
{ 0, 0, 7 / Divisor },
{ 3 / Divisor, 5 / Divisor, 1 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="FloydSteinbergDiffuser"/> class.
/// </summary>
public FloydSteinbergDiffuser()
: base(FloydSteinbergMatrix, 16)
: base(FloydSteinbergMatrix)
{
}
}
}
}

7
src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -19,11 +19,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <param name="x">The column index.</param>
/// <param name="y">The row index.</param>
/// <param name="minX">The minimum column value.</param>
/// <param name="minY">The minimum row value.</param>
/// <param name="maxX">The maximum column value.</param>
/// <param name="maxY">The maximum row value.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>;
}
}
}

14
src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuser
{
private const float Divisor = 48F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> JarvisJudiceNinkeMatrix =
new float[,]
{
{ 0, 0, 0, 7, 5 },
{ 3, 5, 7, 5, 3 },
{ 1, 3, 5, 3, 1 }
{ 0, 0, 0, 7 / Divisor, 5 / Divisor },
{ 3 / Divisor, 5 / Divisor, 7 / Divisor, 5 / Divisor, 3 / Divisor },
{ 1 / Divisor, 3 / Divisor, 5 / Divisor, 3 / Divisor, 1 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="JarvisJudiceNinkeDiffuser"/> class.
/// </summary>
public JarvisJudiceNinkeDiffuser()
: base(JarvisJudiceNinkeMatrix, 48)
: base(JarvisJudiceNinkeMatrix)
{
}
}
}
}

12
src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class Sierra2Diffuser : ErrorDiffuser
{
private const float Divisor = 16F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> Sierra2Matrix =
new float[,]
{
{ 0, 0, 0, 4, 3 },
{ 1, 2, 3, 2, 1 }
{ 0, 0, 0, 4 / Divisor, 3 / Divisor },
{ 1 / Divisor, 2 / Divisor, 3 / Divisor, 2 / Divisor, 1 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="Sierra2Diffuser"/> class.
/// </summary>
public Sierra2Diffuser()
: base(Sierra2Matrix, 16)
: base(Sierra2Matrix)
{
}
}
}
}

14
src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class Sierra3Diffuser : ErrorDiffuser
{
private const float Divisor = 32F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> Sierra3Matrix =
new float[,]
{
{ 0, 0, 0, 5, 3 },
{ 2, 4, 5, 4, 2 },
{ 0, 2, 3, 2, 0 }
{ 0, 0, 0, 5 / Divisor, 3 / Divisor },
{ 2 / Divisor, 4 / Divisor, 5 / Divisor, 4 / Divisor, 2 / Divisor },
{ 0, 2 / Divisor, 3 / Divisor, 2 / Divisor, 0 }
};
/// <summary>
/// Initializes a new instance of the <see cref="Sierra3Diffuser"/> class.
/// </summary>
public Sierra3Diffuser()
: base(Sierra3Matrix, 32)
: base(Sierra3Matrix)
{
}
}
}
}

12
src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class SierraLiteDiffuser : ErrorDiffuser
{
private const float Divisor = 4F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> SierraLiteMatrix =
new float[,]
{
{ 0, 0, 2 },
{ 1, 1, 0 }
{ 0, 0, 2 / Divisor },
{ 1 / Divisor, 1 / Divisor, 0 }
};
/// <summary>
/// Initializes a new instance of the <see cref="SierraLiteDiffuser"/> class.
/// </summary>
public SierraLiteDiffuser()
: base(SierraLiteMatrix, 4)
: base(SierraLiteMatrix)
{
}
}
}
}

16
src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -10,24 +10,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class StevensonArceDiffuser : ErrorDiffuser
{
private const float Divisor = 200F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> StevensonArceMatrix =
new float[,]
{
{ 0, 0, 0, 0, 0, 32, 0 },
{ 12, 0, 26, 0, 30, 0, 16 },
{ 0, 12, 0, 26, 0, 12, 0 },
{ 5, 0, 12, 0, 12, 0, 5 }
{ 0, 0, 0, 0, 0, 32 / Divisor, 0 },
{ 12 / Divisor, 0, 26 / Divisor, 0, 30 / Divisor, 0, 16 / Divisor },
{ 0, 12 / Divisor, 0, 26 / Divisor, 0, 12 / Divisor, 0 },
{ 5 / Divisor, 0, 12 / Divisor, 0, 12 / Divisor, 0, 5 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="StevensonArceDiffuser"/> class.
/// </summary>
public StevensonArceDiffuser()
: base(StevensonArceMatrix, 200)
: base(StevensonArceMatrix)
{
}
}
}
}

14
src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Primitives;
@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// </summary>
public sealed class StuckiDiffuser : ErrorDiffuser
{
private const float Divisor = 42F;
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly DenseMatrix<float> StuckiMatrix =
new float[,]
{
{ 0, 0, 0, 8, 4 },
{ 2, 4, 8, 4, 2 },
{ 1, 2, 4, 2, 1 }
{ 0, 0, 0, 8 / Divisor, 4 / Divisor },
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor },
{ 1 / Divisor, 2 / Divisor, 4 / Divisor, 2 / Divisor, 1 / Divisor }
};
/// <summary>
/// Initializes a new instance of the <see cref="StuckiDiffuser"/> class.
/// </summary>
public StuckiDiffuser()
: base(StuckiMatrix, 42)
: base(StuckiMatrix)
{
}
}
}
}

3
src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt

@ -1,3 +1,6 @@
Reference:
http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/error_diffusion.txt
List of error diffusion schemes.
Quantization error of *current* pixel is added to the pixels

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;
@ -571,4 +571,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;
@ -98,4 +98,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte QuantizePixel(ref TPixel pixel) => this.GetClosestPixel(ref pixel);
}
}
}

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

@ -260,7 +260,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;

49
tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs

@ -0,0 +1,49 @@
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Dithering;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Benchmarks.Samplers
{
[Config(typeof(Config.ShortClr))]
public class Diffuse
{
[Benchmark]
public Size DoDiffuse()
{
using (var image = new Image<Rgba32>(Configuration.Default, 800, 800, Rgba32.BlanchedAlmond))
{
image.Mutate(x => x.Diffuse());
return image.Size();
}
}
}
}
// #### 25th October 2019 ####
//
// BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
// Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
// .NET Core SDK = 3.0.100
//
// [Host] : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.8.4018.0
// Core : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT
//
// IterationCount=3 LaunchCount=1 WarmupCount=3
//
// #### Before ####
//
// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------- |----- |-------- |----------:|---------:|---------:|------:|------:|------:|----------:|
// | DoDiffuse | Clr | Clr | 129.58 ms | 24.60 ms | 1.349 ms | - | - | - | 6 KB |
// | DoDiffuse | Core | Core | 92.63 ms | 89.78 ms | 4.921 ms | - | - | - | 4.58 KB |
//
// #### After ####
//
// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------- |----- |-------- |---------:|---------:|---------:|------:|------:|------:|----------:|
// | DoDiffuse | Clr | Clr | 12.94 ms | 22.48 ms | 1.232 ms | - | - | - | 4.25 KB |
// | DoDiffuse | Core | Core | 10.95 ms | 19.31 ms | 1.058 ms | - | - | - | 4.13 KB |
Loading…
Cancel
Save