Browse Source

Introduce Frame buffer class

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
19ca2ea3c8
  1. 137
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs
  2. 28
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
  3. 65
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs

137
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs

@ -0,0 +1,137 @@
// 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 Av1FrameBuffer
{
// Number of Coefficients in a single ModeInfo 4x4 block of pixels (1 length + 4 x 4).
public const int CoefficientCountPerModeInfo = 1 + 16;
private readonly int[] coefficientsY = [];
private readonly int[] coefficientsU = [];
private readonly int[] coefficientsV = [];
private readonly int modeInfoSizePerSuperblock;
private readonly int modeInfoCountPerSuperblock;
private readonly int superblockColumnCount;
private readonly int superblockRowCount;
private readonly int subsamplingFactor;
private readonly Av1SuperblockInfo[] superblockInfos;
private readonly Av1BlockModeInfo[] modeInfos;
private readonly Av1TransformInfo[] transformInfosY;
private readonly Av1TransformInfo[] transformInfosUv;
private readonly int[] deltaQ;
private readonly int cdefStrengthFactorLog2;
private readonly int[] cdefStrength;
private readonly int deltaLoopFactorLog2 = 2;
private readonly int[] deltaLoopFilter;
public Av1FrameBuffer(ObuSequenceHeader sequenceHeader)
{
// init_main_frame_ctxt
int superblockSizeLog2 = sequenceHeader.SuperBlockSizeLog2;
int superblockAlignedWidth = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, superblockSizeLog2);
int superblockAlignedHeight = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameHeight, superblockSizeLog2);
this.superblockColumnCount = superblockAlignedWidth >> superblockSizeLog2;
this.superblockRowCount = superblockAlignedHeight >> superblockSizeLog2;
int superblockCount = this.superblockColumnCount * this.superblockRowCount;
this.modeInfoSizePerSuperblock = 1 << (superblockSizeLog2 - Av1Constants.ModeInfoSizeLog2);
this.modeInfoCountPerSuperblock = this.modeInfoSizePerSuperblock * this.modeInfoSizePerSuperblock;
this.superblockInfos = new Av1SuperblockInfo[superblockCount];
this.modeInfos = new Av1BlockModeInfo[superblockCount * this.modeInfoCountPerSuperblock];
this.transformInfosY = new Av1TransformInfo[superblockCount * this.modeInfoCountPerSuperblock];
this.transformInfosUv = new Av1TransformInfo[2 * superblockCount * this.modeInfoCountPerSuperblock];
this.coefficientsY = new int[superblockCount * this.modeInfoCountPerSuperblock * CoefficientCountPerModeInfo];
bool subX = sequenceHeader.ColorConfig.SubSamplingX;
bool subY = sequenceHeader.ColorConfig.SubSamplingY;
// Factor: 444 => 0, 422 => 1, 420 => 2.
this.subsamplingFactor = (subX && subY) ? 2 : (subX && !subY) ? 1 : (!subX && !subY) ? 0 : -1;
Guard.IsFalse(this.subsamplingFactor == -1, nameof(this.subsamplingFactor), "Invalid combination of subsampling.");
this.coefficientsU = new int[(this.modeInfoCountPerSuperblock * CoefficientCountPerModeInfo) >> this.subsamplingFactor];
this.coefficientsV = new int[(this.modeInfoCountPerSuperblock * CoefficientCountPerModeInfo) >> this.subsamplingFactor];
this.deltaQ = new int[superblockCount];
// Superblock size: 128x128 has sizelog2 = 7, 64x64 = 6. Factor should be 128x128 => 4 and 64x64 => 1.
this.cdefStrengthFactorLog2 = (superblockSizeLog2 - 6) << 2;
this.cdefStrength = new int[superblockCount << this.cdefStrengthFactorLog2];
Array.Fill(this.cdefStrength, -1);
this.deltaLoopFilter = new int[superblockCount << this.deltaLoopFactorLog2];
}
public Av1SuperblockInfo GetSuperblock(Point index)
{
Span<Av1SuperblockInfo> span = this.superblockInfos;
int i = (index.Y * this.superblockColumnCount) + index.X;
return span[i];
}
public Av1BlockModeInfo GetModeInfo(Point superblockIndex, Point modeInfoIndex)
{
Span<Av1BlockModeInfo> span = this.modeInfos;
int baseIndex = ((superblockIndex.Y * this.superblockColumnCount) + superblockIndex.X) * this.modeInfoCountPerSuperblock;
int offset = (modeInfoIndex.Y * this.modeInfoSizePerSuperblock) + modeInfoIndex.X;
return span[baseIndex + offset];
}
public Av1TransformInfo GetTransformY(Point index)
{
Span<Av1TransformInfo> span = this.transformInfosY;
int i = (index.Y * this.superblockColumnCount) + index.X;
return span[i * this.modeInfoCountPerSuperblock];
}
public void GetTransformUv(Point index, out Av1TransformInfo transformU, out Av1TransformInfo transformV)
{
Span<Av1TransformInfo> span = this.transformInfosUv;
int i = 2 * ((index.Y * this.superblockColumnCount) + index.X) * this.modeInfoCountPerSuperblock;
transformU = span[i];
transformV = span[i + 1];
}
public Span<int> GetCoefficientsY(Point index)
{
Span<int> span = this.coefficientsY;
int i = ((index.Y * this.modeInfoCountPerSuperblock) + index.X) * CoefficientCountPerModeInfo;
return span.Slice(i, CoefficientCountPerModeInfo);
}
public Span<int> GetCoefficientsU(Point index)
{
Span<int> span = this.coefficientsU;
int i = ((index.Y * this.modeInfoCountPerSuperblock) + index.X) * CoefficientCountPerModeInfo;
return span.Slice(i >> this.subsamplingFactor, CoefficientCountPerModeInfo);
}
public Span<int> GetCoefficientsV(Point index)
{
Span<int> span = this.coefficientsV;
int i = ((index.Y * this.modeInfoCountPerSuperblock) + index.X) * CoefficientCountPerModeInfo;
return span.Slice(i >> this.subsamplingFactor, CoefficientCountPerModeInfo);
}
public ref int GetDeltaQuantizationIndex(Point index)
{
Span<int> span = this.deltaQ;
int i = (index.Y * this.superblockColumnCount) + index.X;
return ref span[i];
}
public Span<int> GetCdefStrength(Point index)
{
Span<int> span = this.cdefStrength;
int i = ((index.Y * this.superblockColumnCount) + index.X) << this.cdefStrengthFactorLog2;
return span.Slice(i, 1 << this.cdefStrengthFactorLog2);
}
public Span<int> GetDeltaLoopFilter(Point index)
{
Span<int> span = this.deltaLoopFilter;
int i = ((index.Y * this.superblockColumnCount) + index.X) << this.deltaLoopFactorLog2;
return span.Slice(i, 1 << this.deltaLoopFactorLog2);
}
internal void ClearDeltaLoopFilter() => Array.Fill(this.deltaLoopFilter, 0);
}

