diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs index 922ae0193d..a56fc0faca 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs @@ -52,6 +52,8 @@ internal static class BackwardReferenceEncoder Vp8LHashChain? hashChainBox = null; Vp8LStreaks stats = new(); Vp8LBitEntropy bitsEntropy = new(); + + ColorCache[] colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1]; for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1) { int cacheBitsTmp = cacheBitsInitial; @@ -76,7 +78,7 @@ internal static class BackwardReferenceEncoder } // Next, try with a color cache and update the references. - cacheBitsTmp = CalculateBestCacheSize(memoryAllocator, bgra, quality, worst, cacheBitsTmp); + cacheBitsTmp = CalculateBestCacheSize(memoryAllocator, colorCache, bgra, quality, worst, cacheBitsTmp); if (cacheBitsTmp > 0) { BackwardRefsWithLocalCache(bgra, cacheBitsTmp, worst); @@ -123,6 +125,7 @@ internal static class BackwardReferenceEncoder /// Best cache size. private static int CalculateBestCacheSize( MemoryAllocator memoryAllocator, + Span colorCache, ReadOnlySpan bgra, uint quality, Vp8LBackwardRefs refs, @@ -138,12 +141,8 @@ internal static class BackwardReferenceEncoder double entropyMin = MaxEntropy; int pos = 0; - // TODO: Pass from outer loop and clear. - ColorCache[] colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1]; - - // TODO: Use fixed size. - using Vp8LHistogramSet histos = new(memoryAllocator, WebpConstants.MaxColorCacheBits + 1, 0); - for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++) + using Vp8LHistogramSet histos = new(memoryAllocator, colorCache.Length, 0); + for (int i = 0; i < colorCache.Length; i++) { histos[i].PaletteCodeBits = i; colorCache[i] = new ColorCache(i); @@ -448,12 +447,12 @@ internal static class BackwardReferenceEncoder int ix = useColorCache ? colorCache!.Contains(color) : -1; if (ix >= 0) { - double mul0 = 0.68; + const double mul0 = 0.68; costVal += costModel.GetCacheCost((uint)ix) * mul0; } else { - double mul1 = 0.82; + const double mul1 = 0.82; if (useColorCache) { colorCache!.Insert(color); @@ -700,10 +699,8 @@ internal static class BackwardReferenceEncoder bestLength = MaxLength; break; } - else - { - bestLength = currLength; - } + + bestLength = currLength; } } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs index 8a0d132063..cce0356176 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. #nullable disable +using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; @@ -45,9 +46,9 @@ internal static class HistogramEncoder int imageHistoRawSize = histoXSize * histoYSize; const int entropyCombineNumBins = BinSize; - // TODO: Allocations! - ushort[] mapTmp = new ushort[imageHistoRawSize]; - ushort[] clusterMappings = new ushort[imageHistoRawSize]; + using IMemoryOwner tmp = memoryAllocator.Allocate(imageHistoRawSize * 2, AllocationOptions.Clean); + Span mapTmp = tmp.Slice(0, imageHistoRawSize); + Span clusterMappings = tmp.Slice(imageHistoRawSize, imageHistoRawSize); using Vp8LHistogramSet origHisto = new(memoryAllocator, imageHistoRawSize, cacheBits); @@ -60,13 +61,12 @@ internal static class HistogramEncoder bool entropyCombine = numUsed > entropyCombineNumBins * 2 && quality < 100; if (entropyCombine) { - ushort[] binMap = mapTmp; int numClusters = numUsed; double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality); - HistogramAnalyzeEntropyBin(imageHisto, binMap); + HistogramAnalyzeEntropyBin(imageHisto, mapTmp); // Collapse histograms with similar entropy. - HistogramCombineEntropyBin(imageHisto, histogramSymbols, clusterMappings, tmpHisto, binMap, entropyCombineNumBins, combineCostFactor); + HistogramCombineEntropyBin(imageHisto, histogramSymbols, clusterMappings, tmpHisto, mapTmp, entropyCombineNumBins, combineCostFactor); OptimizeHistogramSymbols(clusterMappings, numClusters, mapTmp, histogramSymbols); } @@ -128,7 +128,7 @@ internal static class HistogramEncoder /// 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(Vp8LHistogramSet histograms, ushort[] binMap) + private static void HistogramAnalyzeEntropyBin(Vp8LHistogramSet histograms, Span binMap) { int histoSize = histograms.Count; DominantCostRange costRange = new(); @@ -198,9 +198,9 @@ internal static class HistogramEncoder private static void HistogramCombineEntropyBin( Vp8LHistogramSet histograms, Span clusters, - ushort[] clusterMappings, + Span clusterMappings, Vp8LHistogram curCombo, - ushort[] binMap, + ReadOnlySpan binMap, int numBins, double combineCostFactor) { @@ -276,7 +276,7 @@ internal static class HistogramEncoder /// 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(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, Span symbols) + private static void OptimizeHistogramSymbols(Span clusterMappings, int numClusters, Span clusterMappingsTmp, Span symbols) { bool doContinue = true; @@ -303,7 +303,7 @@ internal static class HistogramEncoder // Create a mapping from a cluster id to its minimal version. int clusterMax = 0; - clusterMappingsTmp.AsSpan().Clear(); + clusterMappingsTmp.Clear(); // Re-map the ids. for (int i = 0; i < symbols.Length; i++) @@ -515,7 +515,6 @@ internal static class HistogramEncoder histograms.DisposeAt(idx2); // Remove pairs intersecting the just combined best pair. - // TODO: Reversing this will avoid the need to remove from the end of the list. for (int i = 0; i < histoPriorityList.Count;) { HistogramPair p = histoPriorityList[i]; diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 10fc8ab804..d570bd448a 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -885,7 +885,7 @@ internal class Vp8LEncoder : IDisposable private void StoreFullHuffmanCode(Span huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) { - // TODO: Allocations. + // TODO: Allocations. This method is called in a loop. int i; byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes]; @@ -1628,6 +1628,7 @@ internal class Vp8LEncoder : IDisposable } } + // TODO: Allocations. int end = 5 * histogramImage.Count; for (int i = 0; i < end; i++) { @@ -1641,9 +1642,9 @@ internal class Vp8LEncoder : IDisposable } // Create Huffman trees. - // TODO: Allocations. Size here has a max and can be sliced. + // TODO: Allocations. bool[] bufRle = new bool[maxNumSymbols]; - Span huffTree = stackalloc HuffmanTree[3 * maxNumSymbols]; + HuffmanTree[] huffTree = new HuffmanTree[3 * maxNumSymbols]; for (int i = 0; i < histogramImage.Count; i++) {