Browse Source

Applying optimisations

Former-commit-id: d202401f6555c117e25d4132d91a76d65aab0e7e
Former-commit-id: e9ee3a8064f0e9db131fd0b136ab327b0d7d29ff
pull/17/head
James South 11 years ago
parent
commit
4a41376fef
  1. 2
      src/ImageProcessor.Playground/Program.cs
  2. BIN
      src/ImageProcessor.Playground/images/input/000.png
  3. BIN
      src/ImageProcessor.Playground/images/input/effect_24bit.png
  4. 1
      src/ImageProcessor.Playground/images/input/h9ghTMB.png.REMOVED.git-id
  5. 1
      src/ImageProcessor.Playground/images/input/monster-whitebg.png2.REMOVED.git-id
  6. BIN
      src/ImageProcessor.Playground/images/input/pixel.png3
  7. 1
      src/ImageProcessor.Playground/images/input/tower - Copy.jpg2.REMOVED.git-id
  8. 4
      src/ImageProcessor/ImageProcessor.csproj
  9. 3
      src/ImageProcessor/Imaging/Formats/PngFormat.cs
  10. 2
      src/ImageProcessor/Imaging/Quantizers/Quantizer.cs
  11. 61
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorData.cs
  12. 31
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs
  13. 81
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
  14. 52
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteBuffer.cs
  15. 164
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
  16. 2
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/Pixel.cs
  17. 17
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/QuantizedPalette.cs
  18. 89
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs
  19. 172
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs

2
src/ImageProcessor.Playground/Program.cs

@ -49,7 +49,7 @@ namespace ImageProcessor.PlayGround
di.Create();
}
Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png"));
// Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png"));
IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".png");
//IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif");

BIN
src/ImageProcessor.Playground/images/input/000.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

BIN
src/ImageProcessor.Playground/images/input/effect_24bit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

1
src/ImageProcessor.Playground/images/input/h9ghTMB.png.REMOVED.git-id

@ -0,0 +1 @@
a442c2c7ce00c44e7de9f8221ad2c396be3431c5

1
src/ImageProcessor.Playground/images/input/monster-whitebg.png2.REMOVED.git-id

@ -1 +0,0 @@
71ccd68b75f913237cdd2047d5f5d0b39d9b16ae

BIN
src/ImageProcessor.Playground/images/input/pixel.png3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

1
src/ImageProcessor.Playground/images/input/tower - Copy.jpg2.REMOVED.git-id

@ -1 +0,0 @@
98c3f8fce4fdd9eebafe12431c8e014fd39d243f

4
src/ImageProcessor/ImageProcessor.csproj

@ -185,16 +185,18 @@
<Compile Include="Imaging\Quantizers\OctreeQuantizer.cs" />
<Compile Include="Imaging\Quantizers\Quantizer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\Box.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\PaletteLookup.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\PaletteBuffer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\ColorData.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\ColorMoment.cs" />
<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\Lookup.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\Pixel.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Imaging\Quantizers\WuQuantizer\QuantizationException.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\QuantizedPalette.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\WuQuantizer.cs" />
<Compile Include="Imaging\Quantizers\WuQuantizer\WuQuantizerBase.cs" />
<Compile Include="Imaging\ResizeLayer.cs" />

3
src/ImageProcessor/Imaging/Formats/PngFormat.cs

@ -104,6 +104,7 @@ namespace ImageProcessor.Imaging.Formats
// 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);
@ -116,6 +117,8 @@ namespace ImageProcessor.Imaging.Formats
image.Dispose();
image = new WuQuantizer().QuantizeImage(clone);
// image = new OctreeQuantizer(255, 8).Quantize(image);
//}
//else
//{

2
src/ImageProcessor/Imaging/Quantizers/Quantizer.cs

@ -59,9 +59,11 @@ namespace ImageProcessor.Imaging.Quantizers
// First off take a 32bpp copy of the image
Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb);
copy.SetResolution(source.HorizontalResolution, source.VerticalResolution);
// And construct an 8bpp version
Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
output.SetResolution(source.HorizontalResolution, source.VerticalResolution);
// Now lock the bitmap into memory
using (Graphics g = Graphics.FromImage(copy))