28
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs

@ -5,27 +5,31 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1SuperblockInfo
{
public Av1SuperblockInfo(Av1BlockModeInfo superblockModeInfo, Av1TransformInfo superblockTransformInfo)
private readonly Av1FrameBuffer frameBuffer;
public Av1SuperblockInfo(Av1FrameBuffer frameBuffer, Point position)
{
this.SuperblockModeInfo = superblockModeInfo;
this.SuperblockTransformInfo = superblockTransformInfo;
this.Position = position;
this.frameBuffer = frameBuffer;
}
public int SuperblockDeltaQ { get; internal set; }
public Point Position { get; }
public ref int SuperblockDeltaQ => ref this.frameBuffer.GetDeltaQuantizationIndex(this.Position);
public Av1BlockModeInfo SuperblockModeInfo { get; set; }
public Av1BlockModeInfo SuperblockModeInfo => this.GetModeInfo(default);
public int[] CoefficientsY { get; set; } = [];
public Span<int> CoefficientsY => this.frameBuffer.GetCoefficientsY(this.Position);
public int[] CoefficientsU { get; set; } = [];
public Span<int> CoefficientsU => this.frameBuffer.GetCoefficientsU(this.Position);
public int[] CoefficientsV { get; set; } = [];
public Span<int> CoefficientsV => this.frameBuffer.GetCoefficientsV(this.Position);
public Av1TransformInfo SuperblockTransformInfo { get; set; }
public Av1TransformInfo SuperblockTransformInfo => this.frameBuffer.GetTransformY(this.Position);
public int CdefStrength { get; internal set; }
public Span<int> CdefStrength => this.frameBuffer.GetCdefStrength(this.Position);
public int SuperblockDeltaLoopFilter { get; set; }
public Span<int> SuperblockDeltaLoopFilter => this.frameBuffer.GetDeltaLoopFilter(this.Position);
public Av1BlockModeInfo GetModeInfo(int rowIndex, int columnIndex) => throw new NotImplementedException();
public Av1BlockModeInfo GetModeInfo(Point index) => this.frameBuffer.GetModeInfo(this.Position, index);
}

