Browse Source

Refactor to better use base classes.

pull/637/head
James Jackson-South 8 years ago
parent
commit
c3d38bac9c
  1. 4
      src/ImageSharp/Processing/Dithering/Processors/ErrorDiffusionPaletteProcessor.cs
  2. 4
      src/ImageSharp/Processing/Dithering/Processors/OrderedDitherPaletteProcessor.cs
  3. 28
      src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs
  4. 83
      src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs
  5. 76
      src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs
  6. 37
      src/ImageSharp/Processing/Quantization/FrameQuantizers/PaletteFrameQuantizer{TPixel}.cs
  7. 22
      src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs
  8. 35
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  9. 1
      tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs

4
src/ImageSharp/Processing/Dithering/Processors/ErrorDiffusionPaletteProcessor.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
// Collect the values before looping so we can reduce our calculation count for identical sibling pixels
TPixel sourcePixel = source[startX, startY];
TPixel previousPixel = sourcePixel;
PixelPair<TPixel> pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette);
PixelPair<TPixel> pair = this.GetClosestPixelPair(ref sourcePixel);
sourcePixel.ToRgba32(ref rgba);
// Convert to grayscale using ITU-R Recommendation BT.709 if required
@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
// rather than calculating it again. This is an inexpensive optimization.
if (!previousPixel.Equals(sourcePixel))
{
pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette);
pair = this.GetClosestPixelPair(ref sourcePixel);
// No error to spread, exact match.
if (sourcePixel.Equals(pair.First))

4
src/ImageSharp/Processing/Dithering/Processors/OrderedDitherPaletteProcessor.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
// Collect the values before looping so we can reduce our calculation count for identical sibling pixels
TPixel sourcePixel = source[startX, startY];
TPixel previousPixel = sourcePixel;
PixelPair<TPixel> pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette);
PixelPair<TPixel> pair = this.GetClosestPixelPair(ref sourcePixel);
sourcePixel.ToRgba32(ref rgba);
// Convert to grayscale using ITU-R Recommendation BT.709 if required
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
// rather than calculating it again. This is an inexpensive optimization.
if (!previousPixel.Equals(sourcePixel))
{
pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette);
pair = this.GetClosestPixelPair(ref sourcePixel);
// No error to spread, exact match.
if (sourcePixel.Equals(pair.First))

28
src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs

