Browse Source

Use Color32 structure.

Former-commit-id: 7766fb32652db5587004bea4da2f9abbc0af17a6
Former-commit-id: c7a35226816f4229d69cfe7571e9653932f9b6f1
pull/17/head
James South 11 years ago
parent
commit
0c5d0d7607
  1. 2
      src/ImageProcessor.Playground/Program.cs
  2. 4
      src/ImageProcessor/ImageProcessor.csproj
  3. 3
      src/ImageProcessor/Imaging/Colors/Color32.cs
  4. 26
      src/ImageProcessor/Imaging/Formats/PngFormat.cs
  5. 12
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs
  6. 7
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
  7. 12
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs
  8. 36
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
  9. 104
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs
  10. 16
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs
  11. 53
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs

2
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)

4
src/ImageProcessor/ImageProcessor.csproj

@ -182,6 +182,7 @@
<Compile Include="Imaging\Formats\GifFrame.cs" />
<Compile Include="Imaging\Helpers\Adjustments.cs" />
<Compile Include="Imaging\Helpers\Effects.cs" />
<Compile Include="Imaging\Quantizers\IQuantizer.cs" />
<Compile Include="Imaging\Quantizers\OctreeQuantizer.cs" />
<Compile Include="Imaging\Quantizers\Quantizer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\Box.cs" />
@ -192,9 +193,6 @@
<Compile Include="Imaging\Quantizers\WuQuantizer\CubeCut.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\ImageBuffer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\IWuQuantizer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\Pixel.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Common\Exceptions\QuantizationException.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\WuQuantizer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\WuQuantizerBase.cs" />

3
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);
}
/// <summary>

26
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;
/// <summary>
@ -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);

12
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs

@ -11,6 +11,8 @@
namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
using ImageProcessor.Imaging.Colors;
/// <summary>
/// The cumulative color moment for holding pixel information.
/// Adapted from <see href="https://github.com/drewnoakes" />
@ -119,12 +121,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <param name="pixel">
/// The pixel to add.
/// </param>
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;

7
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;
/// <summary>
/// The image buffer for storing and manipulating pixel information.
@ -47,13 +48,13 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <exception cref="QuantizationException">
/// Thrown if the given image is not a 32 bit per pixel image.
/// </exception>
public IEnumerable<Pixel[]> PixelLines
public IEnumerable<Color32[]> 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;

12
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteColorHistory.cs

@ -13,6 +13,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
using System.Drawing;
using ImageProcessor.Imaging.Colors;
/// <summary>
/// The palette color history containing the sum of all pixel data.
/// Adapted from <see href="https://github.com/drewnoakes" />
@ -61,12 +63,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <param name="pixel">
/// The pixel to add.
/// </param>
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++;
}
}

36
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;
/// <summary>
/// Stores the indexed color palette of an image for fast access.
/// Adapted from <see href="https://github.com/drewnoakes" />
@ -37,14 +39,14 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <param name="palette">
/// The palette.
/// </param>
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
/// <returns>
/// The <see cref="byte"/> representing the index.
/// </returns>
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
/// <returns>
/// The <see cref="int"/> representing the component value of the mask.
/// </returns>
private static int GetMask(Pixel[] palette)
private static int GetMask(Color32[] palette)
{
IEnumerable<byte> alphas = palette.Select(p => p.Alpha).ToArray();
IEnumerable<byte> alphas = palette.Select(p => p.A).ToArray();
byte maxAlpha = alphas.Max();
int uniqueAlphas = alphas.Distinct().Count();
IEnumerable<byte> reds = palette.Select(p => p.Red).ToArray();
IEnumerable<byte> reds = palette.Select(p => p.R).ToArray();
byte maxRed = reds.Max();
int uniqueReds = reds.Distinct().Count();
IEnumerable<byte> greens = palette.Select(p => p.Green).ToArray();
IEnumerable<byte> greens = palette.Select(p => p.G).ToArray();
byte maxGreen = greens.Max();
int uniqueGreens = greens.Distinct().Count();
IEnumerable<byte> blues = palette.Select(p => p.Green).ToArray();
IEnumerable<byte> 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
/// <param name="palette">
/// The palette.
/// </param>
private void BuildLookup(Pixel[] palette)
private void BuildLookup(Color32[] palette)
{
int mask = GetMask(palette);
Dictionary<int, List<LookupNode>> tempLookup = new Dictionary<int, List<LookupNode>>();
foreach (LookupNode lookup in this.Palette)
{
int pixelKey = lookup.Pixel.Argb & mask;
int pixelKey = lookup.Color32.Argb & mask;
List<LookupNode> bucket;
if (!tempLookup.TryGetValue(pixelKey, out bucket))
@ -261,7 +263,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <summary>
/// The pixel.
/// </summary>
public Pixel Pixel;
public Color32 Color32;
}
}
}

