// // 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(); } } }