From 0c5d0d7607ca45429b64cf254cbd7f85afff588b Mon Sep 17 00:00:00 2001 From: James South Date: Fri, 7 Nov 2014 12:49:30 +0000 Subject: [PATCH] Use Color32 structure. Former-commit-id: 7766fb32652db5587004bea4da2f9abbc0af17a6 Former-commit-id: c7a35226816f4229d69cfe7571e9653932f9b6f1 --- src/ImageProcessor.Playground/Program.cs | 2 +- src/ImageProcessor/ImageProcessor.csproj | 4 +- src/ImageProcessor/Imaging/Colors/Color32.cs | 3 + .../Imaging/Formats/PngFormat.cs | 26 +---- .../Quantizers/WuQuantizer/ColorMoment.cs | 12 +- .../Quantizers/WuQuantizer/ImageBuffer.cs | 7 +- .../WuQuantizer/PaletteColorHistory.cs | 12 +- .../Quantizers/WuQuantizer/PaletteLookup.cs | 36 +++--- .../Imaging/Quantizers/WuQuantizer/Pixel.cs | 104 ------------------ .../Quantizers/WuQuantizer/WuQuantizer.cs | 16 +-- .../Quantizers/WuQuantizer/WuQuantizerBase.cs | 53 +++++---- 11 files changed, 86 insertions(+), 189 deletions(-) delete mode 100644 src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 8fc789a8a..603aee091 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -94,7 +94,7 @@ namespace ImageProcessor.PlayGround //.EntropyCrop() //.Filter(MatrixFilters.Invert) //.Contrast(50) - .Filter(MatrixFilters.Comic) + //.Filter(MatrixFilters.Comic) //.Flip() //.Filter(MatrixFilters.HiSatch) //.Pixelate(8) diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index d47dd3b5d..8b855632f 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -182,6 +182,7 @@ + @@ -192,9 +193,6 @@ - - Code - diff --git a/src/ImageProcessor/Imaging/Colors/Color32.cs b/src/ImageProcessor/Imaging/Colors/Color32.cs index 06d336932..8e919eefb 100644 --- a/src/ImageProcessor/Imaging/Colors/Color32.cs +++ b/src/ImageProcessor/Imaging/Colors/Color32.cs @@ -76,6 +76,9 @@ namespace ImageProcessor.Imaging.Colors this.R = red; this.G = green; this.B = blue; + + // Stolen from Color. + this.Argb = (int)(((uint)((((red << 0x10) | (green << 8)) | blue) | (alpha << 0x18))) & 0xffffffffL); } /// diff --git a/src/ImageProcessor/Imaging/Formats/PngFormat.cs b/src/ImageProcessor/Imaging/Formats/PngFormat.cs index 88a187d7e..998e46b99 100644 --- a/src/ImageProcessor/Imaging/Formats/PngFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/PngFormat.cs @@ -14,7 +14,6 @@ namespace ImageProcessor.Imaging.Formats using System.Drawing.Imaging; using System.IO; - using ImageProcessor.Imaging.Quantizers; using ImageProcessor.Imaging.Quantizers.WuQuantizer; /// @@ -80,7 +79,7 @@ namespace ImageProcessor.Imaging.Formats { if (this.IsIndexed) { - image = new OctreeQuantizer(255, 8).Quantize(image); + image = new WuQuantizer().Quantize(image); } return base.Save(stream, image); @@ -99,28 +98,7 @@ namespace ImageProcessor.Imaging.Formats { if (this.IsIndexed) { - // The Wu Quantizer expects a 32bbp image. - //if (Image.GetPixelFormatSize(image.PixelFormat) != 32) - //{ - Bitmap clone = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); - clone.SetResolution(image.HorizontalResolution, image.VerticalResolution); - - using (Graphics graphics = Graphics.FromImage(clone)) - { - graphics.Clear(Color.Transparent); - graphics.DrawImage(image, new Rectangle(0, 0, clone.Width, clone.Height)); - } - - image.Dispose(); - - image = new WuQuantizer().QuantizeImage(clone); - - // image = new OctreeQuantizer(255, 8).Quantize(image); - //} - //else - //{ - // image = new WuQuantizer().QuantizeImage((Bitmap)image); - //} + image = new WuQuantizer().Quantize(image); } return base.Save(path, image); diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs index 542aabbd4..6d6aa05b8 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs @@ -11,6 +11,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { + using ImageProcessor.Imaging.Colors; + /// /// The cumulative color moment for holding pixel information. /// Adapted from @@ -119,12 +121,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The pixel to add. /// - public void Add(Pixel pixel) + public void Add(Color32 pixel) { - byte alpha = pixel.Alpha; - byte red = pixel.Red; - byte green = pixel.Green; - byte blue = pixel.Blue; + byte alpha = pixel.A; + byte red = pixel.R; + byte green = pixel.G; + byte blue = pixel.B; this.Alpha += alpha; this.Red += red; this.Green += green; diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs index 1bdcc4749..19fee034c 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs @@ -18,6 +18,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer using System.Runtime.InteropServices; using ImageProcessor.Common.Exceptions; + using ImageProcessor.Imaging.Colors; /// /// The image buffer for storing and manipulating pixel information. @@ -47,13 +48,13 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// Thrown if the given image is not a 32 bit per pixel image. /// - public IEnumerable PixelLines + public IEnumerable PixelLines { get { int width = this.Image.Width; int height = this.Image.Height; - Pixel[] pixels = new Pixel[width]; + Color32[] pixels = new Color32[width]; using (FastBitmap bitmap = new FastBitmap(this.Image)) { @@ -62,7 +63,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer for (int x = 0; x < width; x++) { Color color = bitmap.GetPixel(x, y); - pixels[x] = new Pixel(color.A, color.R, color.G, color.B); + pixels[x] = new Color32(color.A, color.R, color.G, color.B); } yield return pixels; diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs index 3f0cafb65..ee155b7de 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs @@ -13,6 +13,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { using System.Drawing; + using ImageProcessor.Imaging.Colors; + /// /// The palette color history containing the sum of all pixel data. /// Adapted from @@ -61,12 +63,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The pixel to add. /// - public void AddPixel(Pixel pixel) + public void AddPixel(Color32 pixel) { - this.Alpha += pixel.Alpha; - this.Red += pixel.Red; - this.Green += pixel.Green; - this.Blue += pixel.Blue; + this.Alpha += pixel.A; + this.Red += pixel.R; + this.Green += pixel.G; + this.Blue += pixel.B; this.Sum++; } } diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs index d69cfb240..f00438bf1 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs @@ -15,6 +15,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer using System.Collections.Generic; using System.Linq; + using ImageProcessor.Imaging.Colors; + /// /// Stores the indexed color palette of an image for fast access. /// Adapted from @@ -37,14 +39,14 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The palette. /// - public PaletteLookup(Pixel[] palette) + public PaletteLookup(Color32[] palette) { this.Palette = new LookupNode[palette.Length]; for (int paletteIndex = 0; paletteIndex < palette.Length; paletteIndex++) { this.Palette[paletteIndex] = new LookupNode { - Pixel = palette[paletteIndex], + Color32 = palette[paletteIndex], PaletteIndex = (byte)paletteIndex }; } @@ -66,7 +68,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The representing the index. /// - public byte GetPaletteIndex(Pixel pixel) + public byte GetPaletteIndex(Color32 pixel) { int pixelKey = pixel.Argb & this.paletteMask; LookupNode[] bucket; @@ -84,18 +86,18 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer byte bestMatch = 0; foreach (LookupNode lookup in bucket) { - Pixel lookupPixel = lookup.Pixel; + Color32 lookupPixel = lookup.Color32; - int deltaAlpha = pixel.Alpha - lookupPixel.Alpha; + int deltaAlpha = pixel.A - lookupPixel.A; int distance = deltaAlpha * deltaAlpha; - int deltaRed = pixel.Red - lookupPixel.Red; + int deltaRed = pixel.R - lookupPixel.R; distance += deltaRed * deltaRed; - int deltaGreen = pixel.Green - lookupPixel.Green; + int deltaGreen = pixel.G - lookupPixel.G; distance += deltaGreen * deltaGreen; - int deltaBlue = pixel.Blue - lookupPixel.Blue; + int deltaBlue = pixel.B - lookupPixel.B; distance += deltaBlue * deltaBlue; if (distance >= bestDistance) @@ -159,21 +161,21 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The representing the component value of the mask. /// - private static int GetMask(Pixel[] palette) + private static int GetMask(Color32[] palette) { - IEnumerable alphas = palette.Select(p => p.Alpha).ToArray(); + IEnumerable alphas = palette.Select(p => p.A).ToArray(); byte maxAlpha = alphas.Max(); int uniqueAlphas = alphas.Distinct().Count(); - IEnumerable reds = palette.Select(p => p.Red).ToArray(); + IEnumerable reds = palette.Select(p => p.R).ToArray(); byte maxRed = reds.Max(); int uniqueReds = reds.Distinct().Count(); - IEnumerable greens = palette.Select(p => p.Green).ToArray(); + IEnumerable greens = palette.Select(p => p.G).ToArray(); byte maxGreen = greens.Max(); int uniqueGreens = greens.Distinct().Count(); - IEnumerable blues = palette.Select(p => p.Green).ToArray(); + IEnumerable blues = palette.Select(p => p.B).ToArray(); byte maxBlue = blues.Max(); int uniqueBlues = blues.Distinct().Count(); @@ -186,7 +188,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer byte greenMask = ComputeBitMask(maxGreen, Convert.ToInt32(Math.Round(uniqueGreens / totalUniques * availableBits))); byte blueMask = ComputeBitMask(maxBlue, Convert.ToInt32(Math.Round(uniqueBlues / totalUniques * availableBits))); - Pixel maskedPixel = new Pixel(alphaMask, redMask, greenMask, blueMask); + Color32 maskedPixel = new Color32(alphaMask, redMask, greenMask, blueMask); return maskedPixel.Argb; } @@ -221,13 +223,13 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The palette. /// - private void BuildLookup(Pixel[] palette) + private void BuildLookup(Color32[] palette) { int mask = GetMask(palette); Dictionary> tempLookup = new Dictionary>(); foreach (LookupNode lookup in this.Palette) { - int pixelKey = lookup.Pixel.Argb & mask; + int pixelKey = lookup.Color32.Argb & mask; List bucket; if (!tempLookup.TryGetValue(pixelKey, out bucket)) @@ -261,7 +263,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The pixel. /// - public Pixel Pixel; + public Color32 Color32; } } } \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs deleted file mode 100644 index be63cda10..000000000 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs +++ /dev/null @@ -1,104 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. -// -// -------------------------------------------------------------------------------------------------------------------- - - - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace ImageProcessor.Imaging.Quantizers.WuQuantizer -{ - /// - /// The pixel. - /// - [StructLayout(LayoutKind.Explicit)] - public struct Pixel - { - /// - /// Initializes a new instance of the struct. - /// - /// - /// The alpha. - /// - /// - /// The red. - /// - /// - /// The green. - /// - /// - /// The blue. - /// - public Pixel(byte alpha, byte red, byte green, byte blue) - : this() - { - Alpha = alpha; - Red = red; - Green = green; - Blue = blue; - - Debug.Assert(Argb == (alpha << 24 | red << 16 | green << 8 | blue)); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The argb. - /// - public Pixel(int argb) - : this() - { - Argb = argb; - Debug.Assert(Alpha == ((uint)argb >> 24)); - Debug.Assert(Red == ((uint)(argb >> 16) & 255)); - Debug.Assert(Green == ((uint)(argb >> 8) & 255)); - Debug.Assert(Blue == ((uint)argb & 255)); - } - - /// - /// The alpha. - /// - [FieldOffset(3)] - public byte Alpha; - - /// - /// The red. - /// - [FieldOffset(2)] - public byte Red; - - /// - /// The green. - /// - [FieldOffset(1)] - public byte Green; - - /// - /// The blue. - /// - [FieldOffset(0)] - public byte Blue; - - /// - /// The argb. - /// - [FieldOffset(0)] - public int Argb; - - /// - /// The to string. - /// - /// - /// The . - /// - public override string ToString() - { - return string.Format("Alpha:{0} Red:{1} Green:{2} Blue:{3}", Alpha, Red, Green, Blue); - } - } -} \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs index dd4936d7e..40f82caee 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs @@ -16,6 +16,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer using System.Drawing; using System.Drawing.Imaging; + using ImageProcessor.Imaging.Colors; + /// /// Encapsulates methods to calculate the color palette of an image using /// a Wu color quantizer . @@ -33,7 +35,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// The maximum number of colors apply to the image. /// /// - /// The array of containing indexed versions of the images colors. + /// The array of containing indexed versions of the images colors. /// /// /// All colors with an alpha value less than this will be considered fully transparent. @@ -41,7 +43,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The quantized . /// - internal override Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Pixel[] lookups, int alphaThreshold) + 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); @@ -82,7 +84,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// The for storing and manipulating pixel information. /// /// - /// The array of containing indexed versions of the images colors. + /// The array of containing indexed versions of the images colors. /// /// /// The alpha threshold. @@ -93,21 +95,21 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// /// The enumerable list of representing each pixel. /// - private static IEnumerable IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram) + private static IEnumerable 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 (Pixel[] pixelLine in image.PixelLines) + foreach (Color32[] pixelLine in image.PixelLines) { int length = pixelLine.Length; for (int i = 0; i < length; i++) { - Pixel pixel = pixelLine[i]; + Color32 pixel = pixelLine[i]; byte bestMatch = fallback; - if (pixel.Alpha > alphaThreshold) + if (pixel.A > alphaThreshold) { bestMatch = lookup.GetPaletteIndex(pixel); paletteHistogram[bestMatch].AddPixel(pixel); diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs index 8e801bc9c..ee212abcc 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs @@ -1,4 +1,16 @@ -namespace ImageProcessor.Imaging.Quantizers.WuQuantizer +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Encapsulates methods to calculate the color palette of an image using +// a Wu color quantizer . +// Adapted from +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { using System; using System.Diagnostics.CodeAnalysis; @@ -7,6 +19,7 @@ using System.Linq; using ImageProcessor.Common.Exceptions; + using ImageProcessor.Imaging.Colors; /// /// Encapsulates methods to calculate the color palette of an image using @@ -148,7 +161,7 @@ BuildHistogram(histogram, buffer, alphaThreshold, alphaFader); CalculateMoments(histogram.Moments); Box[] cubes = SplitData(ref maxColors, histogram.Moments); - Pixel[] lookups = BuildLookups(cubes, histogram.Moments); + Color32[] lookups = BuildLookups(cubes, histogram.Moments); return this.GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold); } catch (Exception ex) @@ -167,7 +180,7 @@ /// The maximum number of colors apply to the image. /// /// - /// The array of containing indexed versions of the images colors. + /// The array of containing indexed versions of the images colors. /// /// /// All colors with an alpha value less than this will be considered fully transparent. @@ -175,7 +188,7 @@ /// /// The quantized . /// - internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Pixel[] lookups, int alphaThreshold); + internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold); /// /// Builds a histogram from the current image. @@ -197,22 +210,22 @@ { ColorMoment[, , ,] moments = histogram.Moments; - foreach (Pixel[] pixelLine in imageBuffer.PixelLines) + foreach (Color32[] pixelLine in imageBuffer.PixelLines) { - foreach (Pixel pixel in pixelLine) + foreach (Color32 pixel in pixelLine) { - byte pixelAlpha = pixel.Alpha; + byte pixelAlpha = pixel.A; if (pixelAlpha > alphaThreshold) { if (pixelAlpha < 255) { - int alpha = pixel.Alpha + (pixel.Alpha % alphaFader); + int alpha = pixel.A + (pixel.A % alphaFader); pixelAlpha = (byte)(alpha > 255 ? 255 : alpha); } - byte pixelRed = pixel.Red; - byte pixelGreen = pixel.Green; - byte pixelBlue = pixel.Blue; + byte pixelRed = pixel.R; + byte pixelGreen = pixel.G; + byte pixelBlue = pixel.B; pixelAlpha = (byte)((pixelAlpha >> 3) + 1); pixelRed = (byte)((pixelRed >> 3) + 1); @@ -224,7 +237,7 @@ } // Set a default pixel for images with less than 256 colors. - moments[0, 0, 0, 0].Add(new Pixel(0, 0, 0, 0)); + moments[0, 0, 0, 0].Add(new Color32(0, 0, 0, 0)); } /// @@ -689,12 +702,12 @@ /// The three dimensional array of . /// /// - /// The array of . + /// The array of . /// [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")] - private static Pixel[] BuildLookups(Box[] cubes, ColorMoment[, , ,] moments) + private static Color32[] BuildLookups(Box[] cubes, ColorMoment[, , ,] moments) { - Pixel[] lookups = new Pixel[cubes.Length]; + Color32[] lookups = new Color32[cubes.Length]; for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++) { @@ -705,12 +718,12 @@ continue; } - Pixel lookup = new Pixel + Color32 lookup = new Color32 { - Alpha = (byte)(volume.Alpha / volume.Weight), - Red = (byte)(volume.Red / volume.Weight), - Green = (byte)(volume.Green / volume.Weight), - Blue = (byte)(volume.Blue / volume.Weight) + A = (byte)(volume.Alpha / volume.Weight), + R = (byte)(volume.Red / volume.Weight), + G = (byte)(volume.Green / volume.Weight), + B = (byte)(volume.Blue / volume.Weight) }; lookups[cubeIndex] = lookup;