65
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs

@ -14,9 +14,6 @@ internal class Av1TileDecoder : IAv1TileDecoder
private static readonly int[] WienerTapsMid = [3, -7, 15];
private const int PartitionProbabilitySet = 4;
// Number of Coefficients in a single ModeInfo 4x4 block of pixels (1 DC + 16 AC).
private const int NumberofCoefficients = 1 + 16;
private bool[][][] blockDecoded = [];
private int[][] referenceSgrXqd = [];
private int[][][] referenceLrWiener = [];
@ -32,19 +29,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
private int maxLumaHeight;
private int deltaLoopFilterResolution = -1;
private int deltaQuantizerResolution = -1;
private int[] coefficientsY = [];
private int[] coefficientsU = [];
private int[] coefficientsV = [];
private int numModeInfosInSuperblock;
private int superblockColumnCount;
private int superblockRowCount;
private Av1SuperblockInfo[] superblockInfos;
private Av1BlockModeInfo[] modeInfos;
private Av1TransformInfo[] transformInfosY;
private Av1TransformInfo[] transformInfosUv;
private int[] deltaQ;
private int[] cdefStrength;
private int[] deltaLoopFilter;
private readonly Av1FrameBuffer frameBuffer;
public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo)
{
@ -53,29 +38,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.TileInfo = tileInfo;
// init_main_frame_ctxt
int superblockSizeLog2 = this.SequenceHeader.SuperBlockSizeLog2;
int superblockAlignedWidth = Av1Math.AlignPowerOf2(this.SequenceHeader.MaxFrameWidth, superblockSizeLog2);
int superblockAlignedHeight = Av1Math.AlignPowerOf2(this.SequenceHeader.MaxFrameHeight, superblockSizeLog2);
this.superblockColumnCount = superblockAlignedWidth >> superblockSizeLog2;
this.superblockRowCount = superblockAlignedHeight >> superblockSizeLog2;
int superblockCount = this.superblockColumnCount * this.superblockRowCount;
this.numModeInfosInSuperblock = (1 << (superblockSizeLog2 - Av1Constants.ModeInfoSizeLog2)) * (1 << (superblockSizeLog2 - Av1Constants.ModeInfoSizeLog2));
this.superblockInfos = new Av1SuperblockInfo[superblockCount];
this.modeInfos = new Av1BlockModeInfo[superblockCount * this.numModeInfosInSuperblock];
this.transformInfosY = new Av1TransformInfo[superblockCount * this.numModeInfosInSuperblock];
this.transformInfosUv = new Av1TransformInfo[2 * superblockCount * this.numModeInfosInSuperblock];
this.coefficientsY = new int[superblockCount * this.numModeInfosInSuperblock * NumberofCoefficients];
int subsamplingFactor = (this.SequenceHeader.ColorConfig.SubSamplingX && this.SequenceHeader.ColorConfig.SubSamplingY) ? 2 :
(this.SequenceHeader.ColorConfig.SubSamplingX && !this.SequenceHeader.ColorConfig.SubSamplingY) ? 1 :
(!this.SequenceHeader.ColorConfig.SubSamplingX && !this.SequenceHeader.ColorConfig.SubSamplingY) ? 0 : -1;
Guard.IsFalse(subsamplingFactor == -1, nameof(subsamplingFactor), "Invalid combination of subsampling.");
this.coefficientsU = new int[(superblockCount * this.numModeInfosInSuperblock * NumberofCoefficients) >> subsamplingFactor];
this.coefficientsV = new int[(superblockCount * this.numModeInfosInSuperblock * NumberofCoefficients) >> subsamplingFactor];
this.deltaQ = new int[superblockCount];
this.cdefStrength = new int[superblockCount * (this.SequenceHeader.Use128x128SuperBlock ? 4 : 1)];
Array.Fill(this.cdefStrength, -1);
this.deltaLoopFilter = new int[superblockCount * Av1Constants.FrameLoopFilterCount];
this.frameBuffer = new(this.SequenceHeader);
}
public bool SequenceHeaderDone { get; set; }
@ -129,17 +92,8 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size);
int superblockIndex = (superBlockRow * this.superblockColumnCount) + superBlockColumn;
int cdefFactor = this.SequenceHeader.Use128x128SuperBlock ? 4 : 1;
Av1SuperblockInfo superblockInfo = new(this.modeInfos[superblockIndex], this.transformInfosY[superblockIndex])
{
CoefficientsY = this.coefficientsY,
CoefficientsU = this.coefficientsU,
CoefficientsV = this.coefficientsV,
CdefStrength = this.cdefStrength[superblockIndex * cdefFactor],
SuperblockDeltaLoopFilter = this.deltaLoopFilter[Av1Constants.FrameLoopFilterCount * superblockIndex],
SuperblockDeltaQ = this.deltaQ[superblockIndex]
};
Point superblockPosition = new Point(superBlockColumn, superBlockRow);
Av1SuperblockInfo superblockInfo = new(this.frameBuffer, superblockPosition);
// Nothing to do for CDEF
// this.ClearCdef(row, column);
@ -150,7 +104,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
private void ClearLoopFilterDelta()
=> this.deltaLoopFilter = new int[4];
=> this.frameBuffer.ClearDeltaLoopFilter();
private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size)
{
@ -350,12 +304,12 @@ internal class Av1TileDecoder : IAv1TileDecoder
if (partitionInfo.AvailableUp)
{
partitionInfo.AboveModeInfo = superblockInfo.GetModeInfo(rowIndex - 1, columnIndex);
partitionInfo.AboveModeInfo = superblockInfo.GetModeInfo(new Point(rowIndex - 1, columnIndex));
}
if (partitionInfo.AvailableLeft)
{
partitionInfo.LeftModeInfo = superblockInfo.GetModeInfo(rowIndex, columnIndex - 1);
partitionInfo.LeftModeInfo = superblockInfo.GetModeInfo(new Point(rowIndex, columnIndex - 1));
}
this.ReadModeInfo(ref reader, partitionInfo);
@ -448,7 +402,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
private void TransformBlock(int plane, int baseX, int baseY, Av1TransformSize transformSize, int x, int y)
{
Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(new(1, Av1BlockSize.Invalid), new()), false, Av1PartitionType.None);
Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(this.frameBuffer, default), false, Av1PartitionType.None);
int startX = (baseX + 4) * x;
int startY = (baseY + 4) * y;
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@ -915,6 +869,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
frameLoopFilterCount = this.SequenceHeader.ColorConfig.ChannelCount > 1 ? Av1Constants.FrameLoopFilterCount : Av1Constants.FrameLoopFilterCount - 2;
}
Span<int> currentDeltaLoopFilter = partitionInfo.SuperblockInfo.SuperblockDeltaLoopFilter;
for (int i = 0; i < frameLoopFilterCount; i++)
{
int deltaLoopFilterAbsolute = reader.ReadDeltaLoopFilterAbsolute();
@ -929,7 +884,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
{
bool deltaLoopFilterSign = reader.ReadLiteral(1) > 0;
int reducedDeltaLoopFilterLevel = deltaLoopFilterSign ? -deltaLoopFilterAbsolute : deltaLoopFilterAbsolute;
this.deltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, this.deltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << this.deltaLoopFilterResolution));
currentDeltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, currentDeltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << this.deltaLoopFilterResolution));
}
}
}

Loading…
Cancel
Save