diff --git a/GreenshotPlugin/Core/QuantizerHelper.cs b/GreenshotPlugin/Core/QuantizerHelper.cs
new file mode 100644
index 000000000..4929d2d96
--- /dev/null
+++ b/GreenshotPlugin/Core/QuantizerHelper.cs
@@ -0,0 +1,686 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Collections.Generic;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// This interface provides a color quantization capabilities.
+ ///
+ public interface IColorQuantizer {
+ ///
+ /// Prepares the quantizer for image processing.
+ ///
+ /// The image.
+ void Prepare(Image image);
+
+ ///
+ /// Adds the color to quantizer.
+ ///
+ /// The color to be added.
+ void AddColor(Color color);
+
+ ///
+ /// Gets the palette with specified count of the colors.
+ ///
+ /// The color count.
+ ///
+ List GetPalette(Int32 colorCount);
+
+ ///
+ /// Gets the index of the palette for specific color.
+ ///
+ /// The color.
+ ///
+ Int32 GetPaletteIndex(Color color);
+
+ ///
+ /// Gets the color count.
+ ///
+ ///
+ Int32 GetColorCount();
+ }
+
+ internal class WuColorCube {
+ ///
+ /// Gets or sets the red minimum.
+ ///
+ /// The red minimum.
+ public Int32 RedMinimum { get; set; }
+
+ ///
+ /// Gets or sets the red maximum.
+ ///
+ /// The red maximum.
+ public Int32 RedMaximum { get; set; }
+
+ ///
+ /// Gets or sets the green minimum.
+ ///
+ /// The green minimum.
+ public Int32 GreenMinimum { get; set; }
+
+ ///
+ /// Gets or sets the green maximum.
+ ///
+ /// The green maximum.
+ public Int32 GreenMaximum { get; set; }
+
+ ///
+ /// Gets or sets the blue minimum.
+ ///
+ /// The blue minimum.
+ public Int32 BlueMinimum { get; set; }
+
+ ///
+ /// Gets or sets the blue maximum.
+ ///
+ /// The blue maximum.
+ public Int32 BlueMaximum { get; set; }
+
+ ///
+ /// Gets or sets the cube volume.
+ ///
+ /// The volume.
+ public Int32 Volume { get; set; }
+ }
+
+ public class QuantizationHelper {
+ private static readonly Color BackgroundColor;
+ private static readonly Double[] Factors;
+
+ static QuantizationHelper() {
+ BackgroundColor = Color.White;
+ Factors = PrecalculateFactors();
+ }
+
+ ///
+ /// Precalculates the alpha-fix values for all the possible alpha values (0-255).
+ ///
+ private static Double[] PrecalculateFactors() {
+ Double[] result = new Double[256];
+
+ for (Int32 value = 0; value < 256; value++) {
+ result[value] = value / 255.0;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Converts the alpha blended color to a non-alpha blended color.
+ ///
+ /// The alpha blended color (ARGB).
+ /// The non-alpha blended color (RGB).
+ 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);
+ Int32 argb = 255 << 24 | red << 16 | green << 8 | blue;
+ result = Color.FromArgb(argb);
+ }
+
+ return result;
+ }
+ }
+ ///
+ /// Author: Xiaolin Wu
+ /// Dept. of Computer Science
+ /// Univ. of Western Ontario
+ /// London, Ontario N6A 5B7
+ /// wu@csd.uwo.ca
+ ///
+ public class WuColorQuantizer : IColorQuantizer {
+ #region | Constants |
+
+ private const Int32 MaxColor = 512;
+ private const Int32 Red = 2;
+ private const Int32 Green = 1;
+ private const Int32 Blue = 0;
+ private const Int32 SideSize = 33;
+ private const Int32 MaxSideIndex = 32;
+ private const Int32 MaxVolume = SideSize * SideSize * SideSize;
+
+ #endregion
+
+ #region | Fields |
+ BitArray bitArray;
+
+ private Int32[] reds;
+ private Int32[] greens;
+ private Int32[] blues;
+ private Int32[] sums;
+ private Int32[] indices;
+
+ private Int64[, ,] weights;
+ private Int64[, ,] momentsRed;
+ private Int64[, ,] momentsGreen;
+ private Int64[, ,] momentsBlue;
+ private Single[, ,] moments;
+
+ private Int32[] tag;
+ private Int32[] quantizedPixels;
+ private Int32[] table;
+ private Color[] pixels;
+
+ private Int32 imageSize;
+ private Int32 pixelIndex;
+
+ private WuColorCube[] cubes;
+
+ #endregion
+
+ #region | Helper methods |
+
+ ///
+ /// Converts the histogram to a series of moments.
+ ///
+ private void CalculateMoments() {
+ Int64[] area = new Int64[SideSize];
+ Int64[] areaRed = new Int64[SideSize];
+ Int64[] areaGreen = new Int64[SideSize];
+ Int64[] areaBlue = new Int64[SideSize];
+ Single[] area2 = new Single[SideSize];
+
+ for (Int32 redIndex = 1; redIndex <= MaxSideIndex; ++redIndex) {
+ for (Int32 index = 0; index <= MaxSideIndex; ++index) {
+ area[index] = 0;
+ areaRed[index] = 0;
+ areaGreen[index] = 0;
+ areaBlue[index] = 0;
+ area2[index] = 0;
+ }
+
+ for (Int32 greenIndex = 1; greenIndex <= MaxSideIndex; ++greenIndex) {
+ Int64 line = 0;
+ Int64 lineRed = 0;
+ Int64 lineGreen = 0;
+ Int64 lineBlue = 0;
+ Single line2 = 0.0f;
+
+ for (Int32 blueIndex = 1; blueIndex <= MaxSideIndex; ++blueIndex) {
+ line += weights[redIndex, greenIndex, blueIndex];
+ lineRed += momentsRed[redIndex, greenIndex, blueIndex];
+ lineGreen += momentsGreen[redIndex, greenIndex, blueIndex];
+ lineBlue += momentsBlue[redIndex, greenIndex, blueIndex];
+ line2 += moments[redIndex, greenIndex, blueIndex];
+
+ area[blueIndex] += line;
+ areaRed[blueIndex] += lineRed;
+ areaGreen[blueIndex] += lineGreen;
+ areaBlue[blueIndex] += lineBlue;
+ area2[blueIndex] += line2;
+
+ weights[redIndex, greenIndex, blueIndex] = weights[redIndex - 1, greenIndex, blueIndex] + area[blueIndex];
+ momentsRed[redIndex, greenIndex, blueIndex] = momentsRed[redIndex - 1, greenIndex, blueIndex] + areaRed[blueIndex];
+ momentsGreen[redIndex, greenIndex, blueIndex] = momentsGreen[redIndex - 1, greenIndex, blueIndex] + areaGreen[blueIndex];
+ momentsBlue[redIndex, greenIndex, blueIndex] = momentsBlue[redIndex - 1, greenIndex, blueIndex] + areaBlue[blueIndex];
+ moments[redIndex, greenIndex, blueIndex] = moments[redIndex - 1, greenIndex, blueIndex] + area2[blueIndex];
+ }
+ }
+ }
+ }
+
+ ///
+ /// Computes the volume of the cube in a specific moment.
+ ///
+ private static Int64 Volume(WuColorCube cube, Int64[, ,] moment) {
+ return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
+ moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
+ }
+
+ ///
+ /// Computes the volume of the cube in a specific moment. For the floating-point values.
+ ///
+ private static Single VolumeFloat(WuColorCube cube, Single[, ,] moment) {
+ return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
+ moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
+ }
+
+ ///
+ /// Splits the cube in given position, and color direction.
+ ///
+ private static Int64 Top(WuColorCube cube, Int32 direction, Int32 position, Int64[, ,] moment) {
+ switch (direction) {
+ case Red:
+ return (moment[position, cube.GreenMaximum, cube.BlueMaximum] -
+ moment[position, cube.GreenMaximum, cube.BlueMinimum] -
+ moment[position, cube.GreenMinimum, cube.BlueMaximum] +
+ moment[position, cube.GreenMinimum, cube.BlueMinimum]);
+
+ case Green:
+ return (moment[cube.RedMaximum, position, cube.BlueMaximum] -
+ moment[cube.RedMaximum, position, cube.BlueMinimum] -
+ moment[cube.RedMinimum, position, cube.BlueMaximum] +
+ moment[cube.RedMinimum, position, cube.BlueMinimum]);
+
+ case Blue:
+ return (moment[cube.RedMaximum, cube.GreenMaximum, position] -
+ moment[cube.RedMaximum, cube.GreenMinimum, position] -
+ moment[cube.RedMinimum, cube.GreenMaximum, position] +
+ moment[cube.RedMinimum, cube.GreenMinimum, position]);
+
+ default:
+ return 0;
+ }
+ }
+
+ ///
+ /// Splits the cube in a given color direction at its minimum.
+ ///
+ private static Int64 Bottom(WuColorCube cube, Int32 direction, Int64[, ,] moment) {
+ switch (direction) {
+ case Red:
+ return (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
+
+ case Green:
+ return (-moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
+
+ case Blue:
+ return (-moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
+ moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
+ moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
+ moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
+ default:
+ return 0;
+ }
+ }
+
+ ///
+ /// Calculates statistical variance for a given cube.
+ ///
+ private Single CalculateVariance(WuColorCube cube) {
+ Single volumeRed = Volume(cube, momentsRed);
+ Single volumeGreen = Volume(cube, momentsGreen);
+ Single volumeBlue = Volume(cube, momentsBlue);
+ Single volumeMoment = VolumeFloat(cube, moments);
+ Single volumeWeight = Volume(cube, weights);
+
+ Single distance = volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue;
+
+ return volumeMoment - (distance / volumeWeight);
+ }
+
+ ///
+ /// Finds the optimal (maximal) position for the cut.
+ ///
+ private Single Maximize(WuColorCube cube, Int32 direction, Int32 first, Int32 last, Int32[] cut, Int64 wholeRed, Int64 wholeGreen, Int64 wholeBlue, Int64 wholeWeight) {
+ Int64 bottomRed = Bottom(cube, direction, momentsRed);
+ Int64 bottomGreen = Bottom(cube, direction, momentsGreen);
+ Int64 bottomBlue = Bottom(cube, direction, momentsBlue);
+ Int64 bottomWeight = Bottom(cube, direction, weights);
+
+ Single result = 0.0f;
+ cut[0] = -1;
+
+ for (Int32 position = first; position < last; ++position) {
+ // determines the cube cut at a certain position
+ Int64 halfRed = bottomRed + Top(cube, direction, position, momentsRed);
+ Int64 halfGreen = bottomGreen + Top(cube, direction, position, momentsGreen);
+ Int64 halfBlue = bottomBlue + Top(cube, direction, position, momentsBlue);
+ Int64 halfWeight = bottomWeight + Top(cube, direction, position, weights);
+
+ // the cube cannot be cut at bottom (this would lead to empty cube)
+ if (halfWeight != 0) {
+ Single halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
+ Single temp = halfDistance / halfWeight;
+
+ halfRed = wholeRed - halfRed;
+ halfGreen = wholeGreen - halfGreen;
+ halfBlue = wholeBlue - halfBlue;
+ halfWeight = wholeWeight - halfWeight;
+
+ if (halfWeight != 0) {
+ halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
+ temp += halfDistance / halfWeight;
+
+ if (temp > result) {
+ result = temp;
+ cut[0] = position;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Cuts a cube with another one.
+ ///
+ private Boolean Cut(WuColorCube first, WuColorCube second) {
+ Int32 direction;
+
+ Int32[] cutRed = { 0 };
+ Int32[] cutGreen = { 0 };
+ Int32[] cutBlue = { 0 };
+
+ Int64 wholeRed = Volume(first, momentsRed);
+ Int64 wholeGreen = Volume(first, momentsGreen);
+ Int64 wholeBlue = Volume(first, momentsBlue);
+ Int64 wholeWeight = Volume(first, weights);
+
+ Single maxRed = Maximize(first, Red, first.RedMinimum + 1, first.RedMaximum, cutRed, wholeRed, wholeGreen, wholeBlue, wholeWeight);
+ Single maxGreen = Maximize(first, Green, first.GreenMinimum + 1, first.GreenMaximum, cutGreen, wholeRed, wholeGreen, wholeBlue, wholeWeight);
+ Single maxBlue = Maximize(first, Blue, first.BlueMinimum + 1, first.BlueMaximum, cutBlue, wholeRed, wholeGreen, wholeBlue, wholeWeight);
+
+ if ((maxRed >= maxGreen) && (maxRed >= maxBlue)) {
+ direction = Red;
+
+ // cannot split empty cube
+ if (cutRed[0] < 0) return false;
+ } else {
+ if ((maxGreen >= maxRed) && (maxGreen >= maxBlue)) {
+ direction = Green;
+ } else {
+ direction = Blue;
+ }
+ }
+
+ second.RedMaximum = first.RedMaximum;
+ second.GreenMaximum = first.GreenMaximum;
+ second.BlueMaximum = first.BlueMaximum;
+
+ // cuts in a certain direction
+ switch (direction) {
+ case Red:
+ second.RedMinimum = first.RedMaximum = cutRed[0];
+ second.GreenMinimum = first.GreenMinimum;
+ second.BlueMinimum = first.BlueMinimum;
+ break;
+
+ case Green:
+ second.GreenMinimum = first.GreenMaximum = cutGreen[0];
+ second.RedMinimum = first.RedMinimum;
+ second.BlueMinimum = first.BlueMinimum;
+ break;
+
+ case Blue:
+ second.BlueMinimum = first.BlueMaximum = cutBlue[0];
+ second.RedMinimum = first.RedMinimum;
+ second.GreenMinimum = first.GreenMinimum;
+ break;
+ }
+
+ // determines the volumes after cut
+ first.Volume = (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
+ second.Volume = (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);
+
+ // the cut was successfull
+ return true;
+ }
+
+ ///
+ /// Marks all the tags with a given label.
+ ///
+ private void Mark(WuColorCube cube, Int32 label, Int32[] tag) {
+ for (Int32 redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex) {
+ for (Int32 greenIndex = cube.GreenMinimum + 1; greenIndex <= cube.GreenMaximum; ++greenIndex) {
+ for (Int32 blueIndex = cube.BlueMinimum + 1; blueIndex <= cube.BlueMaximum; ++blueIndex) {
+ tag[(redIndex << 10) + (redIndex << 6) + redIndex + (greenIndex << 5) + greenIndex + blueIndex] = label;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region << IColorQuantizer >>
+
+ ///
+ /// See for more details.
+ ///
+ public void Prepare(Image image) {
+ bitArray = new BitArray((int)Math.Pow(2, 24));
+ // creates all the cubes
+ cubes = new WuColorCube[MaxColor];
+
+ // initializes all the cubes
+ for (Int32 cubeIndex = 0; cubeIndex < MaxColor; cubeIndex++) {
+ cubes[cubeIndex] = new WuColorCube();
+ }
+
+ // resets the reference minimums
+ cubes[0].RedMinimum = 0;
+ cubes[0].GreenMinimum = 0;
+ cubes[0].BlueMinimum = 0;
+
+ // resets the reference maximums
+ cubes[0].RedMaximum = MaxSideIndex;
+ cubes[0].GreenMaximum = MaxSideIndex;
+ cubes[0].BlueMaximum = MaxSideIndex;
+
+ weights = new Int64[SideSize, SideSize, SideSize];
+ momentsRed = new Int64[SideSize, SideSize, SideSize];
+ momentsGreen = new Int64[SideSize, SideSize, SideSize];
+ momentsBlue = new Int64[SideSize, SideSize, SideSize];
+ moments = new Single[SideSize, SideSize, SideSize];
+
+ table = new Int32[256];
+
+ for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) {
+ table[tableIndex] = tableIndex * tableIndex;
+ }
+
+ pixelIndex = 0;
+ imageSize = image.Width * image.Height;
+
+ quantizedPixels = new Int32[imageSize];
+ pixels = new Color[imageSize];
+ }
+
+ ///
+ /// See for more details.
+ ///
+ public void AddColor(Color color) {
+ color = QuantizationHelper.ConvertAlpha(color);
+
+ // To count the colors
+ bitArray.Set(color.ToArgb() & 0x00ffffff, true);
+
+ Int32 indexRed = (color.R >> 3) + 1;
+ Int32 indexGreen = (color.G >> 3) + 1;
+ Int32 indexBlue = (color.B >> 3) + 1;
+
+ weights[indexRed, indexGreen, indexBlue]++;
+ momentsRed[indexRed, indexGreen, indexBlue] += color.R;
+ momentsGreen[indexRed, indexGreen, indexBlue] += color.G;
+ momentsBlue[indexRed, indexGreen, indexBlue] += color.B;
+ moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B];
+
+ quantizedPixels[pixelIndex] = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue;
+ pixels[pixelIndex] = color;
+ pixelIndex++;
+ }
+
+ ///
+ /// See for more details.
+ ///
+ public Int32 GetColorCount() {
+ int result = 0;
+
+ for (int i = 0; i < bitArray.Length; i++) {
+ if (bitArray.Get(i)) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// See for more details.
+ ///
+ public List GetPalette(int colorCount) {
+ // preprocess the colors
+ CalculateMoments();
+
+ Int32 next = 0;
+ Single[] volumeVariance = new Single[MaxColor];
+
+ // processes the cubes
+ for (Int32 cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex) {
+ // if cut is possible; make it
+ if (Cut(cubes[next], cubes[cubeIndex])) {
+ volumeVariance[next] = cubes[next].Volume > 1 ? CalculateVariance(cubes[next]) : 0.0f;
+ volumeVariance[cubeIndex] = cubes[cubeIndex].Volume > 1 ? CalculateVariance(cubes[cubeIndex]) : 0.0f;
+ } else // the cut was not possible, revert the index
+ {
+ volumeVariance[next] = 0.0f;
+ cubeIndex--;
+ }
+
+ next = 0;
+ Single temp = volumeVariance[0];
+
+ for (Int32 index = 1; index <= cubeIndex; ++index) {
+ if (volumeVariance[index] > temp) {
+ temp = volumeVariance[index];
+ next = index;
+ }
+ }
+
+ if (temp <= 0.0) {
+ colorCount = cubeIndex + 1;
+ break;
+ }
+ }
+
+ Int32[] lookupRed = new Int32[MaxColor];
+ Int32[] lookupGreen = new Int32[MaxColor];
+ Int32[] lookupBlue = new Int32[MaxColor];
+
+ tag = new Int32[MaxVolume];
+
+ // precalculates lookup tables
+ for (int k = 0; k < colorCount; ++k) {
+ Mark(cubes[k], k, tag);
+
+ long weight = Volume(cubes[k], weights);
+
+ if (weight > 0) {
+ lookupRed[k] = (int)(Volume(cubes[k], momentsRed) / weight);
+ lookupGreen[k] = (int)(Volume(cubes[k], momentsGreen) / weight);
+ lookupBlue[k] = (int)(Volume(cubes[k], momentsBlue) / weight);
+ } else {
+ lookupRed[k] = 0;
+ lookupGreen[k] = 0;
+ lookupBlue[k] = 0;
+ }
+ }
+
+ // copies the per pixel tags
+ for (Int32 index = 0; index < imageSize; ++index) {
+ quantizedPixels[index] = tag[quantizedPixels[index]];
+ }
+
+ reds = new Int32[colorCount + 1];
+ greens = new Int32[colorCount + 1];
+ blues = new Int32[colorCount + 1];
+ sums = new Int32[colorCount + 1];
+ indices = new Int32[imageSize];
+
+ // scans and adds colors
+ for (Int32 index = 0; index < imageSize; index++) {
+ Color color = pixels[index];
+
+ Int32 match = quantizedPixels[index];
+ Int32 bestMatch = match;
+ Int32 bestDistance = 100000000;
+
+ for (Int32 lookup = 0; lookup < colorCount; lookup++) {
+ Int32 foundRed = lookupRed[lookup];
+ Int32 foundGreen = lookupGreen[lookup];
+ Int32 foundBlue = lookupBlue[lookup];
+ Int32 deltaRed = color.R - foundRed;
+ Int32 deltaGreen = color.G - foundGreen;
+ Int32 deltaBlue = color.B - foundBlue;
+
+ Int32 distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
+
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestMatch = lookup;
+ }
+ }
+
+ reds[bestMatch] += color.R;
+ greens[bestMatch] += color.G;
+ blues[bestMatch] += color.B;
+ sums[bestMatch]++;
+
+ indices[index] = bestMatch;
+ }
+
+ List result = new List();
+
+ // generates palette
+ for (Int32 paletteIndex = 0; paletteIndex < colorCount; paletteIndex++) {
+ if (sums[paletteIndex] > 0) {
+ reds[paletteIndex] /= sums[paletteIndex];
+ greens[paletteIndex] /= sums[paletteIndex];
+ blues[paletteIndex] /= sums[paletteIndex];
+ }
+
+ Color color = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]);
+ result.Add(color);
+ }
+
+ pixelIndex = 0;
+ return result;
+ }
+
+ ///
+ /// See for more details.
+ ///
+ public Int32 GetPaletteIndex(Color color) {
+ return indices[pixelIndex++];
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file