diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs index c49b4f115..e3a010134 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; @@ -10,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; /// Stores left, top, and top-left neighbor values at the granularity required by AV1 encoder contexts. /// /// The context value type, including its invalid sentinel value. -internal class Av1NeighborArrayUnit +internal sealed class Av1NeighborArrayUnit : IDisposable where T : struct, IMinMaxValue { /// @@ -19,31 +21,42 @@ internal class Av1NeighborArrayUnit public static readonly T InvalidNeighborData = T.MaxValue; /// - /// Stores context units exposed to blocks on the right. + /// Owns the contiguous neighbor storage until this instance is disposed. /// - private readonly T[] left; + private IMemoryOwner? memory; /// - /// Stores context units exposed to blocks below. + /// The number of context values exposed to blocks on the right. /// - private readonly T[] top; + private readonly int leftLength; /// - /// Stores context units indexed by the diagonal difference between horizontal and vertical positions. + /// The number of context values exposed to blocks below. /// - private readonly T[] topLeft; + private readonly int topLength; + + /// + /// The number of context values indexed by the diagonal difference between horizontal and vertical positions. + /// + private readonly int topLeftLength; /// /// Initializes a new instance of the class. /// + /// The configuration providing the memory allocator. /// The number of values in the left-neighbor storage. /// The number of values in the top-neighbor storage. /// The number of values in the diagonal-neighbor storage. - public Av1NeighborArrayUnit(int leftSize, int topSize, int topLeftSize) + public Av1NeighborArrayUnit(Configuration configuration, int leftSize, int topSize, int topLeftSize) { - this.left = new T[leftSize]; - this.top = new T[topSize]; - this.topLeft = new T[topLeftSize]; + this.leftLength = leftSize; + this.topLength = topSize; + this.topLeftLength = topLeftSize; + int totalLength = checked(leftSize + topSize + topLeftSize); + + // All three neighbor regions share the picture lifetime, so one clean allocator-backed + // buffer avoids three managed arrays and preserves their zero-initialized starting state. + this.memory = configuration.MemoryAllocator.Allocate(totalLength, AllocationOptions.Clean); } /// @@ -71,17 +84,38 @@ internal class Av1NeighborArrayUnit /// /// Gets the left-neighbor storage. /// - public Span Left => this.left; + public Span Left + { + get + { + ObjectDisposedException.ThrowIf(this.memory is null, this); + return this.memory.Memory.Span[..this.leftLength]; + } + } /// /// Gets the top-neighbor storage. /// - public Span Top => this.top; + public Span Top + { + get + { + ObjectDisposedException.ThrowIf(this.memory is null, this); + return this.memory.Memory.Span.Slice(this.leftLength, this.topLength); + } + } /// /// Gets the top-left diagonal storage. /// - public Span TopLeft => this.topLeft; + public Span TopLeft + { + get + { + ObjectDisposedException.ThrowIf(this.memory is null, this); + return this.memory.Memory.Span.Slice(this.leftLength + this.topLength, this.topLeftLength); + } + } /// /// Gets or sets the base-2 logarithm of the top and left context granularity in samples. @@ -118,7 +152,16 @@ internal class Av1NeighborArrayUnit /// The sample position. /// The top-left neighbor index derived from the position's diagonal. public int GetTopLeftIndex(Point loc) - => this.left.Length + (loc.X >> this.GranularityTopLeftLog2) - (loc.Y >> this.GranularityTopLeftLog2); + => this.leftLength + (loc.X >> this.GranularityTopLeftLog2) - (loc.Y >> this.GranularityTopLeftLog2); + + /// + /// Returns the neighbor storage to the configured memory allocator. + /// + public void Dispose() + { + this.memory?.Dispose(); + this.memory = null; + } /// /// Writes one context unit across the selected block edges.