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++)
{