internal void Insert(Argb color) { if (this.Bits > 0) { this.Array[this.Index(color)] = color; } }
/// The palette (color-indexing) transform replaces pixels with indices into /// a palette that is stored before the image data. If the palette is /// sufficiently small, multiple indices are packed into a single pixel. internal static Image PaletteTransform(BitWriter b, Image image, Palette palette) { b.WriteBits(1, 1); b.WriteBits(3, 2); WritePalette(b, palette); int packSize = palette.Count <= 2 ? 8 : palette.Count <= 4 ? 4 : palette.Count <= 16 ? 2 : 1; int packedWidth = (image.Width + packSize - 1) / packSize; var palettized = new Image(packedWidth, image.Height); for (int y = 0; y < image.Height; ++y) { for (int i = 0; i < packedWidth; ++i) { int pack = 0; for (int j = 0; j < packSize; ++j) { int x = i * packSize + j; if (x >= image.Width) { break; } int colorIndex = palette.Indices[image[x, y]]; pack |= colorIndex << (j * (8 / packSize)); } palettized[i, y] = new Argb(255, 0, (byte)pack, 0); } } return(palettized); }
static Argb ClampAddSubtractHalf(Argb a, Argb b) { return(new Argb( Clamp(a.A + (a.A - b.A) / 2), Clamp(a.R + (a.R - b.R) / 2), Clamp(a.G + (a.G - b.G) / 2), Clamp(a.B + (a.B - b.B) / 2))); }
static Argb ClampAddSubtractFull(Argb l, Argb t, Argb tl) { return(new Argb( Clamp(l.A + t.A - tl.A), Clamp(l.R + t.R - tl.R), Clamp(l.G + t.G - tl.G), Clamp(l.B + t.B - tl.B))); }
static Argb Average2(Argb a, Argb b) { return(new Argb( (byte)((a.A + b.A) / 2), (byte)((a.R + b.R) / 2), (byte)((a.G + b.G) / 2), (byte)((a.B + b.B) / 2))); }
internal static uint Hash(Argb argb) { uint x1 = argb.ToUInt(); uint x2 = (x1 * 47491) ^ ~(x1 * 41227); uint x3 = x2 * (x2 << 3) ^ ~(x2 >> 5) ^ (x2 >> 13); uint x4 = (x3 * 20389) ^ ~(x3 * 28111); return(x4); }
internal bool Lookup(Argb color, out int index) { if (this.Bits <= 0) { index = 0; return(false); } index = this.Index(color); return(this.Array[index] == color); }
internal static uint Hash(Argb a1, Argb a2, Argb a3) { uint h1 = Hash(a1); uint h2 = Hash(a2); uint h3 = Hash(a3); uint x1 = h1 ^ (h2 * 32369) ^ ~(h3 * 39217); uint x2 = ~(h1 * 40483) ^ h2 ^ (h3 * 42943); uint x3 = (x1 << 3) ^ (x2 >> 7); return(x3); }
static Argb Select(Argb l, Argb t, Argb tl) { int pA = l.A + t.A - tl.A; int pR = l.R + t.R - tl.R; int pG = l.G + t.G - tl.G; int pB = l.B + t.B - tl.B; int pL = Abs(pA - l.A) + Abs(pR - l.R) + Abs(pG - l.G) + Abs(pB - l.B); int pT = Abs(pA - t.A) + Abs(pR - t.R) + Abs(pG - t.G) + Abs(pB - t.B); return(pL < pT ? l : t); }
/// The prediction transform predicts the values of pixels using their /// already decoded neighbors, storing only the difference between the /// predicted and actual value. There are 14 prediction modes and which mode /// is used is determined from an encoded subsample image. internal static Image PredictTransform(BitWriter b, Image image) { b.WriteBits(1, 1); b.WriteBits(0, 2); int tileBits = 4; int tileSize = 0; int blockedWidth = 0; int blockedHeight = 0; while (tileBits < 2 + 8) { tileSize = 1 << tileBits; blockedWidth = (image.Width + tileSize - 1) / tileSize; blockedHeight = (image.Height + tileSize - 1) / tileSize; if (blockedWidth * blockedHeight < 2000) { break; } ++tileBits; } b.WriteBits(tileBits - 2, 3); var blocks = new Image(blockedWidth, blockedHeight); var residuals = new Image(image.Width, image.Height); var accumHistos = Enumerable.Range(0, 4).Select(_ => new Histogram(256)).ToList(); for (int y = 0; y < blockedHeight; ++y) { for (int x = 0; x < blockedWidth; ++x) { int bestPrediction = 0; double bestEntropy = PredictEntropy(image, tileBits, x, y, 0, accumHistos); for (int i = 1; i < PREDICTIONS.Count; ++i) { double entropy = PredictEntropy(image, tileBits, x, y, i, accumHistos); if (entropy < bestEntropy) { bestPrediction = i; bestEntropy = entropy; } } blocks[x, y] = new Argb(255, 0, (byte)bestPrediction, 0); PredictBlock(image, residuals, tileBits, x, y, bestPrediction, accumHistos); } } ImageData.WriteImageData(b, blocks, false); return(residuals); }
/// Computes all the histograms from an image. static List <Histogram> ComputeHistograms(Image image, Palette paletteOrNull, out bool hasAlpha) { var histos = Enumerable.Range(0, (int)HistogramIdx._COUNT) .Select(_ => new Histogram(256)).ToList(); Argb previous = new Argb(255, 0, 0, 0); hasAlpha = false; for (int i = 0; i < image.Pixels.Length; ++i) { Argb pixel = image.Pixels[i]; if (pixel.A != 255) { hasAlpha = true; } Argb delta = pixel - previous; previous = pixel; histos[(int)HistogramIdx.RED].Hit(pixel.R); histos[(int)HistogramIdx.GREEN].Hit(pixel.G); histos[(int)HistogramIdx.BLUE].Hit(pixel.B); histos[(int)HistogramIdx.ALPHA].Hit(pixel.A); histos[(int)HistogramIdx.DELTA_RED].Hit(delta.R); histos[(int)HistogramIdx.DELTA_GREEN].Hit(delta.G); histos[(int)HistogramIdx.DELTA_BLUE].Hit(delta.B); histos[(int)HistogramIdx.DELTA_ALPHA].Hit(delta.A); histos[(int)HistogramIdx.RED_MINUS_GREEN].Hit((byte)(pixel.R - pixel.G)); histos[(int)HistogramIdx.BLUE_MINUS_GREEN].Hit((byte)(pixel.B - pixel.G)); histos[(int)HistogramIdx.DELTA_RED_MINUS_DELTA_GREEN] .Hit((byte)(delta.R - delta.G)); histos[(int)HistogramIdx.DELTA_BLUE_MINUS_DELTA_GREEN] .Hit((byte)(delta.B - delta.G)); if (paletteOrNull != null) { histos[(int)HistogramIdx.PALETTE].Hit(paletteOrNull.Indices[pixel]); } } return(histos); }
/// Computes the predicted value for a single image pixel given the /// prediction mode. static Argb Predict(Image image, int x, int y, int prediction) { if (x == 0 && y == 0) { return(new Argb(255, 0, 0, 0)); } else if (x == 0) { return(image[x, y - 1]); } else if (y == 0) { return(image[x - 1, y]); } int i = y * image.Width + x; Argb top = image.Pixels[i - image.Width]; Argb left = image.Pixels[i - 1]; Argb topLeft = image.Pixels[i - image.Width - 1]; Argb topRight = image.Pixels[i - image.Width + 1]; return(PREDICTIONS[prediction](top, left, topLeft, topRight)); }
private int Index(Argb color) { return((int)((uint)(color.ToUInt() * 0x1e35a7bd) >> (32 - this.Bits))); }
internal void AddChain(Argb a1, Argb a2, Argb a3, int index) { int i = (int)(Argb.Hash(a1, a2, a3) % this.Chains.Count()); this.Chains[i] = new Chain(this.Chains[i], index); }
internal Chain GetChain(Argb a1, Argb a2, Argb a3) { return(this.Chains[(int)(Argb.Hash(a1, a2, a3) % this.Chains.Count())]); }