Browse Source

Use caching for the distance calculation

af/merge-core
James Jackson-South 9 years ago
parent
commit
dfbdba5763
  1. 48
      src/ImageSharp/Quantizers/Octree/Quantizer.cs
  2. 42
      src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs

48
src/ImageSharp/Quantizers/Octree/Quantizer.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Quantizers
{
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -18,6 +19,11 @@ namespace ImageSharp.Quantizers
public abstract class Quantizer<TColor> : IDitheredQuantizer<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// A lookup table for colors
/// </summary>
private readonly Dictionary<TColor, byte> colorMap = new Dictionary<TColor, byte>();
/// <summary>
/// Flag used to indicate whether a single pass or two passes are needed for quantization.
/// </summary>
@ -129,7 +135,7 @@ namespace ImageSharp.Quantizers
{
// Apply the dithering matrix
TColor sourcePixel = source[x, y];
TColor transformedPixel = this.palette[GetClosestColor(sourcePixel, this.palette)];
TColor transformedPixel = this.palette[this.GetClosestColor(sourcePixel, this.palette, this.colorMap)];
this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height);
}
@ -171,32 +177,46 @@ namespace ImageSharp.Quantizers
/// Returns the closest color from the palette to the given color by calculating the Euclidean distance.
/// </summary>
/// <param name="pixel">The color.</param>
/// <param name="palette">The color palette.</param>
/// <param name="colorPalette">The color palette.</param>
/// <param name="cache">The cache to store the result in.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetClosestColor(TColor pixel, TColor[] palette)
protected byte GetClosestColor(TColor pixel, TColor[] colorPalette, Dictionary<TColor, byte> cache)
{
// Check if the color is in the lookup table
if (this.colorMap.ContainsKey(pixel))
{
return this.colorMap[pixel];
}
// Not found - loop through the palette and find the nearest match.
byte colorIndex = 0;
float leastDistance = int.MaxValue;
Vector4 vector = pixel.ToVector4();
byte colorIndex = 0;
for (int index = 0; index < palette.Length; index++)
for (int index = 0; index < colorPalette.Length; index++)
{
float distance = Vector4.Distance(vector, palette[index].ToVector4());
float distance = Vector4.Distance(vector, colorPalette[index].ToVector4());
if (distance < leastDistance)
// Greater... Move on.
if (!(distance < leastDistance))
{
colorIndex = (byte)index;
leastDistance = distance;
continue;
}
// And if it's an exact match, exit the loop
if (Math.Abs(distance) < Constants.Epsilon)
{
break;
}
colorIndex = (byte)index;
leastDistance = distance;
// And if it's an exact match, exit the loop
if (Math.Abs(distance) < Constants.Epsilon)
{
break;
}
}
// Now I have the index, pop it into the cache for next time
this.colorMap.Add(pixel, colorIndex);
return colorIndex;
}
}

42
src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs

@ -7,15 +7,14 @@ namespace ImageSharp.Quantizers
{
using System;
using System.Collections.Generic;
using System.Numerics;
/// <summary>
/// Encapsulates methods to create a quantized image based upon the given palette.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class PaletteQuantizer<TColor> : Quantizer<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
public sealed class PaletteQuantizer<TColor> : Quantizer<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// The pixel buffer, used to reduce allocations.
@ -73,42 +72,7 @@ namespace ImageSharp.Quantizers
/// <inheritdoc/>
protected override byte QuantizePixel(TColor pixel)
{
byte colorIndex = 0;
TColor colorHash = pixel;
// Check if the color is in the lookup table
if (this.colorMap.ContainsKey(colorHash))
{
colorIndex = this.colorMap[colorHash];
}
else
{
// Not found - loop through the palette and find the nearest match.
float leastDistance = int.MaxValue;
Vector4 vector = pixel.ToVector4();
for (int index = 0; index < this.colors.Length; index++)
{
float distance = Vector4.Distance(vector, this.colors[index].ToVector4());
if (distance < leastDistance)
{
colorIndex = (byte)index;
leastDistance = distance;
// And if it's an exact match, exit the loop
if (Math.Abs(distance) < Constants.Epsilon)
{
break;
}
}
}
// Now I have the color, pop it into the cache for next time
this.colorMap.Add(colorHash, colorIndex);
}
return colorIndex;
return this.GetClosestColor(pixel, this.colors, this.colorMap);
}
/// <inheritdoc/>

Loading…
Cancel
Save