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); + } } }