Browse Source

Implement Transform parsing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
e60b39fb25
  1. 19
      src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
  2. 5
      src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
  3. 10
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
  4. 8
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs
  5. 43
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs
  6. 20
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
  7. 20
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
  8. 46
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
  9. 27
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs
  10. 246
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
  11. 18
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
  12. 2
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
  13. 38
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs

19
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,
};
}
/// <summary>
/// Returns the largest transform size that can be used for blocks of given size.
/// The can be either a square or rectangular block.

5
src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs

@ -133,4 +133,9 @@ internal static class Av1Constants
/// Number of values for palette_size.
/// </summary>
public const int PaletteMaxSize = 8;
/// <summary>
/// Maximum transform size categories.
/// </summary>
public const int MaxTransformCategories = 4;
}

10
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];

8
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)],
];
}

43
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<Av1SuperblockInfo> 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<Av1TransformInfo> span = this.transformInfosY;
return ref span[index];
}
public void SetTransformY(int index, Av1TransformInfo transformInfo)
{
Span<Av1TransformInfo> span = this.transformInfosY;
span[index] = transformInfo;
}
public ref Av1TransformInfo GetTransformY(Point index)
{
Span<Av1TransformInfo> 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<Av1TransformInfo> 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<Av1TransformInfo> 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<int> 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);
}

20
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);
}

20
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);
}

46
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;
}
}

27
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<byte> 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];
}

246
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
/// <summary>
/// 5.11.3. Clear block decoded flags function.
/// </summary>
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
}
/// <summary>
/// 5.11.16. Block TX size syntax.
/// 5.11.15. TX size syntax.
/// </summary>
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);
}
/// <summary>
/// Section 5.11.16. Block TX size syntax.
/// </summary>
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;
}
/// <summary>
@ -594,16 +750,15 @@ internal class Av1TileDecoder : IAv1TileDecoder
/// </summary>
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);

18
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; }
}

2
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
{

38
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];
}

Loading…
Cancel
Save