diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
index dc546f8ac2..93f6372c64 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
@@ -3,10 +3,11 @@
using System;
using System.Collections.Generic;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
- internal class BackwardReferenceEncoder
+ internal static class BackwardReferenceEncoder
{
///
/// Maximum bit length.
@@ -41,6 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int quality,
int lz77TypesToTry,
ref int cacheBits,
+ MemoryAllocator memoryAllocator,
Vp8LHashChain hashChain,
Vp8LBackwardRefs best,
Vp8LBackwardRefs worst)
@@ -69,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
BackwardReferencesLz77(width, height, bgra, 0, hashChain, worst);
break;
case Vp8LLz77Type.Lz77Box:
- hashChainBox = new Vp8LHashChain(width * height);
+ hashChainBox = new Vp8LHashChain(memoryAllocator, width * height);
BackwardReferencesLz77Box(width, height, bgra, 0, hashChain, hashChainBox, worst);
break;
}
@@ -617,7 +619,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
- hashChain.OffsetLength[0] = 0;
+ Span hashChainOffsetLength = hashChain.OffsetLength.GetSpan();
+ hashChainOffsetLength[0] = 0;
for (i = 1; i < pixelCount; i++)
{
int ind;
@@ -695,19 +698,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (bestLength <= MinLength)
{
- hashChain.OffsetLength[i] = 0;
+ hashChainOffsetLength[i] = 0;
bestOffsetPrev = 0;
bestLengthPrev = 0;
}
else
{
- hashChain.OffsetLength[i] = (uint)((bestOffset << MaxLengthBits) | bestLength);
+ hashChainOffsetLength[i] = (uint)((bestOffset << MaxLengthBits) | bestLength);
bestOffsetPrev = bestOffset;
bestLengthPrev = bestLength;
}
}
- hashChain.OffsetLength[0] = 0;
+ hashChainOffsetLength[0] = 0;
BackwardReferencesLz77(xSize, ySize, bgra, cacheBits, hashChain, refs);
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
index da815a479a..48f7d0e2b7 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
@@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.EncodedData = memoryAllocator.Allocate(pixelCount);
this.Palette = memoryAllocator.Allocate(WebpConstants.MaxPaletteSize);
this.Refs = new Vp8LBackwardRefs[3];
- this.HashChain = new Vp8LHashChain(pixelCount);
+ this.HashChain = new Vp8LHashChain(memoryAllocator, pixelCount);
// We round the block size up, so we're guaranteed to have at most MaxRefsBlockPerImage blocks used:
int refsBlockSize = ((pixelCount - 1) / MaxRefsBlockPerImage) + 1;
@@ -515,7 +515,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Calculate backward references from BGRA image.
- this.HashChain.Fill(this.memoryAllocator, bgra, this.quality, width, height, lowEffort);
+ this.HashChain.Fill(bgra, this.quality, width, height, lowEffort);
Vp8LBitWriter bitWriterBest = config.SubConfigs.Count > 1 ? this.bitWriter.Clone() : this.bitWriter;
Vp8LBitWriter bwInit = this.bitWriter;
@@ -529,6 +529,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.quality,
subConfig.Lz77,
ref cacheBits,
+ this.memoryAllocator,
this.HashChain,
this.Refs[0],
this.Refs[1]);
@@ -735,7 +736,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Calculate backward references from the image pixels.
- hashChain.Fill(this.memoryAllocator, bgra, quality, width, height, lowEffort);
+ hashChain.Fill(bgra, quality, width, height, lowEffort);
Vp8LBackwardRefs refs = BackwardReferenceEncoder.GetBackwardReferences(
width,
@@ -744,6 +745,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
quality,
(int)Vp8LLz77Type.Lz77Standard | (int)Vp8LLz77Type.Lz77Rle,
ref cacheBits,
+ this.memoryAllocator,
hashChain,
refsTmp1,
refsTmp2);
@@ -1802,6 +1804,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.BgraScratch.Dispose();
this.Palette.Dispose();
this.TransformData.Dispose();
+ this.HashChain.Dispose();
}
}
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs
index 977a094bd1..2aa35e392e 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs
@@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
- internal class Vp8LHashChain
+ internal class Vp8LHashChain : IDisposable
{
private const uint HashMultiplierHi = 0xc6a4a793u;
@@ -28,14 +28,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
private const int WindowSize = (1 << WindowSizeBits) - 120;
+ private readonly MemoryAllocator memoryAllocator;
+
+ private bool disposed;
+
///
/// Initializes a new instance of the class.
///
+ /// The memory allocator.
/// The size off the chain.
- public Vp8LHashChain(int size)
+ public Vp8LHashChain(MemoryAllocator memoryAllocator, int size)
{
- this.OffsetLength = new uint[size];
- this.OffsetLength.AsSpan().Fill(0xcdcdcdcd);
+ this.memoryAllocator = memoryAllocator;
+ this.OffsetLength = this.memoryAllocator.Allocate(size, AllocationOptions.Clean);
this.Size = size;
}
@@ -45,16 +50,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// These 20 bits are the limit defined by GetWindowSizeForHashChain (through WindowSize = 1 << 20).
/// The lower 12 bits contain the length of the match.
///
- public uint[] OffsetLength { get; }
+ public IMemoryOwner OffsetLength { get; }
///
/// Gets the size of the hash chain.
- /// This is the maximum size of the hash_chain that can be constructed.
+ /// This is the maximum size of the hashchain that can be constructed.
/// Typically this is the pixel count (width x height) for a given image.
///
public int Size { get; }
- public void Fill(MemoryAllocator memoryAllocator, ReadOnlySpan bgra, int quality, int xSize, int ySize, bool lowEffort)
+ public void Fill(ReadOnlySpan bgra, int quality, int xSize, int ySize, bool lowEffort)
{
int size = xSize * ySize;
int iterMax = GetMaxItersForQuality(quality);
@@ -63,20 +68,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (size <= 2)
{
- this.OffsetLength[0] = 0;
+ this.OffsetLength.GetSpan()[0] = 0;
return;
}
- using IMemoryOwner hashToFirstIndexBuffer = memoryAllocator.Allocate(HashSize);
+ using IMemoryOwner hashToFirstIndexBuffer = this.memoryAllocator.Allocate(HashSize);
+ using IMemoryOwner chainBuffer = this.memoryAllocator.Allocate(size, AllocationOptions.Clean);
Span hashToFirstIndex = hashToFirstIndexBuffer.GetSpan();
+ Span chain = chainBuffer.GetSpan();
// Initialize hashToFirstIndex array to -1.
hashToFirstIndex.Fill(-1);
- int[] chain = new int[size];
-
// Fill the chain linking pixels with the same hash.
bool bgraComp = bgra.Length > 1 && bgra[0] == bgra[1];
+ Span tmp = stackalloc uint[2];
for (pos = 0; pos < size - 2;)
{
uint hashCode;
@@ -85,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
// Consecutive pixels with the same color will share the same hash.
// We therefore use a different hash: the color and its repetition length.
- uint[] tmp = new uint[2];
+ tmp.Clear();
uint len = 1;
tmp[0] = bgra[pos];
@@ -134,7 +140,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Find the best match interval at each pixel, defined by an offset to the
// pixel and a length. The right-most pixel cannot match anything to the right
// (hence a best length of 0) and the left-most pixel nothing to the left (hence an offset of 0).
- this.OffsetLength[0] = this.OffsetLength[size - 1] = 0;
+ Span offsetLength = this.OffsetLength.GetSpan();
+ offsetLength[0] = offsetLength[size - 1] = 0;
for (int basePosition = size - 2; basePosition > 0;)
{
int maxLen = LosslessUtils.MaxFindCopyLength(size - 1 - basePosition);
@@ -208,7 +215,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint maxBasePosition = (uint)basePosition;
while (true)
{
- this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
+ offsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
--basePosition;
// Stop if we don't have a match or if we are out of bounds.
@@ -242,10 +249,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
[MethodImpl(InliningOptions.ShortMethod)]
- public int FindLength(int basePosition) => (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
+ public int FindLength(int basePosition) => (int)(this.OffsetLength.GetSpan()[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
[MethodImpl(InliningOptions.ShortMethod)]
- public int FindOffset(int basePosition) => (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
+ public int FindOffset(int basePosition) => (int)(this.OffsetLength.GetSpan()[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
///
/// Calculates the hash for a pixel pair.
@@ -280,5 +287,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return maxWindowSize > WindowSize ? WindowSize : maxWindowSize;
}
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.disposed)
+ {
+ if (disposing)
+ {
+ this.OffsetLength.Dispose();
+ }
+
+ this.disposed = true;
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
}
}