diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
index a269c01071..34fe80da3b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@@ -89,6 +90,24 @@ internal static class Av1BlockSizeExtensions
return SubSampled[(int)blockSize][subX ? 1 : 0][subY ? 1 : 0];
}
+ public static Av1TransformSize GetMaxUvTransformSize(this Av1BlockSize blockSize, bool subX, bool subY)
+ {
+ Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY);
+ Av1TransformSize uvTransformSize = Av1TransformSize.Invalid;
+ if (planeBlockSize < Av1BlockSize.SizeS)
+ {
+ uvTransformSize = planeBlockSize.GetMaximumTransformSize();
+ }
+
+ return uvTransformSize switch
+ {
+ Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64 => Av1TransformSize.Size32x32,
+ Av1TransformSize.Size64x16 => Av1TransformSize.Size32x16,
+ Av1TransformSize.Size16x64 => Av1TransformSize.Size16x32,
+ _ => uvTransformSize,
+ };
+ }
+
///
/// Returns the largest transform size that can be used for blocks of given size.
/// The can be either a square or rectangular block.
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
index 92810728a3..6fd6c536ac 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
@@ -133,4 +133,9 @@ internal static class Av1Constants
/// Number of values for palette_size.
///
public const int PaletteMaxSize = 8;
+
+ ///
+ /// Maximum transform size categories.
+ ///
+ public const int MaxTransformCategories = 4;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
index b8b8f8bb47..a78df4428e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
@@ -13,9 +13,11 @@ internal class Av1BlockModeInfo
{
this.BlockSize = blockSize;
this.PositionInSuperblock = position;
- this.AngleDelta = new int[numPlanes];
+ this.AngleDelta = new int[numPlanes - 1];
this.paletteSize = new int[numPlanes - 1];
this.FilterIntraModeInfo = new();
+ this.FirstTransformLocation = new int[numPlanes - 1];
+ this.TusCount = new int[numPlanes - 1];
}
public Av1BlockSize BlockSize { get; }
@@ -24,7 +26,7 @@ internal class Av1BlockModeInfo
public bool Skip { get; set; }
- public Av1PartitionType PartitionType { get; }
+ public Av1PartitionType PartitionType { get; set; }
public bool SkipMode { get; set; }
@@ -44,6 +46,10 @@ internal class Av1BlockModeInfo
public Av1IntraFilterModeInfo FilterIntraModeInfo { get; internal set; }
+ public int[] FirstTransformLocation { get; }
+
+ public int[] TusCount { get; internal set; }
+
public int GetPaletteSize(Av1PlaneType planeType) => this.paletteSize[(int)planeType];
public void SetPaletteSizes(int ySize, int uvSize) => this.paletteSize = [ySize, uvSize];
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs
index cb65bfafe4..fbd0d99856 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs
@@ -171,4 +171,12 @@ internal static class Av1DefaultDistributions
new(16384), new(16384), new(16384), new(16384), new(12770), new(10368),
new(20229), new(18101), new(16384), new(16384)
];
+
+ public static Av1Distribution[][] TransformSize =>
+ [
+ [new(19968), new(19968), new(24320)],
+ [new(12272, 30172), new(12272, 30172), new(18677, 30848)],
+ [new(12986, 15180), new(12986, 15180), new(24302, 25602)],
+ [new(5782, 11475), new(5782, 11475), new(16803, 22759)],
+ ];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs
index db8b6ba18f..03e7100606 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs
@@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
@@ -50,6 +51,7 @@ internal class Av1FrameBuffer
// Initialize the arrays.
int i = 0;
int j = 0;
+ int k = 0;
for (int y = 0; y < this.superblockRowCount; y++)
{
for (int x = 0; x < this.superblockColumnCount; x++)
@@ -58,11 +60,16 @@ internal class Av1FrameBuffer
this.superblockInfos[i] = new(this, point);
for (int u = 0; u < this.modeInfoSizePerSuperblock; u++)
{
+ this.transformInfosY[j] = new Av1TransformInfo();
+ this.transformInfosY[j << 1] = new Av1TransformInfo();
+ this.transformInfosY[(j << 1) + 1] = new Av1TransformInfo();
for (int v = 0; v < this.modeInfoSizePerSuperblock; v++)
{
- this.modeInfos[j] = new Av1BlockModeInfo(numPlanes, Av1BlockSize.Block4x4, new Point(u, v));
- j++;
+ this.modeInfos[k] = new Av1BlockModeInfo(numPlanes, Av1BlockSize.Block4x4, new Point(u, v));
+ k++;
}
+
+ j++;
}
i++;
@@ -87,6 +94,8 @@ internal class Av1FrameBuffer
this.deltaLoopFilter = new int[superblockCount << this.deltaLoopFactorLog2];
}
+ public int ModeInfoCount => this.modeInfos.Length;
+
public Av1SuperblockInfo GetSuperblock(Point index)
{
Span span = this.superblockInfos;
@@ -109,19 +118,35 @@ internal class Av1FrameBuffer
return span[(superblock * this.modeInfoCountPerSuperblock) + modeInfo];
}
- public Av1TransformInfo GetTransformY(Point index)
+ public ref Av1TransformInfo GetTransformY(int index)
+ {
+ Span span = this.transformInfosY;
+ return ref span[index];
+ }
+
+ public void SetTransformY(int index, Av1TransformInfo transformInfo)
+ {
+ Span span = this.transformInfosY;
+ span[index] = transformInfo;
+ }
+
+ public ref Av1TransformInfo GetTransformY(Point index)
{
Span span = this.transformInfosY;
int i = (index.Y * this.superblockColumnCount) + index.X;
- return span[i * this.modeInfoCountPerSuperblock];
+ return ref span[i * this.modeInfoCountPerSuperblock];
+ }
+
+ public ref Av1TransformInfo GetTransformUv(int index)
+ {
+ Span span = this.transformInfosUv;
+ return ref span[index];
}
- public void GetTransformUv(Point index, out Av1TransformInfo transformU, out Av1TransformInfo transformV)
+ public void SetTransformUv(int index, Av1TransformInfo transformInfo)
{
Span span = this.transformInfosUv;
- int i = 2 * ((index.Y * this.superblockColumnCount) + index.X) * this.modeInfoCountPerSuperblock;
- transformU = span[i];
- transformV = span[i + 1];
+ span[index] = transformInfo;
}
public Span GetCoefficientsY(Point index)
@@ -175,5 +200,5 @@ internal class Av1FrameBuffer
return span.Slice(i, 1 << this.deltaLoopFactorLog2);
}
- internal void ClearDeltaLoopFilter() => Array.Fill(this.deltaLoopFilter, 0);
+ public void ClearDeltaLoopFilter() => Array.Fill(this.deltaLoopFilter, 0);
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
index c883d618bf..2efbde6e4c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
@@ -2,7 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
-using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
@@ -44,6 +44,8 @@ internal class Av1ParseAboveNeighbor4x4Context
public int[] AbovePartitionWidth => this.abovePartitionWidth;
+ public int[] AboveTransformWidth => this.aboveTransformWidth;
+
public void Clear(ObuSequenceHeader sequenceHeader)
{
int planeCount = sequenceHeader.ColorConfig.ChannelCount;
@@ -70,6 +72,22 @@ internal class Av1ParseAboveNeighbor4x4Context
}
}
+ public void UpdateTransformation(Point modeInfoLocation, Av1TileInfo tileInfo, Av1TransformSize transformSize, Av1BlockSize blockSize, bool skip)
+ {
+ int startIndex = modeInfoLocation.X - tileInfo.ModeInfoColumnStart;
+ int transformWidth = transformSize.GetWidth();
+ int n4w = blockSize.Get4x4WideCount();
+ if (skip)
+ {
+ transformWidth = n4w * (1 << Av1Constants.ModeInfoSizeLog2);
+ }
+
+ for (int i = 0; i < n4w; i++)
+ {
+ this.aboveTransformWidth[startIndex + i] = transformWidth;
+ }
+ }
+
internal void ClearContext(int plane, int offset, int length)
=> Array.Fill(this.aboveContext[plane], 0, offset, length);
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
index 9030ea6012..18ea5b5f7f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
@@ -2,7 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
-using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
@@ -45,6 +45,8 @@ internal class Av1ParseLeftNeighbor4x4Context
public int[] LeftPartitionHeight => this.leftPartitionHeight;
+ public int[] LeftTransformHeight => this.leftTransformHeight;
+
public void Clear(ObuSequenceHeader sequenceHeader)
{
int planeCount = sequenceHeader.ColorConfig.ChannelCount;
@@ -71,6 +73,22 @@ internal class Av1ParseLeftNeighbor4x4Context
}
}
+ public void UpdateTransformation(Point modeInfoLocation, Av1SuperblockInfo superblockInfo, Av1TransformSize transformSize, Av1BlockSize blockSize, bool skip)
+ {
+ int startIndex = modeInfoLocation.Y - superblockInfo.Position.Y;
+ int transformHeight = transformSize.GetHeight();
+ int n4h = blockSize.Get4x4HighCount();
+ if (skip)
+ {
+ transformHeight = n4h * (1 << Av1Constants.ModeInfoSizeLog2);
+ }
+
+ for (int i = 0; i < n4h; i++)
+ {
+ this.leftTransformHeight[startIndex + i] = transformHeight;
+ }
+ }
+
internal void ClearContext(int plane, int offset, int length)
=> Array.Fill(this.leftContext[plane], 0, offset, length);
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
index d6dc9ffa65..cfacff6d10 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
@@ -1,10 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
+
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1PartitionInfo
{
+ private int modeBlockToLeftEdge;
+ private int modeBlockToRightEdge;
+ private int modeBlockToTopEdge;
+ private int modeBlockToBottomEdge;
+
public Av1PartitionInfo(Av1BlockModeInfo modeInfo, Av1SuperblockInfo superblockInfo, bool isChroma, Av1PartitionType partitionType)
{
this.ModeInfo = modeInfo;
@@ -42,4 +49,43 @@ internal class Av1PartitionInfo
public int[][] CdefStrength { get; set; }
public int[] ReferenceFrame { get; set; }
+
+ public void ComputeBoundaryOffsets(ObuFrameHeader frameInfo, Av1TileInfo tileInfo)
+ {
+ Av1BlockSize blockSize = this.ModeInfo.BlockSize;
+ int bw4 = blockSize.Get4x4WideCount();
+ int bh4 = blockSize.Get4x4HighCount();
+ this.AvailableUp = this.RowIndex > tileInfo.ModeInfoRowStart;
+ this.AvailableLeft = this.ColumnIndex > tileInfo.ModeInfoColumnStart;
+ this.AvailableUpForChroma = this.AvailableUp;
+ this.AvailableLeftForChroma = this.AvailableLeft;
+ this.modeBlockToLeftEdge = -(this.ColumnIndex << Av1Constants.ModeInfoSizeLog2) << 3;
+ this.modeBlockToRightEdge = ((frameInfo.ModeInfoColumnCount - bw4 - this.ColumnIndex) << Av1Constants.ModeInfoSizeLog2) << 3;
+ this.modeBlockToTopEdge = -(this.RowIndex << Av1Constants.ModeInfoSizeLog2) << 3;
+ this.modeBlockToBottomEdge = ((frameInfo.ModeInfoRowCount - bh4 - this.RowIndex) << Av1Constants.ModeInfoSizeLog2) << 3;
+ }
+
+ public int GetMaxBlockWide(Av1BlockSize blockSize, bool subX)
+ {
+ int maxBlockWide = blockSize.GetWidth();
+ if (this.modeBlockToRightEdge < 0)
+ {
+ int shift = subX ? 4 : 3;
+ maxBlockWide += this.modeBlockToRightEdge >> shift;
+ }
+
+ return maxBlockWide >> 2;
+ }
+
+ public int GetMaxBlockHigh(Av1BlockSize blockSize, bool subY)
+ {
+ int maxBlockHigh = blockSize.GetHeight();
+ if (this.modeBlockToBottomEdge < 0)
+ {
+ int shift = subY ? 4 : 3;
+ maxBlockHigh += this.modeBlockToBottomEdge >> shift;
+ }
+
+ return maxBlockHigh >> 2;
+ }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs
index 3d76994391..7d6eb4998f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs
@@ -1,7 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Drawing;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
@@ -20,6 +22,7 @@ internal ref struct Av1SymbolDecoder
private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta;
private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode;
private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra;
+ private readonly Av1Distribution[][] transformSize = Av1DefaultDistributions.TransformSize;
private Av1SymbolReader reader;
public Av1SymbolDecoder(Span tileData) => this.reader = new Av1SymbolReader(tileData);
@@ -149,6 +152,30 @@ internal ref struct Av1SymbolDecoder
return (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
}
+ public Av1TransformSize ReadTransformSize(Av1BlockSize blockSize, int context)
+ {
+ ref Av1SymbolReader r = ref this.reader;
+ Av1TransformSize maxTransformSize = blockSize.GetMaximumTransformSize();
+ int depth = 0;
+ while (maxTransformSize != Av1TransformSize.Size4x4)
+ {
+ depth++;
+ maxTransformSize = maxTransformSize.GetSubSize();
+ DebugGuard.MustBeLessThan(depth, 10, nameof(depth));
+ }
+
+ DebugGuard.MustBeLessThanOrEqualTo(depth, Av1Constants.MaxTransformCategories, nameof(depth));
+ int category = depth - 1;
+ int value = r.ReadSymbol(this.transformSize[category][context]);
+ Av1TransformSize transformSize = blockSize.GetMaximumTransformSize();
+ for (int d = 0; d < value; ++d)
+ {
+ transformSize = transformSize.GetSubSize();
+ }
+
+ return transformSize;
+ }
+
private static uint GetElementProbability(Av1Distribution probability, Av1PartitionType element)
=> probability[(int)element - 1] - probability[(int)element];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
index ea0af6ce9b..c286b34b6a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
@@ -2,10 +2,13 @@
// Licensed under the Six Labors Split License.
using System.Data.Common;
+using System.Drawing;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using static SixLabors.ImageSharp.PixelFormats.Utils.Vector4Converters;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
@@ -31,6 +34,8 @@ internal class Av1TileDecoder : IAv1TileDecoder
private int maxLumaHeight;
private int deltaLoopFilterResolution = -1;
private bool readDeltas;
+ private int[][] tusCount = [];
+ private int[] firstTransformOffset = new int[2];
public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
@@ -55,6 +60,10 @@ internal class Av1TileDecoder : IAv1TileDecoder
modeInfoWideColumnCount = AlignPowerOfTwo(modeInfoWideColumnCount, sequenceHeader.SuperBlockSizeLog2 - 2);
this.aboveNeighborContext = new Av1ParseAboveNeighbor4x4Context(planesCount, modeInfoWideColumnCount);
this.leftNeighborContext = new Av1ParseLeftNeighbor4x4Context(planesCount, sequenceHeader.ModeInfoSize);
+ this.tusCount = new int[Av1Constants.MaxPlanes][];
+ this.tusCount[0] = new int[this.FrameBuffer.ModeInfoCount];
+ this.tusCount[1] = new int[this.FrameBuffer.ModeInfoCount];
+ this.tusCount[2] = new int[this.FrameBuffer.ModeInfoCount];
}
public ObuFrameHeader FrameInfo { get; }
@@ -71,7 +80,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.aboveNeighborContext.Clear(this.SequenceHeader);
this.ClearLoopFilterDelta();
- int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
+ int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
// Default initialization of Wiener and SGR Filter.
this.referenceSgrXqd = new int[planesCount][];
@@ -102,9 +111,9 @@ internal class Av1TileDecoder : IAv1TileDecoder
Point superblockPosition = new(superBlockColumn, superBlockRow);
Av1SuperblockInfo superblockInfo = this.FrameBuffer.GetSuperblock(superblockPosition);
- // this.ClearBlockDecodedFlags(row, column, superBlock4x4Size);
- this.FrameBuffer.ClearCdef(superblockPosition);
+ // this.ClearBlockDecodedFlags(modeInfoLocation, superBlock4x4Size);
Point modeInfoLocation = new(column, row);
+ this.FrameBuffer.ClearCdef(superblockPosition);
this.ReadLoopRestoration(modeInfoLocation, superBlockSize);
this.ParsePartition(ref reader, modeInfoLocation, superBlockSize, superblockInfo, tileInfo);
}
@@ -119,7 +128,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
///
/// 5.11.3. Clear block decoded flags function.
///
- private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size)
+ private void ClearBlockDecodedFlags(Point modeInfoLocation, int superBlock4x4Size)
{
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
this.blockDecoded = new bool[planesCount][][];
@@ -127,12 +136,12 @@ internal class Av1TileDecoder : IAv1TileDecoder
{
int subX = plane > 0 && this.SequenceHeader.ColorConfig.SubSamplingX ? 1 : 0;
int subY = plane > 0 && this.SequenceHeader.ColorConfig.SubSamplingY ? 1 : 0;
- int superBlock4x4Width = (this.FrameInfo.ModeInfoColumnCount - column) >> subX;
- int superBlock4x4Height = (this.FrameInfo.ModeInfoRowCount - row) >> subY;
- this.blockDecoded[plane] = new bool[superBlock4x4Size >> subY][];
+ int superBlock4x4Width = (this.FrameInfo.ModeInfoColumnCount - modeInfoLocation.X) >> subX;
+ int superBlock4x4Height = (this.FrameInfo.ModeInfoRowCount - modeInfoLocation.Y) >> subY;
+ this.blockDecoded[plane] = new bool[(superBlock4x4Size >> subY) + 3][];
for (int y = -1; y <= superBlock4x4Size >> subY; y++)
{
- this.blockDecoded[plane][y] = new bool[superBlock4x4Size >> subX];
+ this.blockDecoded[plane][y] = new bool[(superBlock4x4Size >> subX) + 3];
for (int x = -1; x <= superBlock4x4Size >> subX; x++)
{
if (y < 0 && x < superBlock4x4Width)
@@ -194,7 +203,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
bool availableLeft = this.IsInside(rowIndex, columnIndex - 1);
int block4x4Size = blockSize.Get4x4WideCount();
int halfBlock4x4Size = block4x4Size >> 1;
- int quarterBlock4x4Size = halfBlock4x4Size >> 2;
+ int quarterBlock4x4Size = halfBlock4x4Size >> 1;
bool hasRows = (modeInfoLocation.Y + halfBlock4x4Size) < this.FrameInfo.ModeInfoRowCount;
bool hasColumns = (modeInfoLocation.X + halfBlock4x4Size) < this.FrameInfo.ModeInfoColumnCount;
Av1PartitionType partitionType = Av1PartitionType.Split;
@@ -234,53 +243,53 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.ParsePartition(ref reader, loc3, subSize, superblockInfo, tileInfo);
break;
case Av1PartitionType.None:
- this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, Av1PartitionType.None);
+ this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.None);
break;
case Av1PartitionType.Horizontal:
- this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, Av1PartitionType.Horizontal);
+ this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Horizontal);
if (hasRows)
{
Point halfLocation = new(columnIndex, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, halfLocation, subSize, superblockInfo, Av1PartitionType.Horizontal);
+ this.ParseBlock(ref reader, halfLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Horizontal);
}
break;
case Av1PartitionType.Vertical:
- this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, Av1PartitionType.Vertical);
+ this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Vertical);
if (hasRows)
{
Point halfLocation = new(columnIndex + halfBlock4x4Size, rowIndex);
- this.ParseBlock(ref reader, halfLocation, subSize, superblockInfo, Av1PartitionType.Vertical);
+ this.ParseBlock(ref reader, halfLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Vertical);
}
break;
case Av1PartitionType.HorizontalA:
- this.ParseBlock(ref reader, modeInfoLocation, splitSize, superblockInfo, Av1PartitionType.HorizontalA);
+ this.ParseBlock(ref reader, modeInfoLocation, splitSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalA);
Point locHorA1 = new(columnIndex + halfBlock4x4Size, rowIndex);
- this.ParseBlock(ref reader, locHorA1, splitSize, superblockInfo, Av1PartitionType.HorizontalA);
+ this.ParseBlock(ref reader, locHorA1, splitSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalA);
Point locHorA2 = new(columnIndex, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, locHorA2, subSize, superblockInfo, Av1PartitionType.HorizontalA);
+ this.ParseBlock(ref reader, locHorA2, subSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalA);
break;
case Av1PartitionType.HorizontalB:
- this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, Av1PartitionType.HorizontalB);
+ this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalB);
Point locHorB1 = new(columnIndex + halfBlock4x4Size, rowIndex);
- this.ParseBlock(ref reader, locHorB1, splitSize, superblockInfo, Av1PartitionType.HorizontalB);
+ this.ParseBlock(ref reader, locHorB1, splitSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalB);
Point locHorB2 = new(columnIndex + halfBlock4x4Size, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, locHorB2, splitSize, superblockInfo, Av1PartitionType.HorizontalB);
+ this.ParseBlock(ref reader, locHorB2, splitSize, superblockInfo, tileInfo, Av1PartitionType.HorizontalB);
break;
case Av1PartitionType.VerticalA:
- this.ParseBlock(ref reader, modeInfoLocation, splitSize, superblockInfo, Av1PartitionType.VerticalA);
+ this.ParseBlock(ref reader, modeInfoLocation, splitSize, superblockInfo, tileInfo, Av1PartitionType.VerticalA);
Point locVertA1 = new(columnIndex, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, locVertA1, splitSize, superblockInfo, Av1PartitionType.VerticalA);
+ this.ParseBlock(ref reader, locVertA1, splitSize, superblockInfo, tileInfo, Av1PartitionType.VerticalA);
Point locVertA2 = new(columnIndex + halfBlock4x4Size, rowIndex);
- this.ParseBlock(ref reader, locVertA2, subSize, superblockInfo, Av1PartitionType.VerticalA);
+ this.ParseBlock(ref reader, locVertA2, subSize, superblockInfo, tileInfo, Av1PartitionType.VerticalA);
break;
case Av1PartitionType.VerticalB:
- this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, Av1PartitionType.VerticalB);
+ this.ParseBlock(ref reader, modeInfoLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.VerticalB);
Point locVertB1 = new(columnIndex + halfBlock4x4Size, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, locVertB1, splitSize, superblockInfo, Av1PartitionType.VerticalB);
+ this.ParseBlock(ref reader, locVertB1, splitSize, superblockInfo, tileInfo, Av1PartitionType.VerticalB);
Point locVertB2 = new(columnIndex + halfBlock4x4Size, rowIndex + halfBlock4x4Size);
- this.ParseBlock(ref reader, locVertB2, splitSize, superblockInfo, Av1PartitionType.VerticalB);
+ this.ParseBlock(ref reader, locVertB2, splitSize, superblockInfo, tileInfo, Av1PartitionType.VerticalB);
break;
case Av1PartitionType.Horizontal4:
for (int i = 0; i < 4; i++)
@@ -292,7 +301,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
Point currentLocation = new(modeInfoLocation.X, currentBlockRow);
- this.ParseBlock(ref reader, currentLocation, subSize, superblockInfo, Av1PartitionType.Horizontal4);
+ this.ParseBlock(ref reader, currentLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Horizontal4);
}
break;
@@ -306,7 +315,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
Point currentLocation = new(currentBlockColumn, modeInfoLocation.Y);
- this.ParseBlock(ref reader, currentLocation, subSize, superblockInfo, Av1PartitionType.Vertical4);
+ this.ParseBlock(ref reader, currentLocation, subSize, superblockInfo, tileInfo, Av1PartitionType.Vertical4);
}
break;
@@ -317,7 +326,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.UpdatePartitionContext(new Point(columnIndex, rowIndex), tileInfo, superblockInfo, subSize, blockSize, partitionType);
}
- private void ParseBlock(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1PartitionType partitionType)
+ private void ParseBlock(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo, Av1PartitionType partitionType)
{
int rowIndex = modeInfoLocation.Y;
int columnIndex = modeInfoLocation.X;
@@ -325,14 +334,12 @@ internal class Av1TileDecoder : IAv1TileDecoder
int block4x4Height = blockSize.Get4x4HighCount();
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
Av1BlockModeInfo blockModeInfo = superblockInfo.GetModeInfo(modeInfoLocation);
+ blockModeInfo.PartitionType = partitionType;
bool hasChroma = this.HasChroma(rowIndex, columnIndex, blockSize);
Av1PartitionInfo partitionInfo = new(blockModeInfo, superblockInfo, hasChroma, partitionType);
partitionInfo.ColumnIndex = columnIndex;
partitionInfo.RowIndex = rowIndex;
- partitionInfo.AvailableUp = this.IsInside(rowIndex - 1, columnIndex);
- partitionInfo.AvailableLeft = this.IsInside(rowIndex, columnIndex - 1);
- partitionInfo.AvailableUpForChroma = partitionInfo.AvailableUp;
- partitionInfo.AvailableLeftForChroma = partitionInfo.AvailableLeft;
+ partitionInfo.ComputeBoundaryOffsets(this.FrameInfo, tileInfo);
if (hasChroma)
{
if (this.SequenceHeader.ColorConfig.SubSamplingY && block4x4Height == 1)
@@ -358,7 +365,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.ReadModeInfo(ref reader, partitionInfo);
ReadPaletteTokens(ref reader, partitionInfo);
- ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
+ this.ReadBlockTransformSize(ref reader, modeInfoLocation, partitionInfo, superblockInfo, tileInfo);
if (partitionInfo.ModeInfo.Skip)
{
this.ResetSkipContext(partitionInfo);
@@ -571,22 +578,171 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
///
- /// 5.11.16. Block TX size syntax.
+ /// 5.11.15. TX size syntax.
///
- private static void ReadBlockTransformSize(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
+ private Av1TransformSize ReadTransformSize(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo, bool allowSelect)
+ {
+ Av1BlockModeInfo modeInfo = partitionInfo.ModeInfo;
+ if (this.FrameInfo.LosslessArray[modeInfo.SegmentId])
+ {
+ return Av1TransformSize.Size4x4;
+ }
+
+ if (modeInfo.BlockSize > Av1BlockSize.Block4x4 && allowSelect && this.FrameInfo.TransformMode == Transform.Av1TransformMode.Select)
+ {
+ return this.ReadSelectedTransformSize(ref reader, partitionInfo, superblockInfo, tileInfo);
+ }
+
+ return modeInfo.BlockSize.GetMaximumTransformSize();
+ }
+
+ private Av1TransformSize ReadSelectedTransformSize(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
+ int context = 0;
+ Av1TransformSize maxTransformSize = partitionInfo.ModeInfo.BlockSize.GetMaximumTransformSize();
+ int aboveWidth = this.aboveNeighborContext.AboveTransformWidth[partitionInfo.ColumnIndex - tileInfo.ModeInfoColumnStart];
+ int above = (aboveWidth >= maxTransformSize.GetWidth()) ? 1 : 0;
+ int leftHeight = this.leftNeighborContext.LeftTransformHeight[partitionInfo.RowIndex - superblockInfo.Position.Y];
+ int left = (leftHeight >= maxTransformSize.GetHeight()) ? 1 : 0;
+ bool hasAbove = partitionInfo.AvailableUp;
+ bool hasLeft = partitionInfo.AvailableLeft;
+
+ if (hasAbove && hasLeft)
+ {
+ context = above + left;
+ }
+ else if (hasAbove)
+ {
+ context = above;
+ }
+ else if (hasLeft)
+ {
+ context = left;
+ }
+ else
+ {
+ context = 0;
+ }
+
+ return reader.ReadTransformSize(partitionInfo.ModeInfo.BlockSize, context);
+ }
+
+ ///
+ /// Section 5.11.16. Block TX size syntax.
+ ///
+ private void ReadBlockTransformSize(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
+ {
+ Av1BlockSize blockSize = partitionInfo.ModeInfo.BlockSize;
int block4x4Width = blockSize.Get4x4WideCount();
int block4x4Height = blockSize.Get4x4HighCount();
// First condition in spec is for INTER frames, implemented only the INTRA condition.
- ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
- /*for (int row = rowIndex; row < rowIndex + block4x4Height; row++)
+ bool allowSelect = !partitionInfo.ModeInfo.Skip || true;
+ Av1TransformSize transformSize = this.ReadTransformSize(ref reader, partitionInfo, superblockInfo, tileInfo, allowSelect);
+ this.aboveNeighborContext.UpdateTransformation(modeInfoLocation, tileInfo, transformSize, blockSize, false);
+ this.leftNeighborContext.UpdateTransformation(modeInfoLocation, superblockInfo, transformSize, blockSize, false);
+ this.UpdateTransformInfo(partitionInfo, blockSize, transformSize);
+ }
+
+ private unsafe void UpdateTransformInfo(Av1PartitionInfo partitionInfo, Av1BlockSize blockSize, Av1TransformSize transformSize)
+ {
+ int transformInfoYIndex = partitionInfo.ModeInfo.FirstTransformLocation[(int)Av1PlaneType.Y];
+ int transformInfoUvIndex = partitionInfo.ModeInfo.FirstTransformLocation[(int)Av1PlaneType.Uv];
+ Av1TransformInfo lumaTransformInfo = this.FrameBuffer.GetTransformY(transformInfoYIndex);
+ Av1TransformInfo chromaTransformInfo = this.FrameBuffer.GetTransformUv(transformInfoUvIndex);
+ int totalLumaTusCount = 0;
+ int totalChromaTusCount = 0;
+ int forceSplitCount = 0;
+ bool subX = this.SequenceHeader.ColorConfig.SubSamplingX;
+ bool subY = this.SequenceHeader.ColorConfig.SubSamplingY;
+ int maxBlockWide = partitionInfo.GetMaxBlockWide(blockSize, false);
+ int maxBlockHigh = partitionInfo.GetMaxBlockHigh(blockSize, false);
+ int width = 64 >> 2;
+ int height = 64 >> 2;
+ width = Math.Min(width, maxBlockWide);
+ height = Math.Min(height, maxBlockHigh);
+
+ bool isLossLess = this.FrameInfo.LosslessArray[partitionInfo.ModeInfo.SegmentId];
+ Av1TransformSize transformSizeUv = isLossLess ? Av1TransformSize.Size4x4 : blockSize.GetMaxUvTransformSize(subX, subY);
+
+ for (int idy = 0; idy < maxBlockHigh; idy += height)
{
- for (int column = columnIndex; column < columnIndex + block4x4Width; column++)
+ for (int idx = 0; idx < maxBlockWide; idx += width, forceSplitCount++)
{
- this.InterTransformSizes[row][column] = this.TransformSize;
+ int lumaTusCount = 0;
+ int chromaTusCount = 0;
+
+ // Update Luminance Transform Info.
+ int stepColumn = transformSize.Get4x4WideCount();
+ int stepRow = transformSize.Get4x4HighCount();
+
+ int unitHeight = Av1Math.Round2(Math.Min(height + idy, maxBlockHigh), 0);
+ int unitWidth = Av1Math.Round2(Math.Min(width + idx, maxBlockWide), 0);
+ for (int blockRow = idy; blockRow < unitHeight; blockRow += stepRow)
+ {
+ for (int blockColumn = idx; blockColumn < unitWidth; blockColumn += stepColumn)
+ {
+ this.FrameBuffer.SetTransformY(transformInfoYIndex, new Av1TransformInfo
+ {
+ TransformSize = transformSize,
+ OffsetX = blockColumn,
+ OffsetY = blockRow
+ });
+ transformInfoYIndex++;
+ lumaTusCount++;
+ totalLumaTusCount++;
+ }
+ }
+
+ this.tusCount[(int)Av1Plane.Y][forceSplitCount] = lumaTusCount;
+
+ if (this.SequenceHeader.ColorConfig.IsMonochrome || !partitionInfo.IsChroma)
+ {
+ continue;
+ }
+
+ // Update Chroma Transform Info.
+ stepColumn = transformSizeUv.Get4x4WideCount();
+ stepRow = transformSizeUv.Get4x4HighCount();
+
+ unitHeight = Av1Math.Round2(Math.Min(height + idx, maxBlockHigh), subY ? 1 : 0);
+ unitWidth = Av1Math.Round2(Math.Min(width + idx, maxBlockWide), subX ? 1 : 0);
+ for (int blockRow = idy; blockRow < unitHeight; blockRow += stepRow)
+ {
+ for (int blockColumn = idx; blockColumn < unitWidth; blockColumn += stepColumn)
+ {
+ this.FrameBuffer.SetTransformUv(transformInfoUvIndex, new Av1TransformInfo
+ {
+ TransformSize = transformSizeUv,
+ OffsetX = blockColumn,
+ OffsetY = blockRow
+ });
+ transformInfoUvIndex++;
+ chromaTusCount++;
+ totalChromaTusCount++;
+ }
+ }
+
+ this.tusCount[(int)Av1Plane.U][forceSplitCount] = lumaTusCount;
+ this.tusCount[(int)Av1Plane.V][forceSplitCount] = lumaTusCount;
}
- }*/
+ }
+
+ // Cr Transform Info Update from Cb.
+ if (totalChromaTusCount != 0)
+ {
+ int originalIndex = transformInfoUvIndex - totalChromaTusCount;
+ for (int i = 0; i < totalChromaTusCount; i++)
+ {
+ this.FrameBuffer.SetTransformUv(transformInfoUvIndex + i, this.FrameBuffer.GetTransformUv(originalIndex + i));
+ }
+ }
+
+ partitionInfo.ModeInfo.TusCount[(int)Av1PlaneType.Y] = totalLumaTusCount;
+ partitionInfo.ModeInfo.TusCount[(int)Av1PlaneType.Uv] = totalChromaTusCount;
+
+ this.firstTransformOffset[(int)Av1PlaneType.Y] += totalLumaTusCount;
+ this.firstTransformOffset[(int)Av1PlaneType.Uv] += totalChromaTusCount;
}
///
@@ -594,16 +750,15 @@ internal class Av1TileDecoder : IAv1TileDecoder
///
private static void ReadPaletteTokens(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
- reader.ReadLiteral(-1);
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) != 0)
{
- // Todo: Implement.
+ // TODO: Implement.
throw new NotImplementedException();
}
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0)
{
- // Todo: Implement.
+ // TODO: Implement.
throw new NotImplementedException();
}
}
@@ -636,8 +791,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.ReadCdef(ref reader, partitionInfo);
- bool readDeltas = false;
- if (readDeltas)
+ if (this.readDeltas)
{
this.ReadDeltaQuantizerIndex(ref reader, partitionInfo);
this.ReadDeltaLoopFilter(ref reader, partitionInfo);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
index 910309f873..ce91a4916f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
@@ -1,8 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
+
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1TransformInfo
{
+ public Av1TransformInfo()
+ {
+ }
+
+ public Av1TransformInfo(Av1TransformInfo originalInfo)
+ {
+ this.TransformSize = originalInfo.TransformSize;
+ this.OffsetX = originalInfo.OffsetX;
+ this.OffsetY = originalInfo.OffsetY;
+ }
+
+ public Av1TransformSize TransformSize { get; internal set; }
+
+ public int OffsetX { get; internal set; }
+
+ public int OffsetY { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
index 267c55266d..e812778335 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-namespace SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
internal enum Av1TransformSize : byte
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs
index 8366a8e887..1fc36fd100 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs
@@ -1,12 +1,40 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-namespace SixLabors.ImageSharp.Formats.Heif.Av1.Quantization;
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
internal static class Av1TransformSizeExtensions
{
private static readonly int[] Size2d = [
- 16, 64, 256, 1024, 4096, 32, 32, 128, 128, 512, 512, 2048, 2048, 64, 64, 256, 256, 1024, 1024];
+ 16, 64, 256, 1024, 4096, 32, 32, 128, 128, 512, 512, 2048, 2048, 64, 64, 256, 256, 1024, 1024];
+
+ private static readonly Av1TransformSize[] SubTransformSize = [
+ Av1TransformSize.Size4x4, // TX_4X4
+ Av1TransformSize.Size4x4, // TX_8X8
+ Av1TransformSize.Size8x8, // TX_16X16
+ Av1TransformSize.Size16x16, // TX_32X32
+ Av1TransformSize.Size32x32, // TX_64X64
+ Av1TransformSize.Size4x4, // TX_4X8
+ Av1TransformSize.Size4x4, // TX_8X4
+ Av1TransformSize.Size8x8, // TX_8X16
+ Av1TransformSize.Size8x8, // TX_16X8
+ Av1TransformSize.Size16x16, // TX_16X32
+ Av1TransformSize.Size16x16, // TX_32X16
+ Av1TransformSize.Size32x32, // TX_32X64
+ Av1TransformSize.Size32x32, // TX_64X32
+ Av1TransformSize.Size4x8, // TX_4X16
+ Av1TransformSize.Size8x4, // TX_16X4
+ Av1TransformSize.Size8x16, // TX_8X32
+ Av1TransformSize.Size16x8, // TX_32X8
+ Av1TransformSize.Size16x32, // TX_16X64
+ Av1TransformSize.Size32x16, // TX_64X16
+ ];
+
+ // Transform block width in units.
+ private static readonly int[] WideUnit = [1, 2, 4, 8, 16, 1, 2, 2, 4, 4, 8, 8, 16, 1, 4, 2, 8, 4, 16];
+
+ // Transform block height in unit
+ private static readonly int[] HighUnit = [1, 2, 4, 8, 16, 2, 1, 4, 2, 8, 4, 16, 8, 4, 1, 8, 2, 16, 4];
public static int GetScale(this Av1TransformSize size)
{
@@ -21,4 +49,10 @@ internal static class Av1TransformSizeExtensions
public static int GetWidthLog2(this Av1TransformSize size) => (int)size;
public static int GetHeightLog2(this Av1TransformSize size) => (int)size;
+
+ public static int Get4x4WideCount(this Av1TransformSize size) => WideUnit[(int)size];
+
+ public static int Get4x4HighCount(this Av1TransformSize size) => HighUnit[(int)size];
+
+ public static Av1TransformSize GetSubSize(this Av1TransformSize size) => SubTransformSize[(int)size];
}