61
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorData.cs

@ -12,81 +12,22 @@ namespace nQuant
/// </summary>
public class ColorData
{
/// <summary>
/// The pixels.
/// </summary>
private readonly Pixel[] pixels;
/// <summary>
/// The pixels count.
/// </summary>
private readonly int pixelsCount;
/// <summary>
/// The pixel filling counter.
/// </summary>
private int pixelFillingCounter;
/// <summary>
/// Initializes a new instance of the <see cref="ColorData"/> class.
/// </summary>
/// <param name="dataGranularity">
/// The data granularity.
/// </param>
/// <param name="bitmapWidth">
/// The bitmap width.
/// </param>
/// <param name="bitmapHeight">
/// The bitmap height.
/// </param>
public ColorData(int dataGranularity, int bitmapWidth, int bitmapHeight)
public ColorData(int dataGranularity)
{
dataGranularity++;
this.Moments = new ColorMoment[dataGranularity, dataGranularity, dataGranularity, dataGranularity];
this.pixelsCount = bitmapWidth * bitmapHeight;
this.pixels = new Pixel[this.pixelsCount];
}
/// <summary>
/// Gets the moments.
/// </summary>
public ColorMoment[, , ,] Moments { get; private set; }
/// <summary>
/// Gets the pixels.
/// </summary>
public Pixel[] Pixels
{
get
{
return this.pixels;
}
}
/// <summary>
/// Gets the pixels count.
/// </summary>
public int PixelsCount
{
get
{
return this.pixelsCount;
}
}
/// <summary>
/// The add pixel.
/// </summary>
/// <param name="pixel">
/// The pixel.
/// </param>
/// <param name="quantizedPixel">
/// The quantized pixel.
/// </param>
public void AddPixel(Pixel pixel, Pixel quantizedPixel)
{
this.pixels[this.pixelFillingCounter++] = pixel;
}
}
}

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

@ -43,30 +43,39 @@ namespace nQuant
return c1;
}
public static ColorMoment operator +(ColorMoment m, Pixel p)
public void Add(Pixel p)
{
m.Alpha += p.Alpha;
m.Red += p.Red;
m.Green += p.Green;
m.Blue += p.Blue;
m.Weight++;
m.Moment += p.Distance();
return m;
Alpha += p.Alpha;
Red += p.Red;
Green += p.Green;
Blue += p.Blue;
Weight++;
Moment += p.Amplitude();
}
public long Distance()
public void AddFast(ref ColorMoment c2)
{
Alpha += c2.Alpha;
Red += c2.Red;
Green += c2.Green;
Blue += c2.Blue;
Weight += c2.Weight;
Moment += c2.Moment;
}
public long Amplitude()
{
return (Alpha * Alpha) + (Red * Red) + (Green * Green) + (Blue * Blue);
}
public long WeightedDistance()
{
return Distance() / Weight;
return this.Amplitude() / Weight;
}
public float Variance()
{
var result = Moment - ((float)Distance() / Weight);
var result = Moment - ((float)this.Amplitude() / this.Weight);
return float.IsNaN(result) ? 0.0f : result;
}
}

81
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace nQuant
{
class ImageBuffer
{
public ImageBuffer(Bitmap image)
{
this.Image = image;
}
public Bitmap Image { get; set; }
protected const int Alpha = 3;
protected const int Red = 2;
protected const int Green = 1;
protected const int Blue = 0;
public IEnumerable<Pixel> Pixels
{
get
{
var bitDepth = System.Drawing.Image.GetPixelFormatSize(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, Image.Palette.Entries.Length));
int width = this.Image.Width;
int height = this.Image.Height;
int[] buffer = new int[width];
for (int rowIndex = 0; rowIndex < height; rowIndex++)
{
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
{
Marshal.Copy(data.Scan0, buffer, 0, width);
foreach (int pixel in buffer)
{
yield return new Pixel(pixel);
}
}
finally
{
this.Image.UnlockBits(data);
}
}
}
}
public void UpdatePixelIndexes(IEnumerable<byte> indexes)
{
int width = this.Image.Width;
int height = this.Image.Height;
byte[] buffer = new byte[width];
IEnumerator<byte> indexesIterator = indexes.GetEnumerator();
for (int rowIndex = 0; rowIndex < height; rowIndex++)
{
for (int columnIndex = 0; columnIndex < buffer.Length; columnIndex++)
{
indexesIterator.MoveNext();
buffer[columnIndex] = indexesIterator.Current;
}
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
Marshal.Copy(buffer, 0, data.Scan0, width);
}
finally
{
this.Image.UnlockBits(data);
}
}
}
}
}

