using System.Collections.Concurrent;
namespace Terminal.Gui;
///
/// Translates colors in an image into a Palette of up to colors (typically 256).
///
public class ColorQuantizer
{
///
/// Gets the current colors in the palette based on the last call to
/// .
///
public IReadOnlyCollection Palette { get; private set; } = new List ();
///
/// Gets or sets the maximum number of colors to put into the .
/// Defaults to 256 (the maximum for sixel images).
///
public int MaxColors { get; set; } = 256;
///
/// Gets or sets the algorithm used to map novel colors into existing
/// palette colors (closest match). Defaults to
///
public IColorDistance DistanceAlgorithm { get; set; } = new EuclideanColorDistance ();
///
/// Gets or sets the algorithm used to build the .
///
public IPaletteBuilder PaletteBuildingAlgorithm { get; set; } = new PopularityPaletteWithThreshold (new EuclideanColorDistance (), 8);
private readonly ConcurrentDictionary _nearestColorCache = new ();
///
/// Builds a of colors that most represent the colors used in image.
/// This is based on the currently configured .
///
///
public void BuildPalette (Color [,] pixels)
{
List allColors = new ();
int width = pixels.GetLength (0);
int height = pixels.GetLength (1);
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
allColors.Add (pixels [x, y]);
}
}
_nearestColorCache.Clear ();
Palette = PaletteBuildingAlgorithm.BuildPalette (allColors, MaxColors);
}
///
/// Returns the closest color in that matches
/// based on the color comparison algorithm defined by
///
///
///
public int GetNearestColor (Color toTranslate)
{
if (_nearestColorCache.TryGetValue (toTranslate, out int cachedAnswer))
{
return cachedAnswer;
}
// Simple nearest color matching based on DistanceAlgorithm
var minDistance = double.MaxValue;
var nearestIndex = 0;
for (var index = 0; index < Palette.Count; index++)
{
Color color = Palette.ElementAt (index);
double distance = DistanceAlgorithm.CalculateDistance (color, toTranslate);
if (distance < minDistance)
{
minDistance = distance;
nearestIndex = index;
}
}
_nearestColorCache.TryAdd (toTranslate, nearestIndex);
return nearestIndex;
}
}