Skip to content

Commit 7e1ee9e

Browse files
Merge pull request #2546 from SixLabors/js/webp-allocations
WebP - Reduce the allocations in lossless encoding
2 parents 0888e54 + c51f5ae commit 7e1ee9e

File tree

12 files changed

+571
-316
lines changed

12 files changed

+571
-316
lines changed

src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ public static Vp8LBackwardRefs GetBackwardReferences(
5050
double bitCostBest = -1;
5151
int cacheBitsInitial = cacheBits;
5252
Vp8LHashChain? hashChainBox = null;
53-
var stats = new Vp8LStreaks();
54-
var bitsEntropy = new Vp8LBitEntropy();
53+
Vp8LStreaks stats = new();
54+
Vp8LBitEntropy bitsEntropy = new();
55+
56+
ColorCache[] colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
5557
for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1)
5658
{
5759
int cacheBitsTmp = cacheBitsInitial;
@@ -76,21 +78,19 @@ public static Vp8LBackwardRefs GetBackwardReferences(
7678
}
7779

7880
// Next, try with a color cache and update the references.
79-
cacheBitsTmp = CalculateBestCacheSize(bgra, quality, worst, cacheBitsTmp);
81+
cacheBitsTmp = CalculateBestCacheSize(memoryAllocator, colorCache, bgra, quality, worst, cacheBitsTmp);
8082
if (cacheBitsTmp > 0)
8183
{
8284
BackwardRefsWithLocalCache(bgra, cacheBitsTmp, worst);
8385
}
8486

8587
// Keep the best backward references.
86-
var histo = new Vp8LHistogram(worst, cacheBitsTmp);
88+
using OwnedVp8LHistogram histo = OwnedVp8LHistogram.Create(memoryAllocator, worst, cacheBitsTmp);
8789
double bitCost = histo.EstimateBits(stats, bitsEntropy);
8890

8991
if (lz77TypeBest == 0 || bitCost < bitCostBest)
9092
{
91-
Vp8LBackwardRefs tmp = worst;
92-
worst = best;
93-
best = tmp;
93+
(best, worst) = (worst, best);
9494
bitCostBest = bitCost;
9595
cacheBits = cacheBitsTmp;
9696
lz77TypeBest = lz77Type;
@@ -102,7 +102,7 @@ public static Vp8LBackwardRefs GetBackwardReferences(
102102
{
103103
Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!;
104104
BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
105-
var histo = new Vp8LHistogram(worst, cacheBits);
105+
using OwnedVp8LHistogram histo = OwnedVp8LHistogram.Create(memoryAllocator, worst, cacheBits);
106106
double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
107107
if (bitCostTrace < bitCostBest)
108108
{
@@ -123,7 +123,13 @@ public static Vp8LBackwardRefs GetBackwardReferences(
123123
/// The local color cache is also disabled for the lower (smaller then 25) quality.
124124
/// </summary>
125125
/// <returns>Best cache size.</returns>
126-
private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality, Vp8LBackwardRefs refs, int bestCacheBits)
126+
private static int CalculateBestCacheSize(
127+
MemoryAllocator memoryAllocator,
128+
Span<ColorCache> colorCache,
129+
ReadOnlySpan<uint> bgra,
130+
uint quality,
131+
Vp8LBackwardRefs refs,
132+
int bestCacheBits)
127133
{
128134
int cacheBitsMax = quality <= 25 ? 0 : bestCacheBits;
129135
if (cacheBitsMax == 0)
@@ -134,11 +140,11 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
134140

135141
double entropyMin = MaxEntropy;
136142
int pos = 0;
137-
var colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
138-
var histos = new Vp8LHistogram[WebpConstants.MaxColorCacheBits + 1];
139-
for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++)
143+
144+
using Vp8LHistogramSet histos = new(memoryAllocator, colorCache.Length, 0);
145+
for (int i = 0; i < colorCache.Length; i++)
140146
{
141-
histos[i] = new Vp8LHistogram(paletteCodeBits: i);
147+
histos[i].PaletteCodeBits = i;
142148
colorCache[i] = new ColorCache(i);
143149
}
144150

@@ -149,10 +155,10 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
149155
if (v.IsLiteral())
150156
{
151157
uint pix = bgra[pos++];
152-
uint a = (pix >> 24) & 0xff;
153-
uint r = (pix >> 16) & 0xff;
154-
uint g = (pix >> 8) & 0xff;
155-
uint b = (pix >> 0) & 0xff;
158+
int a = (int)(pix >> 24) & 0xff;
159+
int r = (int)(pix >> 16) & 0xff;
160+
int g = (int)(pix >> 8) & 0xff;
161+
int b = (int)(pix >> 0) & 0xff;
156162

157163
// The keys of the caches can be derived from the longest one.
158164
int key = ColorCache.HashPix(pix, 32 - cacheBitsMax);
@@ -218,8 +224,8 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
218224
}
219225
}
220226

221-
var stats = new Vp8LStreaks();
222-
var bitsEntropy = new Vp8LBitEntropy();
227+
Vp8LStreaks stats = new();
228+
Vp8LBitEntropy bitsEntropy = new();
223229
for (int i = 0; i <= cacheBitsMax; i++)
224230
{
225231
double entropy = histos[i].EstimateBits(stats, bitsEntropy);
@@ -266,7 +272,7 @@ private static void BackwardReferencesHashChainDistanceOnly(
266272
int pixCount = xSize * ySize;
267273
bool useColorCache = cacheBits > 0;
268274
int literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (cacheBits > 0 ? 1 << cacheBits : 0);
269-
var costModel = new CostModel(literalArraySize);
275+
CostModel costModel = new(memoryAllocator, literalArraySize);
270276
int offsetPrev = -1;
271277
int lenPrev = -1;
272278
double offsetCost = -1;
@@ -280,7 +286,7 @@ private static void BackwardReferencesHashChainDistanceOnly(
280286
}
281287

282288
costModel.Build(xSize, cacheBits, refs);
283-
using var costManager = new CostManager(memoryAllocator, distArrayBuffer, pixCount, costModel);
289+
using CostManager costManager = new(memoryAllocator, distArrayBuffer, pixCount, costModel);
284290
Span<float> costManagerCosts = costManager.Costs.GetSpan();
285291
Span<ushort> distArray = distArrayBuffer.GetSpan();
286292

@@ -441,12 +447,12 @@ private static void AddSingleLiteralWithCostModel(
441447
int ix = useColorCache ? colorCache!.Contains(color) : -1;
442448
if (ix >= 0)
443449
{
444-
double mul0 = 0.68;
450+
const double mul0 = 0.68;
445451
costVal += costModel.GetCacheCost((uint)ix) * mul0;
446452
}
447453
else
448454
{
449-
double mul1 = 0.82;
455+
const double mul1 = 0.82;
450456
if (useColorCache)
451457
{
452458
colorCache!.Insert(color);
@@ -693,10 +699,8 @@ private static void BackwardReferencesLz77Box(int xSize, int ySize, ReadOnlySpan
693699
bestLength = MaxLength;
694700
break;
695701
}
696-
else
697-
{
698-
bestLength = currLength;
699-
}
702+
703+
bestLength = currLength;
700704
}
701705
}
702706
}

src/ImageSharp/Formats/Webp/Lossless/CostModel.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using SixLabors.ImageSharp.Memory;
5+
46
namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
57

68
internal class CostModel
79
{
10+
private readonly MemoryAllocator memoryAllocator;
811
private const int ValuesInBytes = 256;
912

1013
/// <summary>
1114
/// Initializes a new instance of the <see cref="CostModel"/> class.
1215
/// </summary>
16+
/// <param name="memoryAllocator">The memory allocator.</param>
1317
/// <param name="literalArraySize">The literal array size.</param>
14-
public CostModel(int literalArraySize)
18+
public CostModel(MemoryAllocator memoryAllocator, int literalArraySize)
1519
{
20+
this.memoryAllocator = memoryAllocator;
1621
this.Alpha = new double[ValuesInBytes];
1722
this.Red = new double[ValuesInBytes];
1823
this.Blue = new double[ValuesInBytes];
@@ -32,13 +37,12 @@ public CostModel(int literalArraySize)
3237

3338
public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs)
3439
{
35-
var histogram = new Vp8LHistogram(cacheBits);
36-
using System.Collections.Generic.List<PixOrCopy>.Enumerator refsEnumerator = backwardRefs.Refs.GetEnumerator();
40+
using OwnedVp8LHistogram histogram = OwnedVp8LHistogram.Create(this.memoryAllocator, cacheBits);
3741

3842
// The following code is similar to HistogramCreate but converts the distance to plane code.
39-
while (refsEnumerator.MoveNext())
43+
for (int i = 0; i < backwardRefs.Refs.Count; i++)
4044
{
41-
histogram.AddSinglePixOrCopy(refsEnumerator.Current, true, xSize);
45+
histogram.AddSinglePixOrCopy(backwardRefs.Refs[i], true, xSize);
4246
}
4347

4448
ConvertPopulationCountTableToBitEstimates(histogram.NumCodes(), histogram.Literal, this.Literal);
@@ -70,7 +74,7 @@ public double GetCacheCost(uint idx)
7074

7175
public double GetLiteralCost(uint v) => this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff];
7276

73-
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output)
77+
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, Span<uint> populationCounts, double[] output)
7478
{
7579
uint sum = 0;
7680
int nonzeros = 0;

0 commit comments

Comments
 (0)