@ -18,6 +18,11 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
{
private readonly Dictionary<TPixel, PixelPair<TPixel>> cache = new Dictionary<TPixel, PixelPair<TPixel>>();
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private readonly Vector4[] paletteVector;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteDitherProcessorBase{TPixel}"/> class.
/// </summary>
@ -26,6 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
{
Guard.NotNull(palette, nameof(palette));
this.Palette = palette;
this.paletteVector = new Vector4[this.Palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(this.Palette, this.paletteVector, this.Palette.Length);
}
/// <summary>
@ -33,8 +40,13 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
/// </summary>
public TPixel[] Palette { get; }
/// <summary>
/// Returns the two closest colors from the palette calcluated via Euclidean distance in the Rgba space.
/// </summary>
/// <param name="pixel">The source color to match.</param>
/// <returns>The <see cref="PixelPair{TPixel}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected PixelPair<TPixel> GetClosestPixelPair(ref TPixel pixel, TPixel[] colorPalette)
protected PixelPair<TPixel> GetClosestPixelPair(ref TPixel pixel)
{
// Check if the color is in the lookup table
if (this.cache.TryGetValue(pixel, out PixelPair<TPixel> value))
@ -42,11 +54,11 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
return value;
}
return this.GetClosestPixelPairSlow(ref pixel, colorPalette);
return this.GetClosestPixelPairSlow(ref pixel);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private PixelPair<TPixel> GetClosestPixelPairSlow(ref TPixel pixel, TPixel[] colorPalette)
private PixelPair<TPixel> GetClosestPixelPairSlow(ref TPixel pixel)
{
// Not found - loop through the palette and find the nearest match.
float leastDistance = float.MaxValue;
@ -55,21 +67,21 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
TPixel closest = default;
TPixel secondClosest = default;
for (int index = 0; index < colorPalette.Length; index++)
for (int index = 0; index < this.paletteVector.Length; index++)
{
ref TPixel candidate = ref colorPalette[index];
float distance = Vector4.DistanceSquared(vector, candidate.ToVector4());
ref Vector4 candidate = ref this.paletteVector[index];
float distance = Vector4.DistanceSquared(vector, candidate);
if (distance < leastDistance)
{
leastDistance = distance;
secondClosest = closest;
closest = candidate;
closest = this.Palette[index];
}
else if (distance < secondLeastDistance)
{
secondLeastDistance = distance;
secondClosest = candidate;
secondClosest = this.Palette[index];
}
}

83
src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs

@ -27,6 +27,11 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// </summary>
private readonly bool singlePass;
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private Vector4[] paletteVector;
/// <summary>
/// Initializes a new instance of the <see cref="FrameQuantizerBase{TPixel}"/> class.
/// </summary>
@ -35,10 +40,9 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// If true, the quantization process only needs to loop through the source pixels once
/// </param>
/// <remarks>
/// If you construct this class with a true value for singlePass, then the code will, when quantizing your image,
/// only call the <see cref="FirstPass(ImageFrame{TPixel}, int, int)"/> methods.
/// If two passes are required, the code will also call <see cref="SecondPass(ImageFrame{TPixel}, Span{byte}, int, int)"/>
/// and then 'QuantizeImage'.
/// If you construct this class with a <value>true</value> for <paramref name="singlePass"/>, then the code will
/// only call the <see cref="SecondPass(ImageFrame{TPixel}, Span{byte}, ReadOnlySpan{TPixel}, int, int)"/> method.
/// If two passes are required, the code will also call <see cref="FirstPass(ImageFrame{TPixel}, int, int)"/>.
/// </remarks>
protected FrameQuantizerBase(IQuantizer quantizer, bool singlePass)
{
@ -73,28 +77,31 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
// Collect the palette. Required before the second pass runs.
var quantizedFrame = new QuantizedFrame<TPixel>(image.MemoryAllocator, width, height, this.GetPalette());
TPixel[] palette = this.GetPalette();
this.paletteVector = new Vector4[palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(palette, this.paletteVector, palette.Length);
var quantizedFrame = new QuantizedFrame<TPixel>(image.MemoryAllocator, width, height, palette);
if (this.Dither)
{
// We clone the image as we don't want to alter the original.
// We clone the image as we don't want to alter the original via dithering.
using (ImageFrame<TPixel> clone = image.Clone())
{
this.SecondPass(clone, quantizedFrame.GetPixelSpan(), width, height);
this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette, width, height);
}
}
else
{
this.SecondPass(image, quantizedFrame.GetPixelSpan(), width, height);
this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette, width, height);
}
return quantizedFrame;
}
/// <summary>
/// Execute the first pass through the pixels in the image
/// Execute the first pass through the pixels in the image to create the palette.
/// </summary>
/// <param name="source">The source data</param>
/// <param name="source">The source data.</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
protected virtual void FirstPass(ImageFrame<TPixel> source, int width, int height)
@ -102,17 +109,22 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
/// <summary>
/// Execute a second pass through the image
/// Execute a second pass through the image to assign the pixels to a palette entry.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="output">The output pixel array</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
protected abstract void SecondPass(ImageFrame<TPixel> source, Span<byte> output, int width, int height);
/// <param name="output">The output pixel array.</param>
/// <param name="palette">The output color palette.</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
protected abstract void SecondPass(
ImageFrame<TPixel> source,
Span<byte> output,
ReadOnlySpan<TPixel> palette,
int width,
int height);
/// <summary>
/// Retrieve the palette for the quantized image.
/// <remarks>Can be called more than once so make sure calls are cached.</remarks>
/// </summary>
/// <returns>
/// <see cref="T:TPixel[]"/>
@ -120,13 +132,34 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
protected abstract TPixel[] GetPalette();
/// <summary>
/// Returns the closest color from the palette to the given color by calculating the Euclidean distance.
/// Returns the index of the first instance of the transparent color in the palette.
/// </summary>
/// <returns>The <see cref="int"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected byte GetTransparentIndex()
{
// Transparent pixels are much more likely to be found at the end of a palette.
int index = this.paletteVector.Length - 1;
for (int i = this.paletteVector.Length - 1; i >= 0; i--)
{
ref Vector4 candidate = ref this.paletteVector[i];
if (candidate.Equals(default))
{
index = i;
}
}
return (byte)index;
}
/// <summary>
/// Returns the closest color from the palette to the given color by calculating the
/// Euclidean distance in the Rgba colorspace.
/// </summary>
/// <param name="pixel">The color.</param>
/// <param name="colorPalette">The color palette.</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected byte GetClosestPixel(TPixel pixel, TPixel[] colorPalette)
protected byte GetClosestPixel(ref TPixel pixel)
{
// Check if the color is in the lookup table
if (this.distanceCache.TryGetValue(pixel, out byte value))
@ -134,22 +167,22 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
return value;
}
return this.GetClosestPixelSlow(pixel, colorPalette);
return this.GetClosestPixelSlow(ref pixel);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private byte GetClosestPixelSlow(TPixel pixel, TPixel[] colorPalette)
private byte GetClosestPixelSlow(ref TPixel pixel)
{
// Loop through the palette and find the nearest match.
int colorIndex = 0;
float leastDistance = float.MaxValue;
var vector = pixel.ToVector4();
Vector4 vector = pixel.ToScaledVector4();
float epsilon = Constants.EpsilonSquared;
for (int index = 0; index < colorPalette.Length; index++)
for (int index = 0; index < this.paletteVector.Length; index++)
{
ref TPixel candidate = ref colorPalette[index];
float distance = Vector4.DistanceSquared(vector, candidate.ToVector4());
ref Vector4 candidate = ref this.paletteVector[index];
float distance = Vector4.DistanceSquared(vector, candidate);
// Greater... Move on.
if (!(distance < leastDistance))

76
src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs

@ -29,11 +29,6 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// </summary>
private readonly Octree octree;
/// <summary>
/// The reduced image palette
/// </summary>
private TPixel[] palette;
/// <summary>
/// The transparent index
/// </summary>
@ -77,16 +72,21 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
/// <inheritdoc/>
protected override void SecondPass(ImageFrame<TPixel> source, Span<byte> output, int width, int height)
protected override void SecondPass(
ImageFrame<TPixel> source,
Span<byte> output,
ReadOnlySpan<TPixel> palette,
int width,
int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.
TPixel sourcePixel = source[0, 0];
TPixel previousPixel = sourcePixel;
Rgba32 rgba = default;
byte pixelValue = this.QuantizePixel(sourcePixel, ref rgba);
TPixel[] colorPalette = this.GetPalette();
TPixel transformedPixel = colorPalette[pixelValue];
this.transparentIndex = this.GetTransparentIndex();
byte pixelValue = this.QuantizePixel(ref sourcePixel, ref rgba);
TPixel transformedPixel = palette[pixelValue];
for (int y = 0; y < height; y++)
{
@ -103,14 +103,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
if (!previousPixel.Equals(sourcePixel))
{
// Quantize the pixel
pixelValue = this.QuantizePixel(sourcePixel, ref rgba);
pixelValue = this.QuantizePixel(ref sourcePixel, ref rgba);
// And setup the previous pointer
previousPixel = sourcePixel;
if (this.Dither)
{
transformedPixel = colorPalette[pixelValue];
transformedPixel = palette[pixelValue];
}
}
@ -126,58 +126,22 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
/// <inheritdoc/>
protected override TPixel[] GetPalette()
{
if (this.palette == null)
{
this.palette = this.octree.Palletize(Math.Max(this.colors, (byte)1));
this.transparentIndex = this.GetTransparentIndex();
}
return this.palette;
}
/// <summary>
/// Returns the index of the first instance of the transparent color in the palette.
/// </summary>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte GetTransparentIndex()
{
// Transparent pixels are much more likely to be found at the end of a palette
int index = this.colors;
Rgba32 trans = default;
for (int i = this.palette.Length - 1; i >= 0; i--)
{
this.palette[i].ToRgba32(ref trans);
if (trans.Equals(default))
{
index = i;
}
}
return (byte)index;
}
protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors);
/// <summary>
/// Process the pixel in the second pass of the algorithm
/// Process the pixel in the second pass of the algorithm.
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <param name="rgba">The color to compare against</param>
/// <returns>
/// The quantized value
/// </returns>
/// <param name="pixel">The pixel to quantize.</param>
/// <param name="rgba">The color to compare against.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte QuantizePixel(TPixel pixel, ref Rgba32 rgba)
private byte QuantizePixel(ref TPixel pixel, ref Rgba32 rgba)
{
if (this.Dither)
{
// The colors have changed so we need to use Euclidean distance calculation to find the closest value.
// This palette can never be null here.
return this.GetClosestPixel(pixel, this.palette);
// The colors have changed so we need to use Euclidean distance calculation to
// find the closest value.
return this.GetClosestPixel(ref pixel);
}
pixel.ToRgba32(ref rgba);

37
src/ImageSharp/Processing/Quantization/FrameQuantizers/PaletteFrameQuantizer{TPixel}.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -18,9 +19,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// List of all colors in the palette.
/// The reduced image palette.
/// </summary>
private readonly TPixel[] colors;
private readonly TPixel[] palette;
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private readonly Vector4[] paletteVector;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
@ -30,20 +36,27 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
public PaletteFrameQuantizer(PaletteQuantizer quantizer, TPixel[] colors)
: base(quantizer, true)
{
Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 255, nameof(colors));
this.colors = colors;
Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 256, nameof(colors));
this.palette = colors;
this.paletteVector = new Vector4[this.palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(this.palette, this.paletteVector, this.palette.Length);
}
/// <inheritdoc/>
protected override void SecondPass(ImageFrame<TPixel> source, Span<byte> output, int width, int height)
protected override void SecondPass(
ImageFrame<TPixel> source,
Span<byte> output,
ReadOnlySpan<TPixel> palette,
int width,
int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.
TPixel sourcePixel = source[0, 0];
TPixel previousPixel = sourcePixel;
byte pixelValue = this.QuantizePixel(sourcePixel);
ref TPixel colorPaletteRef = ref MemoryMarshal.GetReference(this.GetPalette().AsSpan());
TPixel transformedPixel = Unsafe.Add(ref colorPaletteRef, pixelValue);
byte pixelValue = this.QuantizePixel(ref sourcePixel);
ref TPixel paletteRef = ref MemoryMarshal.GetReference(palette);
TPixel transformedPixel = Unsafe.Add(ref paletteRef, pixelValue);
for (int y = 0; y < height; y++)
{
@ -60,14 +73,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
if (!previousPixel.Equals(sourcePixel))
{
// Quantize the pixel
pixelValue = this.QuantizePixel(sourcePixel);
pixelValue = this.QuantizePixel(ref sourcePixel);
// And setup the previous pointer
previousPixel = sourcePixel;
if (this.Dither)
{
transformedPixel = Unsafe.Add(ref colorPaletteRef, pixelValue);
transformedPixel = Unsafe.Add(ref paletteRef, pixelValue);
}
}
@ -84,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override TPixel[] GetPalette() => this.colors;
protected override TPixel[] GetPalette() => this.palette;
/// <summary>
/// Process the pixel in the second pass of the algorithm
@ -94,6 +107,6 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// The quantized value
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte QuantizePixel(TPixel pixel) => this.GetClosestPixel(pixel, this.GetPalette());
private byte QuantizePixel(ref TPixel pixel) => this.GetClosestPixel(ref pixel);
}
}

22
src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs

@ -39,7 +39,6 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
// - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes )
// - 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!
// - There are per-pixel virtual calls in InitialQuantizePixel, why not do it on a per-row basis?
// - 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
@ -182,7 +181,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
float a = Volume(ref this.colorCube[k], this.vma.GetSpan());
ref TPixel color = ref this.palette[k];
color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F);
color.PackFromScaledVector4(new Vector4(r, g, b, a) / weight / 255F);
}
}
}
@ -246,15 +245,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
/// <inheritdoc/>
protected override void SecondPass(ImageFrame<TPixel> source, Span<byte> output, int width, int height)
protected override void SecondPass(ImageFrame<TPixel> source, Span<byte> output, ReadOnlySpan<TPixel> palette, int width, int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.
TPixel sourcePixel = source[0, 0];
TPixel previousPixel = sourcePixel;
byte pixelValue = this.QuantizePixel(sourcePixel);
TPixel[] colorPalette = this.GetPalette();
TPixel transformedPixel = colorPalette[pixelValue];
byte pixelValue = this.QuantizePixel(ref sourcePixel);
TPixel transformedPixel = palette[pixelValue];
for (int y = 0; y < height; y++)
{
@ -271,14 +269,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
if (!previousPixel.Equals(sourcePixel))
{
// Quantize the pixel
pixelValue = this.QuantizePixel(sourcePixel);
pixelValue = this.QuantizePixel(ref sourcePixel);
// And setup the previous pointer
previousPixel = sourcePixel;
if (this.Dither)
{
transformedPixel = colorPalette[pixelValue];
transformedPixel = palette[pixelValue];
}
}
@ -843,13 +841,13 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
/// The quantized value
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private byte QuantizePixel(TPixel pixel)
private byte QuantizePixel(ref TPixel pixel)
{
if (this.Dither)
{
// The colors have changed so we need to use Euclidean distance calculation to find the closest value.
// This palette can never be null here.
return this.GetClosestPixel(pixel, this.palette);
// The colors have changed so we need to use Euclidean distance calculation to
// find the closest value.
return this.GetClosestPixel(ref pixel);
}
// Expected order r->g->b->a

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

@ -87,41 +87,6 @@ namespace SixLabors.ImageSharp.Tests
}
provider.Configuration.MemoryAllocator.ReleaseRetainedResources();
//string path = TestEnvironment.CreateOutputDirectory("Quantize");
//foreach (TestFile file in Files)
//{
// using (Image<Rgba32> srcImage = Image.Load<Rgba32>(file.Bytes, out IImageFormat mimeType))
// {
// using (Image<Rgba32> image = srcImage.Clone())
// {
// using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
// {
// image.Mutate(x => x.Quantize(KnownQuantizers.Octree));
// image.Save(output, mimeType);
// }
// }
// using (Image<Rgba32> image = srcImage.Clone())
// {
// using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
// {
// image.Mutate(x => x.Quantize(KnownQuantizers.Wu));
// image.Save(output, mimeType);
// }
// }
// using (Image<Rgba32> image = srcImage.Clone())
// {
// using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}"))
// {
// image.Mutate(x => x.Quantize(KnownQuantizers.Palette));
// image.Save(output, mimeType);
// }
// }
// }
//}
}
private static IQuantizer GetQuantizer(string name)

1
tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs

@ -40,7 +40,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{ "Stucki", KnownDiffusers.Stucki },
};
private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4;
private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson;

Loading…
Cancel
Save