Click here to Skip to main content
15,892,674 members
Articles / Programming Languages / C#

A Simple - Yet Quite Powerful - Palette Quantizer in C#

Rate me:
Please Sign up or sign in to vote.
4.98/5 (104 votes)
28 Jul 2012CPOL21 min read 339.3K   12.8K   168  
A palette quantizer based on human perception
using System;
using System.Collections.Generic;
using System.Drawing;

namespace SimplePaletteQuantizer.Helpers
{
    public class QuantizationHelper
    {
        private static readonly Color BackgroundColor;
        private static readonly Double[] Factors;

        static QuantizationHelper()
        {
            BackgroundColor = SystemColors.Control;
            Factors = PrecalculateFactors();
        }

        /// <summary>
        /// Precalculates the alpha-fix values for all the possible alpha values (0-255).
        /// </summary>
        private static Double[] PrecalculateFactors()
        {
            Double[] result = new Double[256];

            for (Int32 value = 0; value < 256; value++)
            {
                result[value] = value / 255.0;
            }

            return result;
        }

        /// <summary>
        /// Converts the alpha blended color to a non-alpha blended color.
        /// </summary>
        /// <param name="color">The alpha blended color (ARGB).</param>
        /// <returns>The non-alpha blended color (RGB).</returns>
        internal static Color ConvertAlpha(Color color)
        {
            Color result = color;

            if (color.A < 255)
            {
                // performs a alpha blending (second color is BackgroundColor, by default a Control color)
                Double colorFactor = Factors[color.A];
                Double backgroundFactor = Factors[255 - color.A];
                Int32 red = (Int32) (color.R*colorFactor + BackgroundColor.R*backgroundFactor);
                Int32 green = (Int32) (color.G*colorFactor + BackgroundColor.G*backgroundFactor);
                Int32 blue = (Int32) (color.B*colorFactor + BackgroundColor.B*backgroundFactor);
                result = Color.FromArgb(255, red, green, blue);
            }

            return result;
        }

        /// <summary>
        /// Finds the closest color match in a given palette using Euclidean distance.
        /// </summary>
        /// <param name="color">The color to be matched.</param>
        /// <param name="palette">The palette to search in.</param>
        /// <returns>The palette index of the closest match.</returns>
        internal static Int32 GetNearestColor(Color color, IList<Color> palette)
        {
            // initializes the best difference, set it for worst possible, it can only get better
            Int32 bestIndex = 0;
            Int32 leastDifference = Int32.MaxValue;

            // goes thru all the colors in the palette, looking for the best match
            for (Int32 index = 0; index < palette.Count; index++)
            {
                Color targetColor = palette[index];

                // calculates a difference for all the color components
                Int32 deltaA = color.A - targetColor.A;
                Int32 deltaR = color.R - targetColor.R;
                Int32 deltaG = color.G - targetColor.G;
                Int32 deltaB = color.B - targetColor.B;

                // calculates a power of two
                Int32 factorA = deltaA * deltaA;
                Int32 factorR = deltaR * deltaR;
                Int32 factorG = deltaG * deltaG;
                Int32 factorB = deltaB * deltaB;

                // calculates the Euclidean distance, a square-root is not need 
                // as we're only comparing distance, not measuring it
                Int32 difference = factorA + factorR + factorG + factorB;

                // if a difference is zero, we're good because it won't get better
                if (difference == 0)
                {
                    bestIndex = index;
                    break;
                }

                // if a difference is the best so far, stores it as our best candidate
                if (difference < leastDifference)
                {
                    leastDifference = difference;
                    bestIndex = index;
                }
            }

            // returns the palette index of the most similar color
            return bestIndex;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Czech Republic Czech Republic
Contacts: EMAIL - smartk8@gmail.com

Comments and Discussions