52
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteBuffer.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
namespace nQuant
{
class PaletteBuffer
{
public PaletteBuffer(int colorCount)
{
Alphas = new int[colorCount + 1];
Reds = new int[colorCount + 1];
Greens = new int[colorCount + 1];
Blues = new int[colorCount + 1];
Sums = new int[colorCount + 1];
}
public ColorPalette BuildPalette(ColorPalette palette)
{
var alphas = this.Alphas;
var reds = this.Reds;
var greens = this.Greens;
var blues = this.Blues;
var sums = this.Sums;
for (var paletteIndex = 0; paletteIndex < Sums.Length; paletteIndex++)
{
if (sums[paletteIndex] > 0)
{
alphas[paletteIndex] /= sums[paletteIndex];
reds[paletteIndex] /= sums[paletteIndex];
greens[paletteIndex] /= sums[paletteIndex];
blues[paletteIndex] /= sums[paletteIndex];
}
var color = Color.FromArgb(alphas[paletteIndex], reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]);
palette.Entries[paletteIndex] = color;
}
return palette;
}
public int[] Alphas { get; set; }
public int[] Reds { get; set; }
public int[] Greens { get; set; }
public int[] Blues { get; set; }
public int[] Sums { get; set; }
}
}

164
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace nQuant
{
class PaletteLookup
{
private int mMask;
private Dictionary<int, List<LookupNode>> mLookup = new Dictionary<int, List<LookupNode>>(255);
private List<LookupNode> Palette { get; set; }
public PaletteLookup(List<Pixel> palette)
{
Palette = new List<LookupNode>(palette.Count);
for (int paletteIndex = 0; paletteIndex < palette.Count; paletteIndex++)
{
Palette.Add(new LookupNode { Pixel = palette[paletteIndex], PaletteIndex = (byte)paletteIndex });
}
BuildLookup(palette);
}
public byte GetPaletteIndex(Pixel pixel)
{
int pixelKey = pixel.Argb & mMask;
List<LookupNode> bucket;
if (!mLookup.TryGetValue(pixelKey, out bucket))
{
bucket = Palette;
}
if (bucket.Count == 1)
{
return bucket[0].PaletteIndex;
}
int bestDistance = int.MaxValue;
byte bestMatch = 0;
for (int lookupIndex = 0; lookupIndex < bucket.Count; lookupIndex++)
{
var lookup = bucket[lookupIndex];
var lookupPixel = lookup.Pixel;
var deltaAlpha = pixel.Alpha - lookupPixel.Alpha;
int distance = deltaAlpha * deltaAlpha;
var deltaRed = pixel.Red - lookupPixel.Red;
distance += deltaRed * deltaRed;
var deltaGreen = pixel.Green - lookupPixel.Green;
distance += deltaGreen * deltaGreen;
var deltaBlue = pixel.Blue - lookupPixel.Blue;
distance += deltaBlue * deltaBlue;
if (distance >= bestDistance)
continue;
bestDistance = distance;
bestMatch = lookup.PaletteIndex;
}
return bestMatch;
}
private void BuildLookup(List<Pixel> palette)
{
int mask = GetMask(palette);
foreach (LookupNode lookup in Palette)
{
int pixelKey = lookup.Pixel.Argb & mask;
List<LookupNode> bucket;
if (!mLookup.TryGetValue(pixelKey, out bucket))
{
bucket = new List<LookupNode>();
mLookup[pixelKey] = bucket;
}
bucket.Add(lookup);
}
mMask = mask;
}
private static int GetMask(List<Pixel> palette)
{
IEnumerable<byte> alphas = from pixel in palette
select pixel.Alpha;
byte maxAlpha = alphas.Max();
int uniqueAlphas = alphas.Distinct().Count();
IEnumerable<byte> reds = from pixel in palette
select pixel.Red;
byte maxRed = reds.Max();
int uniqueReds = reds.Distinct().Count();
IEnumerable<byte> greens = from pixel in palette
select pixel.Green;
byte maxGreen = greens.Max();
int uniqueGreens = greens.Distinct().Count();
IEnumerable<byte> blues = from pixel in palette
select pixel.Blue;
byte maxBlue = blues.Max();
int uniqueBlues = blues.Distinct().Count();
double totalUniques = uniqueAlphas + uniqueReds + uniqueGreens + uniqueBlues;
const double AvailableBits = 8f;
byte alphaMask = ComputeBitMask(maxAlpha, Convert.ToInt32(Math.Round(uniqueAlphas / totalUniques * AvailableBits)));
byte redMask = ComputeBitMask(maxRed, Convert.ToInt32(Math.Round(uniqueReds / totalUniques * AvailableBits)));
byte greenMask = ComputeBitMask(maxGreen, Convert.ToInt32(Math.Round(uniqueGreens / totalUniques * AvailableBits)));
byte blueMask = ComputeBitMask(maxAlpha, Convert.ToInt32(Math.Round(uniqueBlues / totalUniques * AvailableBits)));
Pixel maskedPixel = new Pixel(alphaMask, redMask, greenMask, blueMask);
return maskedPixel.Argb;
}
private static byte ComputeBitMask(byte max, int bits)
{
byte mask = 0;
if (bits != 0)
{
byte highestSetBitIndex = HighestSetBitIndex(max);
for (int i = 0; i < bits; i++)
{
mask <<= 1;
mask++;
}
for (int i = 0; i <= highestSetBitIndex - bits; i++)
{
mask <<= 1;
}
}
return mask;
}
private static byte HighestSetBitIndex(byte value)
{
byte index = 0;
for (int i = 0; i < 8; i++)
{
if (0 != (value & 1))
{
index = (byte)i;
}
value >>= 1;
}
return index;
}
private struct LookupNode
{
public Pixel Pixel;
public byte PaletteIndex;
}
}
}

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

