diff --git a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs index 942579487a..8af42a8b49 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs @@ -25,11 +25,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private const int WindowOffsetsSizeMax = 32; - /// - /// Minimum block size for backward references. - /// - private const int MinBlockSize = 256; - /// /// The number of bits for the window size. /// @@ -135,7 +130,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless uint bestBgra; int minPos = (basePosition > windowSize) ? basePosition - windowSize : 0; int lengthMax = (maxLen < 256) ? maxLen : 256; - uint maxBasePosition; pos = (int)chain[basePosition]; int currLength; @@ -193,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // We have the best match but in case the two intervals continue matching // to the left, we have the best matches for the left-extended pixels. - maxBasePosition = (uint)basePosition; + var maxBasePosition = (uint)basePosition; while (true) { p.OffsetLength[basePosition] = (bestDistance << MaxLengthBits) | (uint)bestLength; @@ -432,15 +426,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private static void BackwardReferencesTraceBackwards(int xSize, int ySize, Span bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst) { int distArraySize = xSize * ySize; - var distArray = new short[distArraySize]; + var distArray = new ushort[distArraySize]; BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray); int chosenPathSize = TraceBackwards(distArray, distArraySize); - Span chosenPath = distArray.AsSpan(distArraySize - chosenPathSize); + Span chosenPath = distArray.AsSpan(distArraySize - chosenPathSize); BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, chosenPathSize, hashChain, refsDst); } - private static void BackwardReferencesHashChainDistanceOnly(int xSize, int ySize, Span bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, short[] distArray) + private static void BackwardReferencesHashChainDistanceOnly(int xSize, int ySize, Span bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, ushort[] distArray) { int pixCount = xSize * ySize; bool useColorCache = cacheBits > 0; @@ -502,17 +496,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (i + len - 1 > reach) { - int offsetJ = 0; int lenJ = 0; int j; for (j = i; j <= reach; ++j) { - offset = hashChain.FindOffset(j + 1); - len = hashChain.FindLength(j + 1); + int offsetJ = hashChain.FindOffset(j + 1); + lenJ = hashChain.FindLength(j + 1); if (offsetJ != offset) { - offset = hashChain.FindOffset(j); - len = hashChain.FindLength(j); + lenJ = hashChain.FindLength(j); break; } } @@ -533,14 +525,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - private static int TraceBackwards(short[] distArray, int distArraySize) + private static int TraceBackwards(ushort[] distArray, int distArraySize) { int chosenPathSize = 0; int pathPos = distArraySize; int curPos = distArraySize - 1; while (curPos >= 0) { - short cur = distArray[curPos]; + ushort cur = distArray[curPos]; pathPos--; chosenPathSize++; distArray[pathPos] = cur; @@ -550,7 +542,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless return chosenPathSize; } - private static void BackwardReferencesHashChainFollowChosenPath(Span bgra, int cacheBits, Span chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) + private static void BackwardReferencesHashChainFollowChosenPath(Span bgra, int cacheBits, Span chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) { bool useColorCache = cacheBits > 0; var colorCache = new ColorCache(); @@ -606,7 +598,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - private static void AddSingleLiteralWithCostModel(Span bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, float[] cost, short[] distArray) + private static void AddSingleLiteralWithCostModel(Span bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, float[] cost, ushort[] distArray) { double costVal = prevCost; uint color = bgra[idx]; @@ -965,7 +957,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (ix >= 0) { // Color cache contains bgraLiteral - v = PixOrCopy.CreateCacheIdx(ix); + PixOrCopy.CreateCacheIdx(ix); } else { @@ -983,8 +975,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } } - - // TODO: VP8LColorCacheClear(colorCache)? } private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs) diff --git a/src/ImageSharp/Formats/WebP/Lossless/CostManager.cs b/src/ImageSharp/Formats/WebP/Lossless/CostManager.cs index d912e2e28e..bba82d10e7 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/CostManager.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/CostManager.cs @@ -14,11 +14,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless { private CostInterval head; - public CostManager(short[] distArray, int pixCount, CostModel costModel) + public CostManager(ushort[] distArray, int pixCount, CostModel costModel) { int costCacheSize = (pixCount > BackwardReferenceEncoder.MaxLength) ? BackwardReferenceEncoder.MaxLength : pixCount; - this.Intervals = new List(); this.CacheIntervals = new List(); this.CostCache = new List(); this.Costs = new float[pixCount]; @@ -85,9 +84,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless public float[] Costs { get; } - public short[] DistArray { get; } - - public List Intervals { get; } + public ushort[] DistArray { get; } public List CacheIntervals { get; } @@ -99,7 +96,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless public void UpdateCostAtIndex(int i, bool doCleanIntervals) { CostInterval current = this.head; - using List.Enumerator intervalEnumerator = this.Intervals.GetEnumerator(); while (current != null && current.Start <= i) { CostInterval next = current.Next; @@ -142,7 +138,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (this.Costs[j] > costTmp) { this.Costs[j] = costTmp; - this.DistArray[j] = (short)(k + 1); + this.DistArray[j] = (ushort)(k + 1); } } @@ -150,14 +146,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } CostInterval interval = this.head; - for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; i++) + for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; ++i) { // Define the intersection of the ith interval with the new one. int start = position + this.CacheIntervals[i].Start; int end = position + (this.CacheIntervals[i].End > len ? len : this.CacheIntervals[i].End); float cost = (float)(distanceCost + this.CacheIntervals[i].Cost); - var idx = i; CostInterval intervalNext; for (; interval != null && interval.Start < end; interval = intervalNext) { @@ -203,7 +198,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // We have to split the old interval as it fully contains the new one. int endOriginal = interval.End; interval.End = start; - this.InsertInterval(interval, interval.Cost, idx, end, endOriginal); + this.InsertInterval(interval, interval.Cost, interval.Index, end, endOriginal); break; } else @@ -317,7 +312,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (this.Costs[i] > cost) { this.Costs[i] = cost; - this.DistArray[i] = (short)(k + 1); + this.DistArray[i] = (ushort)(k + 1); } } } diff --git a/src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs b/src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs index b75df6505a..e045579597 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs @@ -13,6 +13,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// /// Number of combine failures per binId. /// - public short NumCombineFailures; + public ushort NumCombineFailures; } } diff --git a/src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs index a1ca4108ab..1497ca2447 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs @@ -26,17 +26,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private const uint NonTrivialSym = 0xffffffff; - private const short InvalidHistogramSymbol = short.MaxValue; + private const ushort InvalidHistogramSymbol = ushort.MaxValue; - public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List imageHisto, Vp8LHistogram tmpHisto, short[] histogramSymbols) + public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List imageHisto, Vp8LHistogram tmpHisto, ushort[] histogramSymbols) { int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(xSize, histoBits) : 1; int histoYSize = histoBits > 0 ? LosslessUtils.SubSampleSize(ySize, histoBits) : 1; int imageHistoRawSize = histoXSize * histoYSize; int entropyCombineNumBins = BinSize; - var mapTmp = new short[imageHistoRawSize]; - var clusterMappings = new short[imageHistoRawSize]; - int numUsed = imageHistoRawSize; + var mapTmp = new ushort[imageHistoRawSize]; + var clusterMappings = new ushort[imageHistoRawSize]; var origHisto = new List(imageHistoRawSize); for (int i = 0; i < imageHistoRawSize; i++) { @@ -47,13 +46,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless HistogramBuild(xSize, histoBits, refs, origHisto); // Copies the histograms and computes its bitCost. histogramSymbols is optimized. - HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols); - numUsed = imageHisto.Count(h => h != null); + int numUsed = HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols); var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100); if (entropyCombine) { - short[] binMap = mapTmp; + ushort[] binMap = mapTmp; var numClusters = numUsed; double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality); HistogramAnalyzeEntropyBin(imageHisto, binMap); @@ -128,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// Partition histograms to different entropy bins for three dominant (literal, /// red and blue) symbol costs and compute the histogram aggregate bitCost. /// - private static void HistogramAnalyzeEntropyBin(List histograms, short[] binMap) + private static void HistogramAnalyzeEntropyBin(List histograms, ushort[] binMap) { int histoSize = histograms.Count; var costRange = new DominantCostRange(); @@ -153,13 +151,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless continue; } - binMap[i] = (short)costRange.GetHistoBinIndex(histograms[i], NumPartitions); + binMap[i] = (ushort)costRange.GetHistoBinIndex(histograms[i], NumPartitions); } } - private static void HistogramCopyAndAnalyze(List origHistograms, List histograms, short[] histogramSymbols) + private static int HistogramCopyAndAnalyze(List origHistograms, List histograms, ushort[] histogramSymbols) { - for (int clusterId = 0, i = 0; i < origHistograms.Count; i++) + for (int clusterId = 0, i = 0; i < origHistograms.Count; ++i) { Vp8LHistogram origHistogram = origHistograms[i]; origHistogram.UpdateHistogramCost(); @@ -174,12 +172,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless else { histograms[i] = (Vp8LHistogram)origHistogram.DeepClone(); - histogramSymbols[i] = (short)clusterId++; + histogramSymbols[i] = (ushort)clusterId++; } } + + int numUsed = histogramSymbols.Count(h => h != InvalidHistogramSymbol); + return numUsed; } - private static void HistogramCombineEntropyBin(List histograms, short[] clusters, short[] clusterMappings, Vp8LHistogram curCombo, short[] binMap, int numBins, double combineCostFactor) + private static void HistogramCombineEntropyBin(List histograms, ushort[] clusters, ushort[] clusterMappings, Vp8LHistogram curCombo, ushort[] binMap, int numBins, double combineCostFactor) { var binInfo = new HistogramBinInfo[BinSize]; for (int idx = 0; idx < numBins; idx++) @@ -191,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // By default, a cluster matches itself. for (int idx = 0; idx < histograms.Count; idx++) { - clusterMappings[idx] = (short)idx; + clusterMappings[idx] = (ushort)idx; } var indicesToRemove = new List(); @@ -253,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// Given a Histogram set, the mapping of clusters 'clusterMapping' and the /// current assignment of the cells in 'symbols', merge the clusters and assign the smallest possible clusters values. /// - private static void OptimizeHistogramSymbols(short[] clusterMappings, int numClusters, short[] clusterMappingsTmp, short[] symbols) + private static void OptimizeHistogramSymbols(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, ushort[] symbols) { bool doContinue = true; @@ -274,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (k != clusterMappings[i]) { doContinue = true; - clusterMappings[i] = (short)k; + clusterMappings[i] = (ushort)k; } } } @@ -295,7 +296,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (cluster > 0 && clusterMappingsTmp[cluster] == 0) { clusterMax++; - clusterMappingsTmp[cluster] = (short)clusterMax; + clusterMappingsTmp[cluster] = (ushort)clusterMax; } symbols[i] = clusterMappingsTmp[cluster]; @@ -522,7 +523,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - private static void HistogramRemap(List input, List output, short[] symbols) + private static void HistogramRemap(List input, List output, ushort[] symbols) { int inSize = input.Count; int outSize = output.Count; @@ -549,7 +550,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - symbols[i] = (short)bestOut; + symbols[i] = (ushort)bestOut; } } else diff --git a/src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs b/src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs index 414c607ad1..2e0805c28e 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless public static void OptimizeHuffmanForRle(int length, bool[] goodForRle, uint[] counts) { // 1) Let's make the Huffman code more compatible with rle encoding. - for (; length >= 0; length--) + for (; length >= 0; --length) { if (length == 0) { @@ -63,13 +63,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Mark any seq of non-0's that is longer as 7 as a goodForRle. uint symbol = counts[0]; int stride = 0; - for (int i = 0; i < length + 1; i++) + for (int i = 0; i < length + 1; ++i) { if (i == length || counts[i] != symbol) { if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7)) { - for (int k = 0; k < stride; k++) + for (int k = 0; k < stride; ++k) { goodForRle[i - k - 1] = true; } @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } else { - stride++; + ++stride; } } @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless stride = 0; uint limit = counts[0]; uint sum = 0; - for (int i = 0; i < length; i++) + for (int i = 0; i < length + 1; ++i) { var valuesShouldBeCollapsedToStrideAverage = ValuesShouldBeCollapsedToStrideAverage((int)counts[i], (int)limit); if (i == length || goodForRle[i] || (i != 0 && goodForRle[i - 1]) || !valuesShouldBeCollapsedToStrideAverage) @@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless count = 0; } - for (k = 0; k < stride; k++) + for (k = 0; k < stride; ++k) { // We don't want to change value at counts[i], // that is already belonging to the next stride. Thus - 1. @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - stride++; + ++stride; if (i != length) { sum += counts[i]; @@ -165,11 +165,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless uint countMin; int treeSizeOrig = 0; - for (int i = 0; i < histogramSize; i++) + for (int i = 0; i < histogramSize; ++i) { if (histogram[i] != 0) { - treeSizeOrig++; + ++treeSizeOrig; } } diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs index 8b1a44fe57..e5b53c6f85 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs @@ -401,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private void EncodeImage(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, int quality, bool useCache, CrunchConfig config, int cacheBits, int histogramBits, int initBytePosition) { int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits); - var histogramSymbols = new short[histogramImageXySize]; + var histogramSymbols = new ushort[histogramImageXySize]; var huffTree = new HuffmanTree[3 * WebPConstants.CodeLengthCodes]; for (int i = 0; i < huffTree.Length; i++) { @@ -412,6 +412,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless { if (cacheBits == 0) { + // TODO: not sure if this should be 10 or 11. Original code comment says "The maximum allowed limit is 11.", but the value itself is 10. cacheBits = WebPConstants.MaxColorCacheBits; } } @@ -485,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless using IMemoryOwner histogramBgraBuffer = this.memoryAllocator.Allocate(histogramImageXySize); Span histogramBgra = histogramBgraBuffer.GetSpan(); int maxIndex = 0; - for (int i = 0; i < histogramImageXySize; i++) + for (int i = 0; i < histogramImageXySize; ++i) { int symbolIndex = histogramSymbols[i] & 0xffff; histogramBgra[i] = (uint)(symbolIndex << 8); @@ -502,7 +503,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Store Huffman codes. // Find maximum number of symbols for the huffman tree-set. int maxTokens = 0; - for (int i = 0; i < 5 * histogramImage.Count; i++) + for (int i = 0; i < 5 * histogramImage.Count; ++i) { HuffmanTreeCode codes = huffmanCodes[i]; if (maxTokens < codes.NumSymbols) @@ -517,7 +518,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless tokens[i] = new HuffmanTreeToken(); } - for (int i = 0; i < 5 * histogramImage.Count; i++) + for (int i = 0; i < 5 * histogramImage.Count; ++i) { HuffmanTreeCode codes = huffmanCodes[i]; this.StoreHuffmanCode(huffTree, tokens, codes); @@ -531,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // TODO: Keep track of the smallest image so far. if (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes()) { - // TODO : This was done in the reference by swapping references, this will be slower + // TODO: This was done in the reference by swapping references, this will be slower bitWriterBest = this.bitWriter.Clone(); } } @@ -606,7 +607,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private void EncodeImageNoHuffman(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality) { int cacheBits = 0; - var histogramSymbols = new short[1]; // Only one tree, one symbol. + var histogramSymbols = new ushort[1]; // Only one tree, one symbol. // TODO: Can HuffmanTreeCode be struct var huffmanCodes = new HuffmanTreeCode[5]; @@ -652,7 +653,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Find maximum number of symbols for the huffman tree-set. int maxTokens = 0; - for (int i = 0; i < 5; i++) + for (int i = 0; i < 5; ++i) { HuffmanTreeCode codes = huffmanCodes[i]; if (maxTokens < codes.NumSymbols) @@ -662,13 +663,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } var tokens = new HuffmanTreeToken[maxTokens]; - for (int i = 0; i < tokens.Length; i++) + for (int i = 0; i < tokens.Length; ++i) { tokens[i] = new HuffmanTreeToken(); } // Store Huffman codes. - for (int i = 0; i < 5; i++) + for (int i = 0; i < 5; ++i) { HuffmanTreeCode codes = huffmanCodes[i]; this.StoreHuffmanCode(huffTree, tokens, codes); @@ -849,7 +850,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, short[] histogramSymbols, HuffmanTreeCode[] huffmanCodes) + private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, ushort[] histogramSymbols, HuffmanTreeCode[] huffmanCodes) { int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1; int tileMask = (histoBits == 0) ? 0 : -(1 << histoBits); diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs index 78420185bc..dd5c9ebda5 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless this.Distance = new uint[WebPConstants.NumDistanceCodes]; var literalSize = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + (1 << WebPConstants.MaxColorCacheBits); - this.Literal = new uint[literalSize]; + this.Literal = new uint[literalSize + 1]; // 5 for literal, red, blue, alpha, distance. this.IsUsed = new bool[5]; diff --git a/src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs index bf59394d3c..943d18397e 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs @@ -145,7 +145,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (isColorCachePresent) { colorCacheBits = (int)this.bitReader.ReadValue(4); - bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits; + + // Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum. + // That is why 11 bits is also considered valid here. + bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= (WebPConstants.MaxColorCacheBits + 1); if (!colorCacheBitsIsValid) { WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); @@ -162,11 +165,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless decoder.Metadata.ColorCache = new ColorCache(); colorCacheSize = 1 << colorCacheBits; decoder.Metadata.ColorCacheSize = colorCacheSize; - if (!(colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits)) - { - WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); - } - decoder.Metadata.ColorCache.Init(colorCacheBits); } else diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index edec77ad88..71fab497d9 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -114,9 +114,9 @@ namespace SixLabors.ImageSharp.Formats.WebP public const int MaxPaletteSize = 256; /// - /// Maximum number of color cache bits is 11. + /// Maximum number of color cache bits is 10. /// - public const int MaxColorCacheBits = 11; + public const int MaxColorCacheBits = 10; /// /// The maximum number of allowed transforms in a VP8L bitstream.