diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameBuffer.cs new file mode 100644 index 000000000..8ae1eec8e --- /dev/null +++ b/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 span = this.superblockInfos; + int i = (index.Y * this.superblockColumnCount) + index.X; + return span[i]; + } + + public Av1BlockModeInfo GetModeInfo(Point superblockIndex, Point modeInfoIndex) + { + Span 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 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 span = this.transformInfosUv; + int i = 2 * ((index.Y * this.superblockColumnCount) + index.X) * this.modeInfoCountPerSuperblock; + transformU = span[i]; + transformV = span[i + 1]; + } + + public Span GetCoefficientsY(Point index) + { + Span span = this.coefficientsY; + int i = ((index.Y * this.modeInfoCountPerSuperblock) + index.X) * CoefficientCountPerModeInfo; + return span.Slice(i, CoefficientCountPerModeInfo); + } + + public Span GetCoefficientsU(Point index) + { + Span span = this.coefficientsU; + int i = ((index.Y * this.modeInfoCountPerSuperblock) + index.X) * CoefficientCountPerModeInfo; + return span.Slice(i >> this.subsamplingFactor, CoefficientCountPerModeInfo); + } + + public Span GetCoefficientsV(Point index) + { + Span 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 span = this.deltaQ; + int i = (index.Y * this.superblockColumnCount) + index.X; + return ref span[i]; + } + + public Span GetCdefStrength(Point index) + { + Span span = this.cdefStrength; + int i = ((index.Y * this.superblockColumnCount) + index.X) << this.cdefStrengthFactorLog2; + return span.Slice(i, 1 << this.cdefStrengthFactorLog2); + } + + public Span GetDeltaLoopFilter(Point index) + { + Span 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); +} diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs index 76528351f..0ee2d8049 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs +++ b/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 CoefficientsY => this.frameBuffer.GetCoefficientsY(this.Position); - public int[] CoefficientsU { get; set; } = []; + public Span CoefficientsU => this.frameBuffer.GetCoefficientsU(this.Position); - public int[] CoefficientsV { get; set; } = []; + public Span 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 CdefStrength => this.frameBuffer.GetCdefStrength(this.Position); - public int SuperblockDeltaLoopFilter { get; set; } + public Span 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); } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs index 2be0110be..da734fb13 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs +++ b/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 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)); } } }