/// <summary> /// Quantizes the image contained within the <see cref="ImageBuffer"/> returning the result. /// </summary> /// <param name="imageBuffer"> /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information.. /// </param> /// <param name="colorCount"> /// The maximum number of colors apply to the image. /// </param> /// <param name="lookups"> /// The array of <see cref="Color32"/> containing indexed versions of the images colors. /// </param> /// <param name="alphaThreshold"> /// All colors with an alpha value less than this will be considered fully transparent. /// </param> /// <returns> /// The quantized <see cref="Bitmap"/>. /// </returns> internal override Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold) { Bitmap result = new Bitmap(imageBuffer.Image.Width, imageBuffer.Image.Height, PixelFormat.Format8bppIndexed); result.SetResolution(imageBuffer.Image.HorizontalResolution, imageBuffer.Image.VerticalResolution); ImageBuffer resultBuffer = new ImageBuffer(result); PaletteColorHistory[] paletteHistogram = new PaletteColorHistory[colorCount + 1]; resultBuffer.UpdatePixelIndexes(IndexedPixels(imageBuffer, lookups, alphaThreshold, paletteHistogram)); result.Palette = BuildPalette(result.Palette, paletteHistogram); return result; }
private static void BuildHistogram(Histogram histogram, ImageBuffer imageBuffer, int alphaThreshold, int alphaFader) { ColorMoment[, , ,] moments = histogram.Moments; foreach (Color32[] pixelLine in imageBuffer.PixelLines) { foreach (Color32 pixel in pixelLine) { byte pixelAlpha = pixel.A; if (pixelAlpha > alphaThreshold) { if (pixelAlpha < 255) { int alpha = pixel.A + (pixel.A % alphaFader); pixelAlpha = (byte)(alpha > 255 ? 255 : alpha); } byte pixelRed = pixel.R; byte pixelGreen = pixel.G; byte pixelBlue = pixel.B; pixelAlpha = (byte)((pixelAlpha >> 3) + 1); pixelRed = (byte)((pixelRed >> 3) + 1); pixelGreen = (byte)((pixelGreen >> 3) + 1); pixelBlue = (byte)((pixelBlue >> 3) + 1); moments[pixelAlpha, pixelRed, pixelGreen, pixelBlue].Add(pixel); } } } // Set a default pixel for images with less than 256 colors. moments[0, 0, 0, 0].Add(new Color32(0, 0, 0, 0)); }
/// <summary> /// Quantizes the image contained within the <see cref="ImageBuffer"/> returning the result. /// </summary> /// <param name="imageBuffer"> /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information.. /// </param> /// <param name="colorCount"> /// The maximum number of colors apply to the image. /// </param> /// <param name="lookups"> /// The array of <see cref="Color32"/> containing indexed versions of the images colors. /// </param> /// <param name="alphaThreshold"> /// All colors with an alpha value less than this will be considered fully transparent. /// </param> /// <returns> /// The quantized <see cref="Bitmap"/>. /// </returns> internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold);
/// <summary> /// Quantize an image and return the resulting output bitmap /// </summary> /// <param name="source"> /// The 32 bit per pixel image to quantize. /// </param> /// <param name="alphaThreshold"> /// All colors with an alpha value less than this will be considered fully transparent. /// </param> /// <param name="alphaFader"> /// Alpha values will be normalized to the nearest multiple of this value. /// </param> /// <param name="histogram"> /// The <see cref="Histogram"/> representing the distribution of color data. /// </param> /// <param name="maxColors"> /// The maximum number of colors apply to the image. /// </param> /// <returns> /// A quantized version of the image. /// </returns> public Bitmap Quantize(Image source, int alphaThreshold, int alphaFader, Histogram histogram, int maxColors) { try { ImageBuffer buffer; // The image has to be a 32 bit per pixel Argb image. if (Image.GetPixelFormatSize(source.PixelFormat) != 32) { Bitmap clone = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppPArgb); clone.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (Graphics graphics = Graphics.FromImage(clone)) { graphics.PageUnit = GraphicsUnit.Pixel; graphics.Clear(Color.Transparent); graphics.DrawImageUnscaled(source, new Rectangle(0, 0, clone.Width, clone.Height)); } source.Dispose(); buffer = new ImageBuffer(clone); } else { buffer = new ImageBuffer((Bitmap)source); } if (histogram == null) { histogram = new Histogram(); } else { histogram.Clear(); } BuildHistogram(histogram, buffer, alphaThreshold, alphaFader); CalculateMoments(histogram.Moments); Box[] cubes = SplitData(ref maxColors, histogram.Moments); Color32[] lookups = BuildLookups(cubes, histogram.Moments); return this.GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold); } catch (Exception ex) { throw new QuantizationException(ex.Message, ex); } }
/// <summary> /// Gets an enumerable array of bytes representing each row of the image. /// </summary> /// <param name="image"> /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information. /// </param> /// <param name="lookups"> /// The array of <see cref="Color32"/> containing indexed versions of the images colors. /// </param> /// <param name="alphaThreshold"> /// The alpha threshold. /// </param> /// <param name="paletteHistogram"> /// The palette histogram. /// </param> /// <returns> /// The enumerable list of <see cref="byte"/> representing each pixel. /// </returns> private static IEnumerable<byte[]> IndexedPixels(ImageBuffer image, Color32[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram) { byte[] lineIndexes = new byte[image.Image.Width]; PaletteLookup lookup = new PaletteLookup(lookups); // Determine the correct fallback color. byte fallback = lookups.Length < AlphaMax ? AlphaMin : AlphaMax; foreach (Color32[] pixelLine in image.PixelLines) { int length = pixelLine.Length; for (int i = 0; i < length; i++) { Color32 pixel = pixelLine[i]; byte bestMatch = fallback; if (pixel.A > alphaThreshold) { bestMatch = lookup.GetPaletteIndex(pixel); paletteHistogram[bestMatch].AddPixel(pixel); } lineIndexes[i] = bestMatch; } yield return lineIndexes; } }