diff --git a/src/ImageProcessor/Imaging/Quantizers/IQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/IQuantizer.cs
new file mode 100644
index 000000000..697055af1
--- /dev/null
+++ b/src/ImageProcessor/Imaging/Quantizers/IQuantizer.cs
@@ -0,0 +1,31 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// The Quantizer interface for allowing quantization of images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging.Quantizers
+{
+ using System.Drawing;
+
+ ///
+ /// The Quantizer interface for allowing quantization of images.
+ ///
+ public interface IQuantizer
+ {
+ ///
+ /// Quantize an image and return the resulting output bitmap.
+ ///
+ ///
+ /// The image to quantize.
+ ///
+ ///
+ /// A quantized version of the image.
+ ///
+ Bitmap Quantize(Image source);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs
index 3d3d1f595..eb5014f7b 100644
--- a/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs
@@ -34,6 +34,21 @@ namespace ImageProcessor.Imaging.Quantizers
///
private readonly int maxColors;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The Octree quantizer is a two pass algorithm. The initial pass sets up the Octree,
+ /// the second pass quantizes a color based on the nodes in the tree.
+ ///
+ /// Defaults to return a maximum of 255 colors plus transparency with 8 significant bits.
+ ///
+ ///
+ public OctreeQuantizer()
+ : this(255, 8)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -69,7 +84,9 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Process the pixel in the first pass of the algorithm
///
- /// The pixel to quantize
+ ///
+ /// The pixel to quantize
+ ///
///
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
@@ -83,8 +100,12 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Override this to process the pixel in the second pass of the algorithm
///
- /// The pixel to quantize
- /// The quantized value
+ ///
+ /// The pixel to quantize
+ ///
+ ///
+ /// The quantized value
+ ///
protected override byte QuantizePixel(Color32* pixel)
{
// The color at [_maxColors] is set to transparent
@@ -102,8 +123,12 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Retrieve the palette for the quantized image
///
- /// Any old palette, this is overwritten
- /// The new color palette
+ ///
+ /// Any old palette, this is overwritten
+ ///
+ ///
+ /// The new color palette
+ ///
protected override ColorPalette GetPalette(ColorPalette original)
{
// First off convert the Octree to maxColors colors
@@ -228,8 +253,12 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Convert the nodes in the Octree to a palette with a maximum of colorCount colors
///
- /// The maximum number of colors
- /// An with the palletized colors
+ ///
+ /// The maximum number of colors
+ ///
+ ///
+ /// An with the palletized colors
+ ///
public ArrayList Palletize(int colorCount)
{
while (this.Leaves > colorCount)
@@ -263,7 +292,9 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Keep track of the previous node that was quantized
///
- /// The node last quantized
+ ///
+ /// The node last quantized
+ ///
protected void TrackPrevious(OctreeNode node)
{
this.previousNode = node;
@@ -385,10 +416,18 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Add a color into the tree
///
- /// The color
- /// The number of significant color bits
- /// The level in the tree
- /// The tree to which this node belongs
+ ///
+ /// The color
+ ///
+ ///
+ /// The number of significant color bits
+ ///
+ ///
+ /// The level in the tree
+ ///
+ ///
+ /// The tree to which this node belongs
+ ///
public void AddColor(Color32* pixel, int colorBits, int level, Octree octree)
{
// Update the color information if this is a leaf
@@ -454,8 +493,12 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Traverse the tree, building up the color palette
///
- /// The palette
- /// The current palette index
+ ///
+ /// The palette
+ ///
+ ///
+ /// The current palette index
+ ///
public void ConstructPalette(ArrayList palette, ref int index)
{
if (this.leaf)
diff --git a/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs b/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs
index 0c546d374..1a269da3f 100644
--- a/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs
@@ -20,7 +20,7 @@ namespace ImageProcessor.Imaging.Quantizers
/// Encapsulates methods to calculate the color palette of an image.
///
///
- public unsafe abstract class Quantizer
+ public unsafe abstract class Quantizer : IQuantizer
{
///
/// Flag used to indicate whether a single pass or two passes are needed for quantization.
@@ -46,8 +46,12 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Quantize an image and return the resulting output bitmap.
///
- /// The image to quantize.
- /// A quantized version of the image.
+ ///
+ /// The image to quantize.
+ ///
+ ///
+ /// A quantized version of the image.
+ ///
public Bitmap Quantize(Image source)
{
// Get the size of the source image
@@ -111,9 +115,15 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Execute the first pass through the pixels in the image
///
- /// The source data
- /// The width in pixels of the image
- /// The height in pixels of the image
+ ///
+ /// The source data
+ ///
+ ///
+ /// The width in pixels of the image
+ ///
+ ///
+ /// The height in pixels of the image
+ ///
protected virtual void FirstPass(BitmapData sourceData, int width, int height)
{
// Define the source data pointers. The source row is a byte to
@@ -141,11 +151,21 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Execute a second pass through the bitmap
///
- /// The source bitmap, locked into memory
- /// The output bitmap
- /// The width in pixels of the image
- /// The height in pixels of the image
- /// The bounding rectangle
+ ///
+ /// The source bitmap, locked into memory
+ ///
+ ///
+ /// The output bitmap
+ ///
+ ///
+ /// The width in pixels of the image
+ ///
+ ///
+ /// The height in pixels of the image
+ ///
+ ///
+ /// The bounding rectangle
+ ///
protected virtual void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
{
BitmapData outputData = null;
@@ -184,7 +204,7 @@ namespace ImageProcessor.Imaging.Quantizers
for (int col = 0; col < width; col++, sourcePixel++, destinationPixel++)
{
// Check if this is the same as the last pixel. If so use that value
- // rather than calculating it again. This is an inexpensive optimisation.
+ // rather than calculating it again. This is an inexpensive optimization.
if (*previousPixel != *sourcePixel)
{
// Quantize the pixel
@@ -215,7 +235,9 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Override this to process the pixel in the first pass of the algorithm
///
- /// The pixel to quantize
+ ///
+ /// The pixel to quantize
+ ///
///
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
@@ -227,15 +249,23 @@ namespace ImageProcessor.Imaging.Quantizers
///
/// Override this to process the pixel in the second pass of the algorithm
///
- /// The pixel to quantize
- /// The quantized value
+ ///
+ /// The pixel to quantize
+ ///
+ ///
+ /// The quantized value
+ ///
protected abstract byte QuantizePixel(Color32* pixel);
///
/// Retrieve the palette for the quantized image
///
- /// Any old palette, this is overwritten
- /// The new color palette
+ ///
+ /// Any old palette, this is overwritten
+ ///
+ ///
+ /// The new color palette
+ ///
protected abstract ColorPalette GetPalette(ColorPalette original);
}
}
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs
index 67040e58b..542aabbd4 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs
@@ -4,7 +4,7 @@
// Licensed under the Apache License, Version 2.0.
//
//
-// The color moment for holding pixel information.
+// The cumulative color moment for holding pixel information.
// Adapted from
//
// --------------------------------------------------------------------------------------------------------------------
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/IWuQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/IWuQuantizer.cs
index 1b06ba63e..90ffb15b4 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/IWuQuantizer.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/IWuQuantizer.cs
@@ -4,7 +4,8 @@
// Licensed under the Apache License, Version 2.0.
//
//
-// The WuQuantizer interface.
+// Encapsulates methods to calculate the color palette of an image using
+// a Wu color quantizer .
// Adapted from
//
// --------------------------------------------------------------------------------------------------------------------
@@ -14,10 +15,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
using System.Drawing;
///
- /// The WuQuantizer interface.
+ /// Encapsulates methods to calculate the color palette of an image using
+ /// a Wu color quantizer .
/// Adapted from
///
- public interface IWuQuantizer
+ public interface IWuQuantizer : IQuantizer
{
///
/// Quantizes the given image.
@@ -35,6 +37,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
///
/// The quantized .
///
- Image QuantizeImage(Bitmap image, int alphaThreshold, int alphaFader);
+ Bitmap Quantize(Image image, int alphaThreshold, int alphaFader);
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
index a0ee180f4..1bdcc4749 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
@@ -4,7 +4,7 @@
// Licensed under the Apache License, Version 2.0.
//
//
-// The image buffer for storing pixel information.
+// The image buffer for storing and manipulating pixel information.
// Adapted from
//
// --------------------------------------------------------------------------------------------------------------------
@@ -20,7 +20,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
using ImageProcessor.Common.Exceptions;
///
- /// The image buffer for storing pixel information.
+ /// The image buffer for storing and manipulating pixel information.
/// Adapted from
///
internal class ImageBuffer
@@ -51,16 +51,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
get
{
- int bitDepth = System.Drawing.Image.GetPixelFormatSize(this.Image.PixelFormat);
- if (bitDepth != 32)
- {
- throw new QuantizationException(
- string.Format(
- "The image you are attempting to quantize does not contain a 32 bit ARGB palette. This image has a bit depth of {0} with {1} colors.",
- bitDepth,
- this.Image.Palette.Entries.Length));
- }
-
int width = this.Image.Width;
int height = this.Image.Height;
Pixel[] pixels = new Pixel[width];
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs
index 190dd09e2..3f0cafb65 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs
@@ -4,7 +4,7 @@
// Licensed under the Apache License, Version 2.0.
//
//
-// The palette color history.
+// The palette color history containing the sum of all pixel data.
// Adapted from
//
// --------------------------------------------------------------------------------------------------------------------
@@ -14,7 +14,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
using System.Drawing;
///
- /// The palette color history.
+ /// The palette color history containing the sum of all pixel data.
/// Adapted from
///
internal struct PaletteColorHistory
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
index d808ecfa2..d69cfb240 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
@@ -44,7 +44,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
this.Palette[paletteIndex] = new LookupNode
{
- Pixel = palette[paletteIndex],
+ Pixel = palette[paletteIndex],
PaletteIndex = (byte)paletteIndex
};
}
@@ -240,7 +240,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
}
this.lookupNodes = new Dictionary(tempLookup.Count);
- foreach (var key in tempLookup.Keys)
+ foreach (int key in tempLookup.Keys)
{
this.lookupNodes[key] = tempLookup[key].ToArray();
}
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs
index 9fe10c252..be63cda10 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs
@@ -1,10 +1,38 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// 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()
{
@@ -16,6 +44,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
Debug.Assert(Argb == (alpha << 24 | red << 16 | green << 8 | blue));
}
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The argb.
+ ///
public Pixel(int argb)
: this()
{
@@ -26,17 +60,42 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
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);
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs
index 7a103d054..dd4936d7e 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs
@@ -3,6 +3,11 @@
// 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
@@ -16,67 +21,68 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// a Wu color quantizer .
/// Adapted from
///
- public class WuQuantizer : WuQuantizerBase, IWuQuantizer
+ public class WuQuantizer : WuQuantizerBase
{
///
- /// The get quantized image.
+ /// Quantizes the image contained within the returning the result.
///
- ///
- /// The image.
+ ///
+ /// The for storing and manipulating pixel information..
///
///
- /// The color count.
+ /// The maximum number of colors apply to the image.
///
///
- /// The lookups.
+ /// The array of containing indexed versions of the images colors.
///
///
- /// The alpha threshold.
+ /// All colors with an alpha value less than this will be considered fully transparent.
///
///
- /// The .
+ /// The quantized .
///
- internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold)
+ internal override Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Pixel[] lookups, int alphaThreshold)
{
- Bitmap result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed);
- result.SetResolution(image.Image.HorizontalResolution, image.Image.VerticalResolution);
+ 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(image, lookups, alphaThreshold, paletteHistogram));
+ resultBuffer.UpdatePixelIndexes(IndexedPixels(imageBuffer, lookups, alphaThreshold, paletteHistogram));
result.Palette = BuildPalette(result.Palette, paletteHistogram);
return result;
}
///
- /// The build palette.
+ /// Builds a color palette from the given .
///
///
- /// The palette.
+ /// The to fill.
///
- ///
- /// The palette histogram.
+ ///
+ /// The containing the sum of all pixel data.
///
///
/// The .
///
- private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistogram)
+ private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistory)
{
- for (int paletteColorIndex = 0; paletteColorIndex < paletteHistogram.Length; paletteColorIndex++)
+ int length = paletteHistory.Length;
+ for (int i = 0; i < length; i++)
{
- palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor();
+ palette.Entries[i] = paletteHistory[i].ToNormalizedColor();
}
return palette;
}
///
- /// The indexed pixels.
+ /// Gets an enumerable array of bytes representing each row of the image.
///
///
- /// The image.
+ /// The for storing and manipulating pixel information.
///
///
- /// The lookups.
+ /// The array of containing indexed versions of the images colors.
///
///
/// The alpha threshold.
@@ -85,7 +91,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// The palette histogram.
///
///
- /// The .
+ /// The enumerable list of representing each pixel.
///
private static IEnumerable IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram)
{
@@ -93,12 +99,13 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
PaletteLookup lookup = new PaletteLookup(lookups);
// Determine the correct fallback color.
- byte fallback = (byte)(lookups.Length < AlphaColor ? 0 : AlphaColor);
+ byte fallback = lookups.Length < AlphaMax ? AlphaMin : AlphaMax;
foreach (Pixel[] pixelLine in image.PixelLines)
{
- for (int pixelIndex = 0; pixelIndex < pixelLine.Length; pixelIndex++)
+ int length = pixelLine.Length;
+ for (int i = 0; i < length; i++)
{
- Pixel pixel = pixelLine[pixelIndex];
+ Pixel pixel = pixelLine[i];
byte bestMatch = fallback;
if (pixel.Alpha > alphaThreshold)
{
@@ -106,7 +113,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
paletteHistogram[bestMatch].AddPixel(pixel);
}
- lineIndexes[pixelIndex] = bestMatch;
+ lineIndexes[i] = bestMatch;
}
yield return lineIndexes;
diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs
index 00a7934fa..8e801bc9c 100644
--- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs
+++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs
@@ -1,32 +1,29 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// 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
+namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
using System;
+ using System.Diagnostics.CodeAnalysis;
using System.Drawing;
+ using System.Drawing.Imaging;
using System.Linq;
+ using ImageProcessor.Common.Exceptions;
+
///
/// Encapsulates methods to calculate the color palette of an image using
/// a Wu color quantizer .
/// Adapted from
///
- public abstract class WuQuantizerBase
+ public abstract class WuQuantizerBase : IWuQuantizer
{
///
- /// The alpha color component.
+ /// The maximum value for an alpha color component.
///
- protected const byte AlphaColor = 255;
+ protected const byte AlphaMax = 255;
+
+ ///
+ /// The minimum value for an alpha color component.
+ ///
+ protected const byte AlphaMin = 0;
///
/// The position of the alpha component within a byte array.
@@ -64,10 +61,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
///
/// The 32 bit per pixel image to quantize.
///
- /// A quantized version of the image.
- public Image QuantizeImage(Bitmap source)
+ ///
+ /// A quantized version of the image.
+ ///
+ public Bitmap Quantize(Image source)
{
- return this.QuantizeImage(source, 0, 1);
+ return this.Quantize(source, 0, 1);
}
///
@@ -76,12 +75,18 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
///
/// The 32 bit per pixel image to quantize.
///
- /// All colors with an alpha value less than this will be considered fully transparent.
- /// Alpha values will be normalized to the nearest multiple of this value.
- /// A quantized version of the image.
- public Image QuantizeImage(Bitmap source, int alphaThreshold, int alphaFader)
+ ///
+ /// All colors with an alpha value less than this will be considered fully transparent.
+ ///
+ ///
+ /// Alpha values will be normalized to the nearest multiple of this value.
+ ///
+ ///
+ /// A quantized version of the image.
+ ///
+ public Bitmap Quantize(Image source, int alphaThreshold, int alphaFader)
{
- return this.QuantizeImage(source, alphaThreshold, alphaFader, null, 256);
+ return this.Quantize(source, alphaThreshold, alphaFader, null, 256);
}
///
@@ -105,26 +110,73 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
///
/// A quantized version of the image.
///
- public Image QuantizeImage(Bitmap source, int alphaThreshold, int alphaFader, Histogram histogram, int maxColors)
+ public Bitmap Quantize(Image source, int alphaThreshold, int alphaFader, Histogram histogram, int maxColors)
{
- ImageBuffer buffer = new ImageBuffer(source);
-
- if (histogram == null)
+ try
{
- histogram = new Histogram();
+ 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.Clear(Color.Transparent);
+ graphics.DrawImage(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);
+ Pixel[] lookups = BuildLookups(cubes, histogram.Moments);
+ return this.GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold);
}
- else
+ catch (Exception ex)
{
- histogram.Clear();
+ throw new QuantizationException(ex.Message, ex);
}
-
- BuildHistogram(histogram, buffer, alphaThreshold, alphaFader);
- CalculateMoments(histogram.Moments);
- Box[] cubes = SplitData(ref maxColors, histogram.Moments);
- Pixel[] lookups = BuildLookups(cubes, histogram.Moments);
- return this.GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold);
}
+ ///
+ /// Quantizes the image contained within the returning the result.
+ ///
+ ///
+ /// The for storing and manipulating pixel information..
+ ///
+ ///
+ /// The maximum number of colors apply to the image.
+ ///
+ ///
+ /// The array of containing indexed versions of the images colors.
+ ///
+ ///
+ /// All colors with an alpha value less than this will be considered fully transparent.
+ ///
+ ///
+ /// The quantized .
+ ///
+ internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Pixel[] lookups, int alphaThreshold);
+
///
/// Builds a histogram from the current image.
///
@@ -140,6 +192,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
///
/// Alpha values will be normalized to the nearest multiple of this value.
///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static void BuildHistogram(Histogram histogram, ImageBuffer imageBuffer, int alphaThreshold, int alphaFader)
{
ColorMoment[, , ,] moments = histogram.Moments;
@@ -174,9 +227,16 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
moments[0, 0, 0, 0].Add(new Pixel(0, 0, 0, 0));
}
+ ///
+ /// Calculates the color moments from the histogram of moments.
+ ///
+ ///
+ /// The three dimensional array of to process.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static void CalculateMoments(ColorMoment[, , ,] moments)
{
- ColorMoment[,] xarea = new ColorMoment[SideSize, SideSize];
+ ColorMoment[,] areaSquared = new ColorMoment[SideSize, SideSize];
ColorMoment[] area = new ColorMoment[SideSize];
for (int alphaIndex = 1; alphaIndex < SideSize; alphaIndex++)
{
@@ -190,10 +250,10 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
line.AddFast(ref moments[alphaIndex, redIndex, greenIndex, blueIndex]);
area[blueIndex].AddFast(ref line);
- xarea[greenIndex, blueIndex].AddFast(ref area[blueIndex]);
+ areaSquared[greenIndex, blueIndex].AddFast(ref area[blueIndex]);
ColorMoment moment = moments[alphaIndex - 1, redIndex, greenIndex, blueIndex];
- moment.AddFast(ref xarea[greenIndex, blueIndex]);
+ moment.AddFast(ref areaSquared[greenIndex, blueIndex]);
moments[alphaIndex, redIndex, greenIndex, blueIndex] = moment;
}
}
@@ -201,6 +261,25 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
}
}
+ ///
+ /// Calculates the volume of the top of the cube.
+ ///
+ ///
+ /// The cube to calculate the volume from.
+ ///
+ ///
+ /// The direction to calculate.
+ ///
+ ///
+ /// The position at which to begin.
+ ///
+ ///
+ /// The three dimensional moment.
+ ///
+ ///
+ /// The representing the top of the cube.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static ColorMoment Top(Box cube, int direction, int position, ColorMoment[, , ,] moment)
{
switch (direction)
@@ -250,6 +329,22 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
}
}
+ ///
+ /// Calculates the volume of the bottom of the cube.
+ ///
+ ///
+ /// The cube to calculate the volume from.
+ ///
+ ///
+ /// The direction to calculate.
+ ///
+ ///
+ /// The three dimensional moment.
+ ///
+ ///
+ /// The representing the bottom of the cube.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static ColorMoment Bottom(Box cube, int direction, ColorMoment[, , ,] moment)
{
switch (direction)
@@ -299,10 +394,35 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
}
}
+ ///
+ /// Maximizes the sum of the two boxes.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// The cube.
+ ///
+ ///
+ /// The direction.
+ ///
+ ///
+ /// The first byte.
+ ///
+ ///
+ /// The last byte.
+ ///
+ ///
+ /// The whole .
+ ///
+ ///
+ /// The representing the sum.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static CubeCut Maximize(ColorMoment[, , ,] moments, Box cube, int direction, byte first, byte last, ColorMoment whole)
{
- var bottom = Bottom(cube, direction, moments);
- var result = 0.0f;
+ ColorMoment bottom = Bottom(cube, direction, moments);
+ float result = 0.0f;
byte? cutPoint = null;
for (byte position = first; position < last; ++position)
@@ -313,7 +433,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
continue;
}
- var temp = half.WeightedDistance();
+ long temp = half.WeightedDistance();
half = whole - half;
if (half.Weight != 0)
@@ -331,28 +451,55 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
return new CubeCut(cutPoint, result);
}
+ ///
+ /// Returns a value indicating whether a cube can be cut.
+ ///
+ ///
+ /// The three dimensional array of .
+ ///
+ ///
+ /// The first .
+ ///
+ ///
+ /// The second .
+ ///
+ ///
+ /// The indicating the result.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static bool Cut(ColorMoment[, , ,] moments, ref Box first, ref Box second)
{
int direction;
- var whole = Volume(first, moments);
- var maxAlpha = Maximize(moments, first, Alpha, (byte)(first.AlphaMinimum + 1), first.AlphaMaximum, whole);
- var maxRed = Maximize(moments, first, Red, (byte)(first.RedMinimum + 1), first.RedMaximum, whole);
- var maxGreen = Maximize(moments, first, Green, (byte)(first.GreenMinimum + 1), first.GreenMaximum, whole);
- var maxBlue = Maximize(moments, first, Blue, (byte)(first.BlueMinimum + 1), first.BlueMaximum, whole);
+ ColorMoment whole = Volume(moments, first);
+ CubeCut maxAlpha = Maximize(moments, first, Alpha, (byte)(first.AlphaMinimum + 1), first.AlphaMaximum, whole);
+ CubeCut maxRed = Maximize(moments, first, Red, (byte)(first.RedMinimum + 1), first.RedMaximum, whole);
+ CubeCut maxGreen = Maximize(moments, first, Green, (byte)(first.GreenMinimum + 1), first.GreenMaximum, whole);
+ CubeCut maxBlue = Maximize(moments, first, Blue, (byte)(first.BlueMinimum + 1), first.BlueMaximum, whole);
if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value))
{
direction = Alpha;
- if (maxAlpha.Position == null) return false;
+ if (maxAlpha.Position == null)
+ {
+ return false;
+ }
}
- else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) && (maxRed.Value >= maxBlue.Value))
+ else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value)
+ && (maxRed.Value >= maxBlue.Value))
+ {
direction = Red;
+ }
else
{
- if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) && (maxGreen.Value >= maxBlue.Value))
+ if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value)
+ && (maxGreen.Value >= maxBlue.Value))
+ {
direction = Green;
+ }
else
+ {
direction = Blue;
+ }
}
second.AlphaMaximum = first.AlphaMaximum;
@@ -363,6 +510,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
switch (direction)
{
case Alpha:
+ if (maxAlpha.Position == null)
+ {
+ return false;
+ }
+
second.AlphaMinimum = first.AlphaMaximum = (byte)maxAlpha.Position;
second.RedMinimum = first.RedMinimum;
second.GreenMinimum = first.GreenMinimum;
@@ -370,6 +522,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
break;
case Red:
+ if (maxRed.Position == null)
+ {
+ return false;
+ }
+
second.RedMinimum = first.RedMaximum = (byte)maxRed.Position;
second.AlphaMinimum = first.AlphaMinimum;
second.GreenMinimum = first.GreenMinimum;
@@ -377,6 +534,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
break;
case Green:
+ if (maxGreen.Position == null)
+ {
+ return false;
+ }
+
second.GreenMinimum = first.GreenMaximum = (byte)maxGreen.Position;
second.AlphaMinimum = first.AlphaMinimum;
second.RedMinimum = first.RedMinimum;
@@ -384,6 +546,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
break;
case Blue:
+ if (maxBlue.Position == null)
+ {
+ return false;
+ }
+
second.BlueMinimum = first.BlueMaximum = (byte)maxBlue.Position;
second.AlphaMinimum = first.AlphaMinimum;
second.RedMinimum = first.RedMinimum;
@@ -397,44 +564,83 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
return true;
}
+ ///
+ /// Calculates the variance of the volume of the cube.
+ ///
+ ///
+ /// The three dimensional array of .
+ ///
+ ///
+ /// The cube.
+ ///
+ ///
+ /// The representing the variance.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static float CalculateVariance(ColorMoment[, , ,] moments, Box cube)
{
- ColorMoment volume = Volume(cube, moments);
+ ColorMoment volume = Volume(moments, cube);
return volume.Variance();
}
- private static ColorMoment Volume(Box cube, ColorMoment[, , ,] moment)
+ ///
+ /// Calculates the volume of the colors.
+ ///
+ ///
+ /// The three dimensional array of .
+ ///
+ ///
+ /// The cube.
+ ///
+ ///
+ /// The representing the volume.
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
+ private static ColorMoment Volume(ColorMoment[, , ,] moments, Box cube)
{
- return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
-
- (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
+ return (moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
+ moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
+ moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
+ moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
+ moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
+ moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
+ moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
+ moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
+
+ (moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
+ moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
+ moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
+ moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
+ moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
+ moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
+ moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
+ moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
}
+ ///
+ /// Splits the data.
+ ///
+ ///
+ /// The color count.
+ ///
+ ///
+ /// The three dimensional array of .
+ ///
+ ///
+ /// The array .
+ ///
+ [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:CommasMustBeSpacedCorrectly", Justification = "Reviewed. Suppression is OK here.")]
private static Box[] SplitData(ref int colorCount, ColorMoment[, , ,] moments)
{
--colorCount;
- var next = 0;
- var volumeVariance = new float[colorCount];
- var cubes = new Box[colorCount];
+ int next = 0;
+ float[] volumeVariance = new float[colorCount];
+ Box[] cubes = new Box[colorCount];
cubes[0].AlphaMaximum = MaxSideIndex;
cubes[0].RedMaximum = MaxSideIndex;
cubes[0].GreenMaximum = MaxSideIndex;
cubes[0].BlueMaximum = MaxSideIndex;
- for (var cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex)
+ for (int cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex)
{
if (Cut(moments, ref cubes[next], ref cubes[cubeIndex]))
{
@@ -448,29 +654,51 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
}
next = 0;
- var temp = volumeVariance[0];
+ float temp = volumeVariance[0];
- for (var index = 1; index <= cubeIndex; ++index)
+ for (int index = 1; index <= cubeIndex; ++index)
{
- if (volumeVariance[index] <= temp) continue;
+ if (volumeVariance[index] <= temp)
+ {
+ continue;
+ }
+
temp = volumeVariance[index];
next = index;
}
- if (temp > 0.0) continue;
+ if (temp > 0.0)
+ {
+ continue;
+ }
+
colorCount = cubeIndex + 1;
break;
}
+
return cubes.Take(colorCount).ToArray();
}
+ ///
+ /// Builds an array of pixel data to look within.
+ ///
+ ///
+ /// The array of cubes.
+ ///
+ ///
+ /// The three dimensional 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)
{
Pixel[] lookups = new Pixel[cubes.Length];
for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++)
{
- ColorMoment volume = Volume(cubes[cubeIndex], moments);
+ ColorMoment volume = Volume(moments, cubes[cubeIndex]);
if (volume.Weight <= 0)
{
@@ -490,7 +718,5 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
return lookups;
}
-
- internal abstract Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold);
}
}
\ No newline at end of file