//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
namespace ImageProcessorCore.Quantizers
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
///
/// Encapsulates methods to create a quantized image based upon the given palette.
///
///
/// The pixel format.
/// The packed format. uint, long, float.
public class PaletteQuantizer : Quantizer
where TColor : struct, IPackedVector
where TPacked : struct
{
///
/// A lookup table for colors
///
private readonly ConcurrentDictionary colorMap = new ConcurrentDictionary();
///
/// List of all colors in the palette
///
private TColor[] colors;
///
/// Initializes a new instance of the class.
///
///
/// The color palette. If none is given this will default to the web safe colors defined
/// in the CSS Color Module Level 4.
///
public PaletteQuantizer(TColor[] palette = null)
: base(true)
{
if (palette == null)
{
Color[] constants = ColorConstants.WebSafeColors;
List safe = new List { default(TColor) };
foreach (Color c in constants)
{
TColor packed = default(TColor);
packed.PackFromVector4(c.ToVector4());
safe.Add(packed);
}
this.colors = safe.ToArray();
}
else
{
this.colors = palette;
}
}
///
public override QuantizedImage Quantize(ImageBase image, int maxColors)
{
Array.Resize(ref this.colors, maxColors.Clamp(1, 256));
return base.Quantize(image, maxColors);
}
///
protected override byte QuantizePixel(TColor pixel)
{
byte colorIndex = 0;
string colorHash = pixel.ToString();
// 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.
Color color = new Color(pixel.ToVector4());
int leastDistance = int.MaxValue;
int red = color.R;
int green = color.G;
int blue = color.B;
int alpha = color.A;
for (int index = 0; index < this.colors.Length; index++)
{
Color paletteColor = new Color(this.colors[index].ToVector4());
int redDistance = paletteColor.R - red;
int greenDistance = paletteColor.G - green;
int blueDistance = paletteColor.B - blue;
int alphaDistance = paletteColor.A - alpha;
int distance = (redDistance * redDistance) +
(greenDistance * greenDistance) +
(blueDistance * blueDistance) +
(alphaDistance * alphaDistance);
if (distance < leastDistance)
{
colorIndex = (byte)index;
leastDistance = distance;
// And if it's an exact match, exit the loop
if (distance == 0)
{
break;
}
}
}
// Now I have the color, pop it into the cache for next time
this.colorMap.TryAdd(colorHash, colorIndex);
}
return colorIndex;
}
///
protected override List GetPalette()
{
return this.colors.ToList();
}
}
}