|
|
|
@ -1,60 +1,80 @@ |
|
|
|
// Copyright (c) Six Labors.
|
|
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
|
|
|
|
using System.Buffers; |
|
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Stores entropy, partition, and transform contexts for 4-by-4 blocks left of the current block.
|
|
|
|
/// </summary>
|
|
|
|
internal class Av1ParseLeftNeighbor4x4Context |
|
|
|
internal sealed class Av1ParseLeftNeighbor4x4Context : IDisposable |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Stores DC-sign and cumulative coefficient-level contexts for each plane left of the current block.
|
|
|
|
/// The region containing transform-height contexts.
|
|
|
|
/// </summary>
|
|
|
|
private readonly int[][] leftContext = new int[Av1Constants.MaxPlanes][]; |
|
|
|
private const int TransformHeightRegionIndex = 0; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Stores segmentation-prediction contexts for the current superblock row.
|
|
|
|
/// The region containing partition-height contexts.
|
|
|
|
/// </summary>
|
|
|
|
private readonly int[] leftSegmentIdPredictionContext; |
|
|
|
private const int PartitionHeightRegionIndex = 1; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Stores compound-reference group contexts for the current superblock row.
|
|
|
|
/// The first region containing a color plane's coefficient contexts.
|
|
|
|
/// </summary>
|
|
|
|
private readonly int[] leftCompGroupIndex; |
|
|
|
private const int PlaneContextRegionStart = 2; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Owns the contiguous left-neighbor storage until this instance is disposed.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<int>? memory; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The number of superblock-row entries stored in each logical region.
|
|
|
|
/// </summary>
|
|
|
|
private readonly int contextLength; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="Av1ParseLeftNeighbor4x4Context"/> class.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration">The configuration providing the memory allocator.</param>
|
|
|
|
/// <param name="planesCount">The number of color planes.</param>
|
|
|
|
/// <param name="superblockModeInfoSize">The superblock height in 4x4 mode-information rows.</param>
|
|
|
|
public Av1ParseLeftNeighbor4x4Context(int planesCount, int superblockModeInfoSize) |
|
|
|
public Av1ParseLeftNeighbor4x4Context(Configuration configuration, int planesCount, int superblockModeInfoSize) |
|
|
|
{ |
|
|
|
this.LeftTransformHeight = new int[superblockModeInfoSize]; |
|
|
|
this.LeftPartitionHeight = new int[superblockModeInfoSize]; |
|
|
|
for (int i = 0; i < planesCount; i++) |
|
|
|
{ |
|
|
|
this.leftContext[i] = new int[superblockModeInfoSize]; |
|
|
|
} |
|
|
|
this.contextLength = superblockModeInfoSize; |
|
|
|
int regionCount = PlaneContextRegionStart + planesCount; |
|
|
|
int totalLength = checked(regionCount * superblockModeInfoSize); |
|
|
|
|
|
|
|
this.leftSegmentIdPredictionContext = new int[superblockModeInfoSize]; |
|
|
|
this.leftCompGroupIndex = new int[superblockModeInfoSize]; |
|
|
|
// Every region spans the same superblock height and shares the tile-reader lifetime.
|
|
|
|
// One clean rent replaces the jagged array and its per-region arrays while preserving zero initialization.
|
|
|
|
this.memory = configuration.MemoryAllocator.Allocate<int>(totalLength, AllocationOptions.Clean); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets a buffer holding the partition context of the left 4x4 blocks corresponding
|
|
|
|
/// to the current super block row.
|
|
|
|
/// </summary>
|
|
|
|
public int[] LeftPartitionHeight { get; } |
|
|
|
public Span<int> LeftPartitionHeight => this.GetRegion(PartitionHeightRegionIndex); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets a buffer holding the transform sizes of the left 4x4 blocks corresponding
|
|
|
|
/// to the current super block row.
|
|
|
|
/// </summary>
|
|
|
|
public int[] LeftTransformHeight { get; } |
|
|
|
public Span<int> LeftTransformHeight => this.GetRegion(TransformHeightRegionIndex); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Returns the left-neighbor storage to the configured memory allocator.
|
|
|
|
/// </summary>
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
this.memory?.Dispose(); |
|
|
|
this.memory = null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Resets all left-neighbor state for a new superblock row.
|
|
|
|
@ -64,16 +84,12 @@ internal class Av1ParseLeftNeighbor4x4Context |
|
|
|
{ |
|
|
|
int blockCount = sequenceHeader.SuperblockModeInfoSize; |
|
|
|
int planeCount = sequenceHeader.ColorConfig.PlaneCount; |
|
|
|
int neighbor4x4Count = sequenceHeader.SuperblockModeInfoSize; |
|
|
|
Array.Fill(this.LeftTransformHeight, Av1TransformSize.Size64x64.GetHeight(), 0, blockCount); |
|
|
|
Array.Fill(this.LeftPartitionHeight, 0, 0, blockCount); |
|
|
|
this.LeftTransformHeight[..blockCount].Fill(Av1TransformSize.Size64x64.GetHeight()); |
|
|
|
this.LeftPartitionHeight[..blockCount].Clear(); |
|
|
|
for (int i = 0; i < planeCount; i++) |
|
|
|
{ |
|
|
|
Array.Fill(this.leftContext[i], 0, 0, blockCount); |
|
|
|
this.GetContext(i)[..blockCount].Clear(); |
|
|
|
} |
|
|
|
|
|
|
|
Array.Fill(this.leftSegmentIdPredictionContext, 0, 0, blockCount); |
|
|
|
Array.Fill(this.leftCompGroupIndex, 0, 0, blockCount); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -90,7 +106,7 @@ internal class Av1ParseLeftNeighbor4x4Context |
|
|
|
int bh = blockSize.Get4x4HighCount(); |
|
|
|
int value = Av1PartitionContext.GetLeftContext(subSize); |
|
|
|
DebugGuard.MustBeLessThanOrEqualTo(startIndex, this.LeftPartitionHeight.Length - bh, nameof(startIndex)); |
|
|
|
Array.Fill(this.LeftPartitionHeight, value, startIndex, bh); |
|
|
|
this.LeftPartitionHeight.Slice(startIndex, bh).Fill(value); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -113,7 +129,7 @@ internal class Av1ParseLeftNeighbor4x4Context |
|
|
|
} |
|
|
|
|
|
|
|
DebugGuard.MustBeLessThanOrEqualTo(startIndex, this.LeftTransformHeight.Length - n4h, nameof(startIndex)); |
|
|
|
Array.Fill(this.LeftTransformHeight, transformHeight, startIndex, n4h); |
|
|
|
this.LeftTransformHeight.Slice(startIndex, n4h).Fill(transformHeight); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -122,13 +138,24 @@ internal class Av1ParseLeftNeighbor4x4Context |
|
|
|
/// <param name="plane">The zero-based plane index.</param>
|
|
|
|
/// <param name="offset">The first context index to clear.</param>
|
|
|
|
/// <param name="length">The number of context entries to clear.</param>
|
|
|
|
internal void ClearContext(int plane, int offset, int length) |
|
|
|
=> Array.Fill(this.leftContext[plane], 0, offset, length); |
|
|
|
public void ClearContext(int plane, int offset, int length) |
|
|
|
=> this.GetContext(plane).Slice(offset, length).Clear(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the coefficient context column for the specified plane.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="plane">The zero-based plane index.</param>
|
|
|
|
/// <returns>The coefficient contexts for the plane.</returns>
|
|
|
|
internal int[] GetContext(int plane) => this.leftContext[plane]; |
|
|
|
public Span<int> GetContext(int plane) => this.GetRegion(PlaneContextRegionStart + plane); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets one logical column from the contiguous left-neighbor allocation.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="regionIndex">The zero-based logical region index.</param>
|
|
|
|
/// <returns>The requested context column.</returns>
|
|
|
|
private Span<int> GetRegion(int regionIndex) |
|
|
|
{ |
|
|
|
ObjectDisposedException.ThrowIf(this.memory is null, this); |
|
|
|
return this.memory.Memory.Span.Slice(regionIndex * this.contextLength, this.contextLength); |
|
|
|
} |
|
|
|
} |
|
|
|
|