@ -26,7 +26,7 @@ namespace nQuant
this.Argb = argb;
}
public long Distance()
public long Amplitude()
{
return (Alpha * Alpha) + (Red * Red) + (Green * Green) + (Blue * Blue);
}

17
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/QuantizedPalette.cs

@ -1,17 +0,0 @@
using System.Collections.Generic;
using System.Drawing;
namespace nQuant
{
public class QuantizedPalette
{
public QuantizedPalette(int size)
{
Colors = new List<Color>();
PixelIndex = new byte[size];
}
public IList<Color> Colors { get; private set; }
public byte[] PixelIndex { get; private set; }
}
}

89
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs

@ -4,88 +4,49 @@ using System.Drawing;
namespace nQuant
{
using System.Drawing.Imaging;
public class WuQuantizer : WuQuantizerBase, IWuQuantizer
{
protected override QuantizedPalette GetQuantizedPalette(int colorCount, ColorData data, Box[] cubes, int alphaThreshold)
{
int pixelsCount = data.Pixels.Length;
Lookup[] lookups = BuildLookups(cubes, data);
var alphas = new int[colorCount + 1];
var reds = new int[colorCount + 1];
var greens = new int[colorCount + 1];
var blues = new int[colorCount + 1];
var sums = new int[colorCount + 1];
var palette = new QuantizedPalette(pixelsCount);
IList<Pixel> pixels = data.Pixels;
private IEnumerable<byte> indexedPixels(ImageBuffer image, List<Pixel> lookups, int alphaThreshold, PaletteBuffer paletteBuffer)
{
var alphas = paletteBuffer.Alphas;
var reds = paletteBuffer.Reds;
var greens = paletteBuffer.Greens;
var blues = paletteBuffer.Blues;
var sums = paletteBuffer.Sums;
Dictionary<int, byte> cachedMaches = new Dictionary<int, byte>();
PaletteLookup lookup = new PaletteLookup(lookups);
for (int pixelIndex = 0; pixelIndex < pixelsCount; pixelIndex++)
foreach (Pixel pixel in image.Pixels)
{
Pixel pixel = pixels[pixelIndex];
byte bestMatch = 255;
if (pixel.Alpha > alphaThreshold)
if (pixel.Alpha >= alphaThreshold)
{
byte bestMatch;
int argb = pixel.Argb;
if (!cachedMaches.TryGetValue(argb, out bestMatch))
{
int bestDistance = int.MaxValue;
for (int lookupIndex = 0; lookupIndex < lookups.Length; lookupIndex++)
{
Lookup lookup = lookups[lookupIndex];
var deltaAlpha = pixel.Alpha - lookup.Alpha;
var deltaRed = pixel.Red - lookup.Red;
var deltaGreen = pixel.Green - lookup.Green;
var deltaBlue = pixel.Blue - lookup.Blue;
int distance = deltaAlpha * deltaAlpha + deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
if (distance >= bestDistance)
continue;
bestDistance = distance;
bestMatch = (byte)lookupIndex;
}
cachedMaches[argb] = bestMatch;
}
bestMatch = lookup.GetPaletteIndex(pixel);
alphas[bestMatch] += pixel.Alpha;
reds[bestMatch] += pixel.Red;
greens[bestMatch] += pixel.Green;
blues[bestMatch] += pixel.Blue;
sums[bestMatch]++;
palette.PixelIndex[pixelIndex] = bestMatch;
}
else
{
palette.PixelIndex[pixelIndex] = AlphaColor;
}
}
for (var paletteIndex = 0; paletteIndex < colorCount; paletteIndex++)
{
if (sums[paletteIndex] > 0)
{
alphas[paletteIndex] /= sums[paletteIndex];
reds[paletteIndex] /= sums[paletteIndex];
greens[paletteIndex] /= sums[paletteIndex];
blues[paletteIndex] /= sums[paletteIndex];
}
var color = Color.FromArgb(alphas[paletteIndex], reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]);
palette.Colors.Add(color);
yield return bestMatch;
}
}
palette.Colors.Add(Color.FromArgb(0, 0, 0, 0));
return palette;
internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, List<Pixel> lookups, int alphaThreshold)
{
var result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed);
result.SetResolution(image.Image.HorizontalResolution, image.Image.VerticalResolution);
var resultBuffer = new ImageBuffer(result);
PaletteBuffer paletteBuffer = new PaletteBuffer(colorCount);
resultBuffer.UpdatePixelIndexes(indexedPixels(image, lookups, alphaThreshold, paletteBuffer));
result.Palette = paletteBuffer.BuildPalette(result.Palette);
return result;
}
}
}

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

