diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs index 93f6372c64..82aa3ff7b1 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using SixLabors.ImageSharp.Memory; @@ -102,7 +103,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25) { Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox; - BackwardReferencesTraceBackwards(width, height, bgra, cacheBits, hashChainTmp, best, worst); + BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst); var histo = new Vp8LHistogram(worst, cacheBits); double bitCostTrace = histo.EstimateBits(stats, bitsEntropy); if (bitCostTrace < bitCostBest) @@ -236,6 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless private static void BackwardReferencesTraceBackwards( int xSize, int ySize, + MemoryAllocator memoryAllocator, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChain, @@ -243,22 +245,24 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless Vp8LBackwardRefs refsDst) { int distArraySize = xSize * ySize; - ushort[] distArray = new ushort[distArraySize]; + using IMemoryOwner distArrayBuffer = memoryAllocator.Allocate(distArraySize); + Span distArray = distArrayBuffer.GetSpan(); - BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray); + BackwardReferencesHashChainDistanceOnly(xSize, ySize, memoryAllocator, bgra, cacheBits, hashChain, refsSrc, distArrayBuffer); int chosenPathSize = TraceBackwards(distArray, distArraySize); - Span chosenPath = distArray.AsSpan(distArraySize - chosenPathSize); + Span chosenPath = distArray.Slice(distArraySize - chosenPathSize); BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, chosenPathSize, hashChain, refsDst); } private static void BackwardReferencesHashChainDistanceOnly( int xSize, int ySize, + MemoryAllocator memoryAllocator, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, - ushort[] distArray) + IMemoryOwner distArrayBuffer) { int pixCount = xSize * ySize; bool useColorCache = cacheBits > 0; @@ -277,22 +281,24 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } costModel.Build(xSize, cacheBits, refs); - var costManager = new CostManager(distArray, pixCount, costModel); + var costManager = new CostManager(memoryAllocator, distArrayBuffer, pixCount, costModel); + Span costManagerCosts = costManager.Costs.GetSpan(); + Span distArray = distArrayBuffer.GetSpan(); // We loop one pixel at a time, but store all currently best points to non-processed locations from this point. distArray[0] = 0; // Add first pixel as literal. - AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManager.Costs, distArray); + AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManagerCosts, distArray); for (int i = 1; i < pixCount; i++) { - float prevCost = costManager.Costs[i - 1]; + float prevCost = costManagerCosts[i - 1]; int offset = hashChain.FindOffset(i); int len = hashChain.FindLength(i); // Try adding the pixel as a literal. - AddSingleLiteralWithCostModel(bgra, colorCache, costModel, i, useColorCache, prevCost, costManager.Costs, distArray); + AddSingleLiteralWithCostModel(bgra, colorCache, costModel, i, useColorCache, prevCost, costManagerCosts, distArray); // If we are dealing with a non-literal. if (len >= 2) @@ -336,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless costManager.UpdateCostAtIndex(j - 1, false); costManager.UpdateCostAtIndex(j, false); - costManager.PushInterval(costManager.Costs[j - 1] + offsetCost, j, lenJ); + costManager.PushInterval(costManagerCosts[j - 1] + offsetCost, j, lenJ); reach = j + lenJ - 1; } } @@ -348,7 +354,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } } - private static int TraceBackwards(ushort[] distArray, int distArraySize) + private static int TraceBackwards(Span distArray, int distArraySize) { int chosenPathSize = 0; int pathPos = distArraySize; @@ -428,8 +434,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless int idx, bool useColorCache, float prevCost, - float[] cost, - ushort[] distArray) + Span cost, + Span distArray) { double costVal = prevCost; uint color = bgra[idx]; diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs index 213971764b..3ee1021386 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs @@ -1,7 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Buffers; using System.Collections.Generic; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Webp.Lossless { @@ -10,21 +13,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless /// It caches the different CostCacheInterval, caches the different /// GetLengthCost(costModel, k) in costCache and the CostInterval's. /// - internal class CostManager + internal class CostManager : IDisposable { + private bool disposed; + private CostInterval head; private const int FreeIntervalsStartCount = 25; private readonly Stack freeIntervals = new(FreeIntervalsStartCount); - public CostManager(ushort[] distArray, int pixCount, CostModel costModel) + public CostManager(MemoryAllocator memoryAllocator, IMemoryOwner distArray, int pixCount, CostModel costModel) { int costCacheSize = pixCount > BackwardReferenceEncoder.MaxLength ? BackwardReferenceEncoder.MaxLength : pixCount; this.CacheIntervals = new List(); this.CostCache = new List(); - this.Costs = new float[pixCount]; + this.Costs = memoryAllocator.Allocate(pixCount); this.DistArray = distArray; this.Count = 0; @@ -73,10 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } // Set the initial costs high for every pixel as we will keep the minimum. - for (int i = 0; i < pixCount; i++) - { - this.Costs[i] = 1e38f; - } + this.Costs.GetSpan().Fill(1e38f); } /// @@ -91,9 +93,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless public int CacheIntervalsSize { get; } - public float[] Costs { get; } + public IMemoryOwner Costs { get; } - public ushort[] DistArray { get; } + public IMemoryOwner DistArray { get; } public List CacheIntervals { get; } @@ -137,6 +139,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless // interval logic, just serialize it right away. This constant is empirical. int skipDistance = 10; + Span costs = this.Costs.GetSpan(); + Span distArray = this.DistArray.GetSpan(); if (len < skipDistance) { for (int j = position; j < position + len; j++) @@ -144,10 +148,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless int k = j - position; float costTmp = (float)(distanceCost + this.CostCache[k]); - if (this.Costs[j] > costTmp) + if (costs[j] > costTmp) { - this.Costs[j] = costTmp; - this.DistArray[j] = (ushort)(k + 1); + costs[j] = costTmp; + distArray[j] = (ushort)(k + 1); } } @@ -314,12 +318,35 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless /// private void UpdateCost(int i, int position, float cost) { + Span costs = this.Costs.GetSpan(); + Span distArray = this.DistArray.GetSpan(); int k = i - position; - if (this.Costs[i] > cost) + if (costs[i] > cost) + { + costs[i] = cost; + distArray[i] = (ushort)(k + 1); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) { - this.Costs[i] = cost; - this.DistArray[i] = (ushort)(k + 1); + if (disposing) + { + this.Costs.Dispose(); + } + + this.disposed = true; } } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } } }