|
|
@ -5,12 +5,24 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Provides dimensions, chroma subsampling, and transform limits for AV1 block sizes.
|
|
|
|
|
|
/// </summary>
|
|
|
internal static class Av1BlockSizeExtensions |
|
|
internal static class Av1BlockSizeExtensions |
|
|
{ |
|
|
{ |
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The width of each block size in units of four samples.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly int[] SizeWide = [1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 1, 4, 2, 8, 4, 16]; |
|
|
private static readonly int[] SizeWide = [1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 1, 4, 2, 8, 4, 16]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The height of each block size in units of four samples.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly int[] SizeHigh = [1, 2, 1, 2, 4, 2, 4, 8, 4, 8, 16, 8, 16, 32, 16, 32, 4, 1, 8, 2, 16, 4]; |
|
|
private static readonly int[] SizeHigh = [1, 2, 1, 2, 4, 2, 4, 8, 4, 8, 16, 8, 16, 32, 16, 32, 4, 1, 8, 2, 16, 4]; |
|
|
|
|
|
|
|
|
// The Subsampled_Size table in the spec (Section 5.11.38. Get plane residual size function).
|
|
|
/// <summary>
|
|
|
|
|
|
/// Maps each luma block size and pair of chroma subsampling shifts to its residual-plane block size.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly Av1BlockSize[][][] SubSampled = |
|
|
private static readonly Av1BlockSize[][][] SubSampled = |
|
|
[ |
|
|
[ |
|
|
|
|
|
|
|
|
@ -40,6 +52,9 @@ internal static class Av1BlockSizeExtensions |
|
|
[[Av1BlockSize.Block64x16, Av1BlockSize.Invalid], [Av1BlockSize.Block32x16, Av1BlockSize.Block32x8]] |
|
|
[[Av1BlockSize.Block64x16, Av1BlockSize.Invalid], [Av1BlockSize.Block32x16, Av1BlockSize.Block32x8]] |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Maps each block size to its largest permitted transform size.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly Av1TransformSize[] MaxTransformSize = [ |
|
|
private static readonly Av1TransformSize[] MaxTransformSize = [ |
|
|
Av1TransformSize.Size4x4, Av1TransformSize.Size4x8, Av1TransformSize.Size8x4, Av1TransformSize.Size8x8, |
|
|
Av1TransformSize.Size4x4, Av1TransformSize.Size4x8, Av1TransformSize.Size8x4, Av1TransformSize.Size8x8, |
|
|
Av1TransformSize.Size8x16, Av1TransformSize.Size16x8, Av1TransformSize.Size16x16, Av1TransformSize.Size16x32, |
|
|
Av1TransformSize.Size8x16, Av1TransformSize.Size16x8, Av1TransformSize.Size16x16, Av1TransformSize.Size16x32, |
|
|
@ -49,9 +64,15 @@ internal static class Av1BlockSizeExtensions |
|
|
Av1TransformSize.Size16x64, Av1TransformSize.Size64x16 |
|
|
Av1TransformSize.Size16x64, Av1TransformSize.Size64x16 |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Contains the base-two logarithm of the sample count for each block size.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly int[] PelsLog2Count = |
|
|
private static readonly int[] PelsLog2Count = |
|
|
[4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 6, 6, 8, 8, 10, 10]; |
|
|
[4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 6, 6, 8, 8, 10, 10]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Maps geometry dimension logarithms to an AV1 block size using the mode-decision scan's transposed axis convention.
|
|
|
|
|
|
/// </summary>
|
|
|
private static readonly Av1BlockSize[][] HeightWidthToSize = [ |
|
|
private static readonly Av1BlockSize[][] HeightWidthToSize = [ |
|
|
[Av1BlockSize.Block4x4, Av1BlockSize.Block4x8, Av1BlockSize.Block4x16, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid], |
|
|
[Av1BlockSize.Block4x4, Av1BlockSize.Block4x8, Av1BlockSize.Block4x16, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid], |
|
|
[Av1BlockSize.Block8x4, Av1BlockSize.Block8x8, Av1BlockSize.Block8x16, Av1BlockSize.Block8x32, Av1BlockSize.Invalid, Av1BlockSize.Invalid], |
|
|
[Av1BlockSize.Block8x4, Av1BlockSize.Block8x8, Av1BlockSize.Block8x16, Av1BlockSize.Block8x32, Av1BlockSize.Invalid, Av1BlockSize.Invalid], |
|
|
@ -61,51 +82,81 @@ internal static class Av1BlockSizeExtensions |
|
|
[Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Block128x64, Av1BlockSize.Block128x128] |
|
|
[Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Invalid, Av1BlockSize.Block128x64, Av1BlockSize.Block128x128] |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the block width in units of four samples.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The number of four-sample columns.</returns>
|
|
|
public static int Get4x4WideCount(this Av1BlockSize blockSize) => SizeWide[(int)blockSize]; |
|
|
public static int Get4x4WideCount(this Av1BlockSize blockSize) => SizeWide[(int)blockSize]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the block height in units of four samples.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The number of four-sample rows.</returns>
|
|
|
public static int Get4x4HighCount(this Av1BlockSize blockSize) => SizeHigh[(int)blockSize]; |
|
|
public static int Get4x4HighCount(this Av1BlockSize blockSize) => SizeHigh[(int)blockSize]; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Gets the <see cref="Av1BlockSize"/> given by the Log2 of the width and height.
|
|
|
/// Gets the block size from mode-decision geometry dimension logarithms, where zero represents four samples.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="widthLog2">Log2 of the width value.</param>
|
|
|
/// <param name="widthLog2">The base-two width logarithm minus two.</param>
|
|
|
/// <param name="heightLog2">Log2 of the height value.</param>
|
|
|
/// <param name="heightLog2">The base-two height logarithm minus two.</param>
|
|
|
/// <returns>The <see cref="Av1BlockSize"/>.</returns>
|
|
|
/// <returns>The matching block size, or <see cref="Av1BlockSize.Invalid"/> for unsupported dimensions.</returns>
|
|
|
public static Av1BlockSize FromWidthAndHeight(uint widthLog2, uint heightLog2) => HeightWidthToSize[heightLog2][widthLog2]; |
|
|
public static Av1BlockSize FromWidthAndHeight(uint widthLog2, uint heightLog2) |
|
|
|
|
|
{ |
|
|
|
|
|
// Mode-decision geometry is ported with its source axis order, so its size lookup is indexed height first.
|
|
|
|
|
|
return HeightWidthToSize[heightLog2][widthLog2]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns the width of the block in samples.
|
|
|
/// Gets the block width in samples.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The block width in samples.</returns>
|
|
|
public static int GetWidth(this Av1BlockSize blockSize) |
|
|
public static int GetWidth(this Av1BlockSize blockSize) |
|
|
=> Get4x4WideCount(blockSize) << 2; |
|
|
=> Get4x4WideCount(blockSize) << 2; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns of the height of the block in 4 samples.
|
|
|
/// Gets the block height in samples.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The block height in samples.</returns>
|
|
|
public static int GetHeight(this Av1BlockSize blockSize) |
|
|
public static int GetHeight(this Av1BlockSize blockSize) |
|
|
=> Get4x4HighCount(blockSize) << 2; |
|
|
=> Get4x4HighCount(blockSize) << 2; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns base 2 logarithm of the width of the block in units of 4 samples.
|
|
|
/// Gets the base-two logarithm of the block width in units of four samples.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The base-two logarithm of the four-sample column count.</returns>
|
|
|
public static int Get4x4WidthLog2(this Av1BlockSize blockSize) |
|
|
public static int Get4x4WidthLog2(this Av1BlockSize blockSize) |
|
|
=> Av1Math.Log2(Get4x4WideCount(blockSize)); |
|
|
=> Av1Math.Log2(Get4x4WideCount(blockSize)); |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns base 2 logarithm of the height of the block in units of 4 samples.
|
|
|
/// Gets the base-two logarithm of the block height in units of four samples.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The base-two logarithm of the four-sample row count.</returns>
|
|
|
public static int Get4x4HeightLog2(this Av1BlockSize blockSize) |
|
|
public static int Get4x4HeightLog2(this Av1BlockSize blockSize) |
|
|
=> Av1Math.Log2(Get4x4HighCount(blockSize)); |
|
|
=> Av1Math.Log2(Get4x4HighCount(blockSize)); |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns the block size of a sub sampled block.
|
|
|
/// Gets the residual-plane block size for Boolean chroma subsampling flags.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The luma block size.</param>
|
|
|
|
|
|
/// <param name="subX">Indicates horizontal chroma subsampling.</param>
|
|
|
|
|
|
/// <param name="subY">Indicates vertical chroma subsampling.</param>
|
|
|
|
|
|
/// <returns>The corresponding residual-plane block size.</returns>
|
|
|
public static Av1BlockSize GetSubsampled(this Av1BlockSize blockSize, bool subX, bool subY) |
|
|
public static Av1BlockSize GetSubsampled(this Av1BlockSize blockSize, bool subX, bool subY) |
|
|
=> GetSubsampled(blockSize, subX ? 1 : 0, subY ? 1 : 0); |
|
|
=> GetSubsampled(blockSize, subX ? 1 : 0, subY ? 1 : 0); |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns the block size of a sub sampled block.
|
|
|
/// Gets the residual-plane block size for chroma subsampling shifts.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The luma block size.</param>
|
|
|
|
|
|
/// <param name="subX">The horizontal chroma subsampling shift.</param>
|
|
|
|
|
|
/// <param name="subY">The vertical chroma subsampling shift.</param>
|
|
|
|
|
|
/// <returns>The corresponding residual-plane block size, or <see cref="Av1BlockSize.Invalid"/> when unavailable.</returns>
|
|
|
public static Av1BlockSize GetSubsampled(this Av1BlockSize blockSize, int subX, int subY) |
|
|
public static Av1BlockSize GetSubsampled(this Av1BlockSize blockSize, int subX, int subY) |
|
|
{ |
|
|
{ |
|
|
if (blockSize == Av1BlockSize.Invalid) |
|
|
if (blockSize == Av1BlockSize.Invalid) |
|
|
@ -116,6 +167,13 @@ internal static class Av1BlockSizeExtensions |
|
|
return SubSampled[(int)blockSize][subX][subY]; |
|
|
return SubSampled[(int)blockSize][subX][subY]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the maximum chroma transform size after applying plane subsampling and AV1 chroma transform limits.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The luma block size.</param>
|
|
|
|
|
|
/// <param name="subX">Indicates horizontal chroma subsampling.</param>
|
|
|
|
|
|
/// <param name="subY">Indicates vertical chroma subsampling.</param>
|
|
|
|
|
|
/// <returns>The maximum chroma transform size, or <see cref="Av1TransformSize.Invalid"/> when the plane block size is invalid.</returns>
|
|
|
public static Av1TransformSize GetMaxUvTransformSize(this Av1BlockSize blockSize, bool subX, bool subY) |
|
|
public static Av1TransformSize GetMaxUvTransformSize(this Av1BlockSize blockSize, bool subX, bool subY) |
|
|
{ |
|
|
{ |
|
|
Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY); |
|
|
Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY); |
|
|
@ -135,12 +193,18 @@ internal static class Av1BlockSizeExtensions |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Returns the largest transform size that can be used for blocks of given size.
|
|
|
/// Gets the largest square or rectangular transform size permitted for a block.
|
|
|
/// The can be either a square or rectangular block.
|
|
|
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The maximum transform size.</returns>
|
|
|
public static Av1TransformSize GetMaximumTransformSize(this Av1BlockSize blockSize) |
|
|
public static Av1TransformSize GetMaximumTransformSize(this Av1BlockSize blockSize) |
|
|
=> MaxTransformSize[(int)blockSize]; |
|
|
=> MaxTransformSize[(int)blockSize]; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the base-two logarithm of the block's sample count.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="blockSize">The block size.</param>
|
|
|
|
|
|
/// <returns>The base-two logarithm of width multiplied by height.</returns>
|
|
|
public static int GetPelsLog2Count(this Av1BlockSize blockSize) |
|
|
public static int GetPelsLog2Count(this Av1BlockSize blockSize) |
|
|
=> PelsLog2Count[(int)blockSize]; |
|
|
=> PelsLog2Count[(int)blockSize]; |
|
|
} |
|
|
} |
|
|
|