@ -20,151 +20,52 @@ namespace nQuant
public Image QuantizeImage(Bitmap image)
{
return QuantizeImage(image, 10, 70);
return QuantizeImage(image, 0, 1);
}
public Image QuantizeImage(Bitmap image, int alphaThreshold, int alphaFader)
{
var colorCount = MaxColor;
var data = BuildHistogram(image, alphaThreshold, alphaFader);
ImageBuffer buffer = new ImageBuffer(image);
var data = BuildHistogram(buffer, alphaThreshold, alphaFader);
data = CalculateMoments(data);
var cubes = SplitData(ref colorCount, data);
var palette = GetQuantizedPalette(colorCount, data, cubes, alphaThreshold);
return ProcessImagePixels(image, palette);
var lookups = BuildLookups(cubes, data);
return GetQuantizedImage(buffer, colorCount, lookups, alphaThreshold);
}
private static Bitmap ProcessImagePixels(Image sourceImage, QuantizedPalette palette)
private static ColorData BuildHistogram(ImageBuffer sourceImage, int alphaThreshold, int alphaFader)
{
var result = new Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format8bppIndexed);
var newPalette = result.Palette;
for (var index = 0; index < palette.Colors.Count; index++)
newPalette.Entries[index] = palette.Colors[index];
result.Palette = newPalette;
BitmapData targetData = null;
try
ColorData colorData = new ColorData(MaxSideIndex);
foreach (Pixel pixel in sourceImage.Pixels)
{
var resultHeight = result.Height;
var resultWidth = result.Width;
targetData = result.LockBits(Rectangle.FromLTRB(0, 0, resultWidth, resultHeight), ImageLockMode.WriteOnly, result.PixelFormat);
const byte targetBitDepth = 8;
var targetByteLength = targetData.Stride < 0 ? -targetData.Stride : targetData.Stride;
var targetByteCount = Math.Max(1, targetBitDepth >> 3);
var targetBuffer = new byte[targetByteLength];
var targetValue = new byte[targetByteCount];
var pixelIndex = 0;
for (var y = 0; y < resultHeight; y++)
if (pixel.Alpha >= alphaThreshold)
{
var targetIndex = 0;
for (var x = 0; x < resultWidth; x++)
Pixel indexedPixel = pixel;
if (indexedPixel.Alpha < 255)
{
var targetIndexOffset = targetIndex >> 3;
targetValue[0] =
(byte)
(palette.PixelIndex[pixelIndex] == AlphaColor
? palette.Colors.Count - 1
: palette.PixelIndex[pixelIndex]);
pixelIndex++;
for (var valueIndex = 0; valueIndex < targetByteCount; valueIndex++)
{
targetBuffer[valueIndex + targetIndexOffset] = targetValue[valueIndex];
}
targetIndex += targetBitDepth;
int alpha = pixel.Alpha + (pixel.Alpha % alphaFader);
indexedPixel.Alpha = (byte)(alpha > 255 ? 255 : alpha);
}
Marshal.Copy(targetBuffer, 0, targetData.Scan0 + (targetByteLength * y), targetByteLength);
}
}
finally
{
if (targetData != null)
{
result.UnlockBits(targetData);
indexedPixel.Alpha = (byte)((indexedPixel.Alpha >> 3) + 1);
indexedPixel.Red = (byte)((indexedPixel.Red >> 3) + 1);
indexedPixel.Green = (byte)((indexedPixel.Green >> 3) + 1);
indexedPixel.Blue = (byte)((indexedPixel.Blue >> 3) + 1);
colorData.Moments[indexedPixel.Alpha, indexedPixel.Red, indexedPixel.Green, indexedPixel.Blue].Add(pixel);
}
}
return result;
}
private static ColorData BuildHistogram(Bitmap sourceImage, int alphaThreshold, int alphaFader)
{
int bitmapWidth = sourceImage.Width;
int bitmapHeight = sourceImage.Height;
BitmapData data = sourceImage.LockBits(
Rectangle.FromLTRB(0, 0, bitmapWidth, bitmapHeight),
ImageLockMode.ReadOnly,
sourceImage.PixelFormat);
ColorData colorData = new ColorData(MaxSideIndex, bitmapWidth, bitmapHeight);
try
{
var bitDepth = Image.GetPixelFormatSize(sourceImage.PixelFormat);
if (bitDepth != 32)
throw new QuantizationException(string.Format("Thie 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, sourceImage.Palette.Entries.Length));
var byteLength = data.Stride < 0 ? -data.Stride : data.Stride;
var byteCount = Math.Max(1, bitDepth >> 3);
var buffer = new Byte[byteLength];
var value = new Byte[byteCount];
for (int y = 0; y < bitmapHeight; y++)
{
Marshal.Copy(data.Scan0 + (byteLength * y), buffer, 0, buffer.Length);
var index = 0;
for (int x = 0; x < bitmapWidth; x++)
{
var indexOffset = index >> 3;
for (var valueIndex = 0; valueIndex < byteCount; valueIndex++)
{
value[valueIndex] = buffer[valueIndex + indexOffset];
}
Pixel pixelValue = new Pixel(value[Alpha], value[Red], value[Green], value[Blue]);
var indexAlpha = (byte)((value[Alpha] >> 3) + 1);
var indexRed = (byte)((value[Red] >> 3) + 1);
var indexGreen = (byte)((value[Green] >> 3) + 1);
var indexBlue = (byte)((value[Blue] >> 3) + 1);
if (value[Alpha] > alphaThreshold)
{
if (value[Alpha] < 255)
{
var alpha = value[Alpha] + (value[Alpha] % alphaFader);
value[Alpha] = (byte)(alpha > 255 ? 255 : alpha);
indexAlpha = (byte)((value[Alpha] >> 3) + 1);
}
colorData.Moments[indexAlpha, indexRed, indexGreen, indexBlue] += pixelValue;
}
colorData.AddPixel(
pixelValue,
new Pixel(indexAlpha, indexRed, indexGreen, indexBlue));
index += bitDepth;
}
}
}
finally
{
sourceImage.UnlockBits(data);
}
return colorData;
}
private static ColorData CalculateMoments(ColorData data)
{
var xarea = new ColorMoment[SideSize, SideSize];
var xPreviousArea = new ColorMoment[SideSize, SideSize];
var area = new ColorMoment[SideSize];
var moments = data.Moments;
for (var alphaIndex = 1; alphaIndex <= MaxSideIndex; ++alphaIndex)
{
for (var redIndex = 1; redIndex <= MaxSideIndex; ++redIndex)
@ -175,17 +76,12 @@ namespace nQuant
ColorMoment line = new ColorMoment();
for (var blueIndex = 1; blueIndex <= MaxSideIndex; ++blueIndex)
{
line += data.Moments[alphaIndex, redIndex, greenIndex, blueIndex];
area[blueIndex] += line;
xarea[greenIndex, blueIndex] = xPreviousArea[greenIndex, blueIndex] + area[blueIndex];
data.Moments[alphaIndex, redIndex, greenIndex, blueIndex] = data.Moments[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea[greenIndex, blueIndex];
line.AddFast(ref moments[alphaIndex, redIndex, greenIndex, blueIndex]);
area[blueIndex].AddFast(ref line);
xarea[greenIndex, blueIndex].AddFast(ref area[blueIndex]);
moments[alphaIndex, redIndex, greenIndex, blueIndex] = moments[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea[greenIndex, blueIndex];
}
}
var temp = xarea;
xarea = xPreviousArea;
xPreviousArea = temp;
}
}
return data;
@ -474,9 +370,9 @@ namespace nQuant
return cubes.Take(colorCount).ToArray();
}
protected Lookup[] BuildLookups(Box[] cubes, ColorData data)
private List<Pixel> BuildLookups(Box[] cubes, ColorData data)
{
List<Lookup> lookups = new List<Lookup>(cubes.Length);
List<Pixel> lookups = new List<Pixel>(cubes.Length);
foreach (var cube in cubes)
{
@ -487,20 +383,20 @@ namespace nQuant
continue;
}
var lookup = new Lookup
var lookup = new Pixel
{
Alpha = (int)(volume.Alpha / volume.Weight),
Red = (int)(volume.Red / volume.Weight),
Green = (int)(volume.Green / volume.Weight),
Blue = (int)(volume.Blue / volume.Weight)
Alpha = (byte)(volume.Alpha / volume.Weight),
Red = (byte)(volume.Red / volume.Weight),
Green = (byte)(volume.Green / volume.Weight),
Blue = (byte)(volume.Blue / volume.Weight)
};
lookups.Add(lookup);
}
return lookups.ToArray();
return lookups;
}
protected abstract QuantizedPalette GetQuantizedPalette(int colorCount, ColorData data, Box[] cubes, int alphaThreshold);
internal abstract Image GetQuantizedImage(ImageBuffer image, int colorCount, List<Pixel> lookups, int alphaThreshold);
}
}
Loading…
Cancel
Save