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