mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: d202401f6555c117e25d4132d91a76d65aab0e7e Former-commit-id: e9ee3a8064f0e9db131fd0b136ab327b0d7d29ffpull/17/head
19 changed files with 388 additions and 295 deletions
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1 @@ |
|||
a442c2c7ce00c44e7de9f8221ad2c396be3431c5 |
|||
@ -1 +0,0 @@ |
|||
71ccd68b75f913237cdd2047d5f5d0b39d9b16ae |
|||
|
Before Width: | Height: | Size: 166 B |
@ -1 +0,0 @@ |
|||
98c3f8fce4fdd9eebafe12431c8e014fd39d243f |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue