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.