private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan <uint> bgra, int cacheBits, Span <ushort> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) { bool useColorCache = cacheBits > 0; var colorCache = new ColorCache(); int i = 0; if (useColorCache) { colorCache.Init(cacheBits); } backwardRefs.Refs.Clear(); for (int ix = 0; ix < chosenPathSize; ix++) { int len = chosenPath[ix]; if (len != 1) { int offset = hashChain.FindOffset(i); backwardRefs.Add(PixOrCopy.CreateCopy((uint)offset, (ushort)len)); if (useColorCache) { for (int k = 0; k < len; k++) { colorCache.Insert(bgra[i + k]); } } i += len; } else { PixOrCopy v; int idx = useColorCache ? colorCache.Contains(bgra[i]) : -1; if (idx >= 0) { // useColorCache is true and color cache contains bgra[i] // Push pixel as a color cache index. v = PixOrCopy.CreateCacheIdx(idx); } else { if (useColorCache) { colorCache.Insert(bgra[i]); } v = PixOrCopy.CreateLiteral(bgra[i]); } backwardRefs.Add(v); i++; } } }
private static void BackwardReferencesRle(int xSize, int ySize, ReadOnlySpan <uint> bgra, int cacheBits, Vp8LBackwardRefs refs) { int pixelCount = xSize * ySize; bool useColorCache = cacheBits > 0; var colorCache = new ColorCache(); if (useColorCache) { colorCache.Init(cacheBits); } refs.Refs.Clear(); // Add first pixel as literal. AddSingleLiteral(bgra[0], useColorCache, colorCache, refs); int i = 1; while (i < pixelCount) { int maxLen = LosslessUtils.MaxFindCopyLength(pixelCount - i); int rleLen = LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen); int prevRowLen = i < xSize ? 0 : LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen); if (rleLen >= prevRowLen && rleLen >= MinLength) { refs.Add(PixOrCopy.CreateCopy(1, (ushort)rleLen)); // We don't need to update the color cache here since it is always the // same pixel being copied, and that does not change the color cache state. i += rleLen; } else if (prevRowLen >= MinLength) { refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)prevRowLen)); if (useColorCache) { for (int k = 0; k < prevRowLen; ++k) { colorCache.Insert(bgra[i + k]); } } i += prevRowLen; } else { AddSingleLiteral(bgra[i], useColorCache, colorCache, refs); i++; } } }
/// <summary> /// Update (in-place) backward references for the specified cacheBits. /// </summary> private static void BackwardRefsWithLocalCache(ReadOnlySpan <uint> bgra, int cacheBits, Vp8LBackwardRefs refs) { int pixelIndex = 0; var colorCache = new ColorCache(); colorCache.Init(cacheBits); for (int idx = 0; idx < refs.Refs.Count; idx++) { PixOrCopy v = refs.Refs[idx]; if (v.IsLiteral()) { uint bgraLiteral = v.BgraOrDistance; int ix = colorCache.Contains(bgraLiteral); if (ix >= 0) { // Color cache contains bgraLiteral v.Mode = PixOrCopyMode.CacheIdx; v.BgraOrDistance = (uint)ix; v.Len = 1; } else { colorCache.Insert(bgraLiteral); } pixelIndex++; } else { // refs was created without local cache, so it can not have cache indexes. for (int k = 0; k < v.Len; ++k) { colorCache.Insert(bgra[pixelIndex++]); } } } }
private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span <uint> pixelData, ref int lastCached) { col++; decodedPixels++; if (col >= width) { col = 0; row++; if (colorCache != null) { while (lastCached < decodedPixels) { colorCache.Insert(pixelData[lastCached]); lastCached++; } } } }
private static void AddSingleLiteralWithCostModel( ReadOnlySpan <uint> bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, Span <float> cost, Span <ushort> distArray) { double costVal = prevCost; uint color = bgra[idx]; int ix = useColorCache ? colorCache.Contains(color) : -1; if (ix >= 0) { double mul0 = 0.68; costVal += costModel.GetCacheCost((uint)ix) * mul0; } else { double mul1 = 0.82; if (useColorCache) { colorCache.Insert(color); } costVal += costModel.GetLiteralCost(color) * mul1; } if (cost[idx] > costVal) { cost[idx] = (float)costVal; distArray[idx] = 1; // only one is inserted. } }
private static void BackwardReferencesLz77(int xSize, int ySize, ReadOnlySpan <uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs) { int iLastCheck = -1; bool useColorCache = cacheBits > 0; int pixCount = xSize * ySize; var colorCache = new ColorCache(); if (useColorCache) { colorCache.Init(cacheBits); } refs.Refs.Clear(); for (int i = 0; i < pixCount;) { // Alternative #1: Code the pixels starting at 'i' using backward reference. int j; int offset = hashChain.FindOffset(i); int len = hashChain.FindLength(i); if (len >= MinLength) { int lenIni = len; int maxReach = 0; int jMax = i + lenIni >= pixCount ? pixCount - 1 : i + lenIni; // Only start from what we have not checked already. iLastCheck = i > iLastCheck ? i : iLastCheck; // We know the best match for the current pixel but we try to find the // best matches for the current pixel AND the next one combined. // The naive method would use the intervals: // [i,i+len) + [i+len, length of best match at i+len) // while we check if we can use: // [i,j) (where j<=i+len) + [j, length of best match at j) for (j = iLastCheck + 1; j <= jMax; j++) { int lenJ = hashChain.FindLength(j); int reach = j + (lenJ >= MinLength ? lenJ : 1); // 1 for single literal. if (reach > maxReach) { len = j - i; maxReach = reach; if (maxReach >= pixCount) { break; } } } } else { len = 1; } // Go with literal or backward reference. if (len == 1) { AddSingleLiteral(bgra[i], useColorCache, colorCache, refs); } else { refs.Add(PixOrCopy.CreateCopy((uint)offset, (ushort)len)); if (useColorCache) { for (j = i; j < i + len; j++) { colorCache.Insert(bgra[j]); } } } i += len; } }
public void DecodeImageData(Vp8LDecoder decoder, Span <uint> pixelData) { int lastPixel = 0; int width = decoder.Width; int height = decoder.Height; int row = lastPixel / width; int col = lastPixel % width; const int lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes; int colorCacheSize = decoder.Metadata.ColorCacheSize; ColorCache colorCache = decoder.Metadata.ColorCache; int colorCacheLimit = lenCodeLimit + colorCacheSize; int mask = decoder.Metadata.HuffmanMask; Span <HTreeGroup> hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row); int totalPixels = width * height; int decodedPixels = 0; int lastCached = decodedPixels; while (decodedPixels < totalPixels) { int code; if ((col & mask) == 0) { hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row); } if (hTreeGroup[0].IsTrivialCode) { pixelData[decodedPixels] = hTreeGroup[0].LiteralArb; this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached); continue; } this.bitReader.FillBitWindow(); if (hTreeGroup[0].UsePackedTable) { code = (int)this.ReadPackedSymbols(hTreeGroup, pixelData, decodedPixels); if (this.bitReader.IsEndOfStream()) { break; } if (code == PackedNonLiteralCode) { this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached); continue; } } else { code = (int)this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]); } if (this.bitReader.IsEndOfStream()) { break; } // Literal if (code < WebpConstants.NumLiteralCodes) { if (hTreeGroup[0].IsTrivialLiteral) { pixelData[decodedPixels] = hTreeGroup[0].LiteralArb | ((uint)code << 8); } else { uint red = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Red]); this.bitReader.FillBitWindow(); uint blue = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Blue]); uint alpha = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Alpha]); if (this.bitReader.IsEndOfStream()) { break; } pixelData[decodedPixels] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue); } this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached); } else if (code < lenCodeLimit) { // Backward reference is used. int lengthSym = code - WebpConstants.NumLiteralCodes; int length = this.GetCopyLength(lengthSym); uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]); this.bitReader.FillBitWindow(); int distCode = this.GetCopyDistance((int)distSymbol); int dist = PlaneCodeToDistance(width, distCode); if (this.bitReader.IsEndOfStream()) { break; } CopyBlock(pixelData, decodedPixels, dist, length); decodedPixels += length; col += length; while (col >= width) { col -= width; row++; } if ((col & mask) != 0) { hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row); } if (colorCache != null) { while (lastCached < decodedPixels) { colorCache.Insert(pixelData[lastCached]); lastCached++; } } } else if (code < colorCacheLimit) { // Color cache should be used. int key = code - lenCodeLimit; while (lastCached < decodedPixels) { colorCache.Insert(pixelData[lastCached]); lastCached++; } pixelData[decodedPixels] = colorCache.Lookup(key); this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached); } else { WebpThrowHelper.ThrowImageFormatException("Webp parsing error"); } } }