104
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs

@ -1,104 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Pixel.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
/// <summary>
/// The pixel.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct Pixel
{
/// <summary>
/// Initializes a new instance of the <see cref="Pixel"/> struct.
/// </summary>
/// <param name="alpha">
/// The alpha.
/// </param>
/// <param name="red">
/// The red.
/// </param>
/// <param name="green">
/// The green.
/// </param>
/// <param name="blue">
/// The blue.
/// </param>
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));
}
/// <summary>
/// Initializes a new instance of the <see cref="Pixel"/> struct.
/// </summary>
/// <param name="argb">
/// The argb.
/// </param>
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));
}
/// <summary>
/// The alpha.
/// </summary>
[FieldOffset(3)]
public byte Alpha;
/// <summary>
/// The red.
/// </summary>
[FieldOffset(2)]
public byte Red;
/// <summary>
/// The green.
/// </summary>
[FieldOffset(1)]
public byte Green;
/// <summary>
/// The blue.
/// </summary>
[FieldOffset(0)]
public byte Blue;
/// <summary>
/// The argb.
/// </summary>
[FieldOffset(0)]
public int Argb;
/// <summary>
/// The to string.
/// </summary>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public override string ToString()
{
return string.Format("Alpha:{0} Red:{1} Green:{2} Blue:{3}", Alpha, Red, Green, Blue);
}
}
}

16
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;
/// <summary>
/// Encapsulates methods to calculate the color palette of an image using
/// a Wu color quantizer <see href="http://www.ece.mcmaster.ca/~xwu/cq.c"/>.
@ -33,7 +35,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// The maximum number of colors apply to the image.
/// </param>
/// <param name="lookups">
/// The array of <see cref="Pixel"/> containing indexed versions of the images colors.
/// 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.
@ -41,7 +43,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <returns>
/// The quantized <see cref="Bitmap"/>.
/// </returns>
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 <see cref="ImageBuffer"/> for storing and manipulating pixel information.
/// </param>
/// <param name="lookups">
/// The array of <see cref="Pixel"/> containing indexed versions of the images colors.
/// The array of <see cref="Color32"/> containing indexed versions of the images colors.
/// </param>
/// <param name="alphaThreshold">
/// The alpha threshold.
@ -93,21 +95,21 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// <returns>
/// The enumerable list of <see cref="byte"/> representing each pixel.
/// </returns>
private static IEnumerable<byte[]> IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram)
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 (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);

53
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs

@ -1,4 +1,16 @@
namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WuQuantizerBase.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to calculate the color palette of an image using
// a Wu color quantizer <see href="http://www.ece.mcmaster.ca/~xwu/cq.c" />.
// Adapted from <see href="https://github.com/drewnoakes" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
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;
/// <summary>
/// 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.
/// </param>
/// <param name="lookups">
/// The array of <see cref="Pixel"/> containing indexed versions of the images colors.
/// 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.
@ -175,7 +188,7 @@
/// <returns>
/// The quantized <see cref="Bitmap"/>.
/// </returns>
internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Pixel[] lookups, int alphaThreshold);
internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold);
/// <summary>
/// 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));
}
/// <summary>
@ -689,12 +702,12 @@
/// The three dimensional array of <see cref="ColorMoment"/>.
/// </param>
/// <returns>
/// The array of <see cref="Pixel"/>.
/// The array of <see cref="Color32"/>.
/// </returns>
[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;

Loading…
Cancel
Save