diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs index f47ca720ea..4dbbfe56e0 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs @@ -49,7 +49,7 @@ internal ref struct Av1BitStreamReader public uint ReadLiteral(int bitCount) { - DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount)); + DebugGuard.MustBeBetweenOrEqualTo(bitCount, 1, 32, nameof(bitCount)); uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount); this.bitOffset += bitCount; diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs b/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs index b21696c64c..8824c957a0 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs @@ -18,8 +18,6 @@ internal class Av1Decoder : IAv1TileDecoder public ObuSequenceHeader? SequenceHeader { get; private set; } - public ObuTileGroupHeader? TilesHeader { get; private set; } - public void Decode(Span buffer) { Av1BitStreamReader reader = new(buffer); @@ -31,8 +29,7 @@ internal class Av1Decoder : IAv1TileDecoder { this.SequenceHeader = this.obuReader.SequenceHeader; this.FrameHeader = this.obuReader.FrameHeader; - this.TilesHeader = this.obuReader.TileGroupHeader; - this.tileDecoder = new Av1TileDecoder(this.SequenceHeader!, this.FrameHeader!, this.TilesHeader!); + this.tileDecoder = new Av1TileDecoder(this.SequenceHeader!, this.FrameHeader!); } public void DecodeTile(Span tileData, int tileNum) diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs index 7ad04f6bba..83dddd2fe4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs @@ -29,8 +29,6 @@ internal class ObuColorConfig public int BitDepth { get; set; } - public bool HasSeparateUvDeltaQ { get; set; } - public Av1ColorFormat GetColorFormat() { Av1ColorFormat format = Av1ColorFormat.Yuv400; diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuDeltaLoopFilterParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuDeltaLoopFilterParameters.cs index 76450d968c..da39e19f3c 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuDeltaLoopFilterParameters.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuDeltaLoopFilterParameters.cs @@ -9,5 +9,5 @@ internal class ObuDeltaLoopFilterParameters public int Resolution { get; internal set; } - public bool Multi { get; internal set; } + public bool IsMulti { get; internal set; } } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs index 51e11610f6..12e5197138 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs @@ -6,4 +6,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; internal class ObuLoopFilterParameters { public int[] FilterLevel { get; internal set; } = new int[2]; + public int FilterLevelU { get; internal set; } + public int FilterLevelV { get; internal set; } + public int SharpnessLevel { get; internal set; } + public bool ReferenceDeltaModeEnabled { get; internal set; } } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs index 889fe51f0d..91c14a7f33 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; @@ -11,8 +12,6 @@ internal class ObuReader public ObuFrameHeader? FrameHeader { get; set; } - public ObuTileGroupHeader? TileGroupHeader { get; set; } - /// /// Decode all OBU's in a frame. /// @@ -91,7 +90,6 @@ internal class ObuReader throw new InvalidImageContentException("Corrupt frame"); } - this.TileGroupHeader = new(); this.ReadTileGroup(ref reader, decoder, header, out frameDecodingFinished); if (frameDecodingFinished) { @@ -202,8 +200,9 @@ internal class ObuReader } } - private static void ComputeImageSize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) + private void ComputeImageSize(ObuSequenceHeader sequenceHeader) { + ObuFrameHeader frameInfo = this.FrameHeader!; frameInfo.ModeInfoColumnCount = 2 * ((frameInfo.FrameSize.FrameWidth + 7) >> 3); frameInfo.ModeInfoRowCount = 2 * ((frameInfo.FrameSize.FrameHeight + 7) >> 3); frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, Av1Constants.MaxSuperBlockSizeLog2) >> Av1Constants.ModeInfoSizeLog2; @@ -366,7 +365,7 @@ internal class ObuReader } } - colorConfig.HasSeparateUvDeltaQ = reader.ReadBoolean(); + colorConfig.HasSeparateUvDelta = reader.ReadBoolean(); return colorConfig; } @@ -387,8 +386,10 @@ internal class ObuReader } } - private static void ReadSuperResolutionParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) + private void ReadSuperResolutionParameters(ref Av1BitStreamReader reader) { + ObuSequenceHeader sequenceHeader = this.SequenceHeader!; + ObuFrameHeader frameInfo = this.FrameHeader!; bool useSuperResolution = false; if (sequenceHeader.EnableSuperResolution) { @@ -417,8 +418,9 @@ internal class ObuReader } } - private static void ReadRenderSize(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo) + private void ReadRenderSize(ref Av1BitStreamReader reader) { + ObuFrameHeader frameInfo = this.FrameHeader!; bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean(); if (renderSizeAndFrameSizeDifferent) { @@ -432,8 +434,10 @@ internal class ObuReader } } - private static void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) + private void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag) { + ObuSequenceHeader sequenceHeader = this.SequenceHeader!; + ObuFrameHeader frameInfo = this.FrameHeader!; bool foundReference = false; for (int i = 0; i < Av1Constants.ReferencesPerFrame; i++) { @@ -447,18 +451,20 @@ internal class ObuReader if (!foundReference) { - ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); - ReadRenderSize(ref reader, frameInfo); + this.ReadFrameSize(ref reader, frameSizeOverrideFlag); + this.ReadRenderSize(ref reader); } else { - ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo); - ComputeImageSize(sequenceHeader, frameInfo); + this.ReadSuperResolutionParameters(ref reader); + this.ComputeImageSize(sequenceHeader); } } - private static void ReadFrameSize(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) + private void ReadFrameSize(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag) { + ObuSequenceHeader sequenceHeader = this.SequenceHeader!; + ObuFrameHeader frameInfo = this.FrameHeader!; if (frameSizeOverrideFlag) { frameInfo.FrameSize.FrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1; @@ -470,8 +476,8 @@ internal class ObuReader frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight; } - ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo); - ComputeImageSize(sequenceHeader, frameInfo); + this.ReadSuperResolutionParameters(ref reader); + this.ComputeImageSize(sequenceHeader); } private static ObuTileGroupHeader ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) @@ -670,11 +676,8 @@ internal class ObuReader { frameInfo.ReferenceValid = new bool[Av1Constants.ReferenceFrameCount]; frameInfo.ReferenceOrderHint = new bool[Av1Constants.ReferenceFrameCount]; - for (int i = 0; i < Av1Constants.ReferenceFrameCount; i++) - { - frameInfo.ReferenceValid[i] = false; - frameInfo.ReferenceOrderHint[i] = false; - } + Array.Fill(frameInfo.ReferenceValid, false); + Array.Fill(frameInfo.ReferenceOrderHint, false); } frameInfo.DisableCdfUpdate = reader.ReadBoolean(); @@ -761,7 +764,14 @@ internal class ObuReader frameSizeOverrideFlag = reader.ReadBoolean(); } - frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); + if (sequenceHeader.OrderHintInfo.OrderHintBits > 0) + { + frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); + } + else + { + frameInfo.OrderHint = 0; + } if (isIntraFrame || frameInfo.ErrorResilientMode) { @@ -807,8 +817,8 @@ internal class ObuReader if (isIntraFrame) { - ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); - ReadRenderSize(ref reader, frameInfo); + this.ReadFrameSize(ref reader, frameSizeOverrideFlag); + this.ReadRenderSize(ref reader); if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0) { if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth) @@ -852,6 +862,9 @@ internal class ObuReader int tilesCount = frameInfo.TilesInfo.TileColumnCount * frameInfo.TilesInfo.TileRowCount; frameInfo.CodedLossless = true; + frameInfo.SegmentationParameters.QMLevel[0] = new int[Av1Constants.MaxSegmentCount]; + frameInfo.SegmentationParameters.QMLevel[1] = new int[Av1Constants.MaxSegmentCount]; + frameInfo.SegmentationParameters.QMLevel[2] = new int[Av1Constants.MaxSegmentCount]; for (int segmentId = 0; segmentId < Av1Constants.MaxSegmentCount; segmentId++) { int qIndex = GetQIndex(frameInfo.SegmentationParameters, segmentId, frameInfo.QuantizationParameters.BaseQIndex); @@ -871,19 +884,24 @@ internal class ObuReader { if (frameInfo.LosslessArray[segmentId]) { - frameInfo.SegmentationParameters.QMLevel[0, segmentId] = 15; - frameInfo.SegmentationParameters.QMLevel[1, segmentId] = 15; - frameInfo.SegmentationParameters.QMLevel[2, segmentId] = 15; + frameInfo.SegmentationParameters.QMLevel[0][segmentId] = 15; + frameInfo.SegmentationParameters.QMLevel[1][segmentId] = 15; + frameInfo.SegmentationParameters.QMLevel[2][segmentId] = 15; } else { - frameInfo.SegmentationParameters.QMLevel[0, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y]; - frameInfo.SegmentationParameters.QMLevel[1, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U]; - frameInfo.SegmentationParameters.QMLevel[2, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V]; + frameInfo.SegmentationParameters.QMLevel[0][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y]; + frameInfo.SegmentationParameters.QMLevel[1][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U]; + frameInfo.SegmentationParameters.QMLevel[2][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V]; } } } + if (frameInfo.CodedLossless) + { + DebugGuard.IsFalse(frameInfo.DeltaQParameters.IsPresent, nameof(frameInfo.DeltaQParameters.IsPresent), "No Delta Q parameters are allowed for lossless frame."); + } + frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth; ReadLoopFilterParameters(ref reader, sequenceHeader, frameInfo, planesCount); ReadCdefParameters(ref reader, sequenceHeader, frameInfo, planesCount); @@ -896,6 +914,10 @@ internal class ObuReader { frameInfo.AllowWarpedMotion = false; } + else + { + frameInfo.AllowWarpedMotion = reader.ReadBoolean(); + } frameInfo.ReducedTransformSet = reader.ReadBoolean(); ReadGlobalMotionParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame); @@ -945,8 +967,7 @@ internal class ObuReader { ObuSequenceHeader sequenceHeader = this.SequenceHeader!; ObuFrameHeader frameInfo = this.FrameHeader!; - ObuTileGroupHeader tileInfo = this.TileGroupHeader!; - this.TileGroupHeader = tileInfo; + ObuTileGroupHeader tileInfo = this.FrameHeader!.TilesInfo; int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount; int startBitPosition = reader.BitPosition; bool tileStartAndEndPresentFlag = false; @@ -1013,7 +1034,7 @@ internal class ObuReader int deltaQ = 0; if (reader.ReadBoolean()) { - deltaQ = reader.ReadSignedFromUnsigned(6); + deltaQ = reader.ReadSignedFromUnsigned(7); } return deltaQ; @@ -1038,7 +1059,7 @@ internal class ObuReader { frameInfo.DeltaLoopFilterParameters.IsPresent = false; frameInfo.DeltaLoopFilterParameters.Resolution = 0; - frameInfo.DeltaLoopFilterParameters.Multi = false; + frameInfo.DeltaLoopFilterParameters.IsMulti = false; if (frameInfo.DeltaQParameters.IsPresent) { if (!frameInfo.AllowIntraBlockCopy) @@ -1049,7 +1070,7 @@ internal class ObuReader if (frameInfo.DeltaLoopFilterParameters.IsPresent) { frameInfo.DeltaLoopFilterParameters.Resolution = (int)reader.ReadLiteral(2); - frameInfo.DeltaLoopFilterParameters.Multi = reader.ReadBoolean(); + frameInfo.DeltaLoopFilterParameters.IsMulti = reader.ReadBoolean(); } } } @@ -1062,6 +1083,11 @@ internal class ObuReader if (planesCount > 1) { bool areUvDeltaDifferent = false; + if (colorInfo.HasSeparateUvDelta) + { + areUvDeltaDifferent = reader.ReadBoolean(); + } + quantParams.DeltaQDc[(int)Av1Plane.U] = ReadDeltaQ(ref reader); quantParams.DeltaQAc[(int)Av1Plane.U] = ReadDeltaQ(ref reader); if (areUvDeltaDifferent) @@ -1088,7 +1114,7 @@ internal class ObuReader { quantParams.QMatrix[(int)Av1Plane.Y] = (int)reader.ReadLiteral(4); quantParams.QMatrix[(int)Av1Plane.U] = (int)reader.ReadLiteral(4); - if (!colorInfo.HasSeparateUvDeltaQ) + if (!colorInfo.HasSeparateUvDelta) { quantParams.QMatrix[(int)Av1Plane.V] = quantParams.QMatrix[(int)Av1Plane.U]; } @@ -1115,14 +1141,31 @@ internal class ObuReader private static void ReadLoopFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) { + frameInfo.LoopFilterParameters.FilterLevel = new int[2]; if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy) { - frameInfo.LoopFilterParameters.FilterLevel[0] = 0; - frameInfo.LoopFilterParameters.FilterLevel[1] = 0; return; } - // TODO: Parse more stuff. + frameInfo.LoopFilterParameters.FilterLevel[0] = (int)reader.ReadLiteral(6); + frameInfo.LoopFilterParameters.FilterLevel[1] = (int)reader.ReadLiteral(6); + + if (planesCount > 1) + { + if (frameInfo.LoopFilterParameters.FilterLevel[0] > 0 || frameInfo.LoopFilterParameters.FilterLevel[1] > 0) + { + frameInfo.LoopFilterParameters.FilterLevelU = (int)reader.ReadLiteral(6); + frameInfo.LoopFilterParameters.FilterLevelV = (int)reader.ReadLiteral(6); + } + } + + frameInfo.LoopFilterParameters.SharpnessLevel = (int)reader.ReadLiteral(3); + frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled = reader.ReadBoolean(); + if (frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled) + { + // TODO: Implement. + throw new NotImplementedException(); + } } private static void ReadTransformMode(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo) @@ -1238,6 +1281,7 @@ internal class ObuReader } // Not applicable for INTRA frames. + throw new NotImplementedException(); } private static ObuReferenceMode ReadFrameReferenceMode(ref Av1BitStreamReader reader, bool isIntraFrame) @@ -1286,7 +1330,7 @@ internal class ObuReader } // TODO: Implement parsing. - return grainParams; + throw new NotImplementedException(); } private static bool IsValidSequenceLevel(int sequenceLevelIndex) diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs index 64624e8535..07d4a6cd8d 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs @@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; internal class ObuSegmentationParameters { - public int[,] QMLevel { get; internal set; } = new int[3, Av1Constants.MaxSegmentCount]; + public int[][] QMLevel { get; internal set; } = new int[3][]; public bool[,] FeatureEnabled { get; internal set; } = new bool[Av1Constants.MaxSegmentCount, Av1Constants.SegmentationLevelMax]; diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs index e9847ffea0..226116c797 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs @@ -10,7 +10,7 @@ internal class ObuWriter /// /// Encode a single frame into OBU's. /// - public static void Write(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo) + public static void Write(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) { MemoryStream bufferStream = new(100); Av1BitStreamWriter writer = new(bufferStream); @@ -26,7 +26,7 @@ internal class ObuWriter WriteObuHeaderAndSize(stream, ObuType.FrameHeader, bufferStream.GetBuffer(), (int)bufferStream.Position); bufferStream.Position = 0; - WriteTileGroup(ref writer, tileInfo); + WriteTileGroup(ref writer, frameInfo.TilesInfo); writer.Flush(); WriteObuHeaderAndSize(stream, ObuType.TileGroup, bufferStream.GetBuffer(), (int)bufferStream.Position); } @@ -145,7 +145,7 @@ internal class ObuWriter } } - writer.WriteBoolean(colorConfig.HasSeparateUvDeltaQ); + writer.WriteBoolean(colorConfig.HasSeparateUvDelta); } private static void WriteBitDepth(ref Av1BitStreamWriter writer, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader) @@ -520,7 +520,7 @@ internal class ObuWriter if (frameInfo.DeltaLoopFilterParameters.IsPresent) { writer.WriteLiteral((uint)frameInfo.DeltaLoopFilterParameters.Resolution, 2); - writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.Multi); + writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.IsMulti); } } } @@ -535,6 +535,11 @@ internal class ObuWriter if (planesCount > 1) { bool areUvDeltaDifferent = false; + if (colorInfo.HasSeparateUvDelta) + { + writer.WriteBoolean(colorInfo.HasSeparateUvDelta); + } + WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.U]); WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.U]); if (areUvDeltaDifferent) @@ -549,7 +554,7 @@ internal class ObuWriter { writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.Y], 4); writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.U], 4); - if (colorInfo.HasSeparateUvDeltaQ) + if (colorInfo.HasSeparateUvDelta) { writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.V], 4); } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs index d283906a0f..a1edf35570 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs @@ -30,11 +30,10 @@ internal class Av1TileDecoder : IAv1TileDecoder private int deltaLoopFilterResolution = -1; private int deltaQuantizerResolution = -1; - public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo) + public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) { this.FrameInfo = frameInfo; this.SequenceHeader = sequenceHeader; - this.TileInfo = tileInfo; // init_main_frame_ctxt this.FrameBuffer = new(this.SequenceHeader); @@ -50,17 +49,15 @@ internal class Av1TileDecoder : IAv1TileDecoder public ObuSequenceHeader SequenceHeader { get; } - public ObuTileGroupHeader TileInfo { get; } - public Av1FrameBuffer FrameBuffer { get; } public void DecodeTile(Span tileData, int tileNum) { Av1SymbolDecoder reader = new(tileData); - int tileColumnIndex = tileNum % this.TileInfo.TileColumnCount; - int tileRowIndex = tileNum / this.TileInfo.TileColumnCount; + int tileColumnIndex = tileNum % this.FrameInfo.TilesInfo.TileColumnCount; + int tileRowIndex = tileNum / this.FrameInfo.TilesInfo.TileColumnCount; - this.aboveContext.Clear(this.TileInfo.TileColumnStartModeInfo[tileColumnIndex], this.TileInfo.TileColumnStartModeInfo[tileColumnIndex - 1]); + this.aboveContext.Clear(this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex], this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex - 1]); this.ClearLoopFilterDelta(); int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3; this.referenceSgrXqd = new int[planesCount][]; @@ -79,11 +76,11 @@ internal class Av1TileDecoder : IAv1TileDecoder Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; int superBlock4x4Size = superBlockSize.Get4x4WideCount(); - for (int row = this.TileInfo.TileRowStartModeInfo[tileRowIndex]; row < this.TileInfo.TileRowStartModeInfo[tileRowIndex + 1]; row += this.SequenceHeader.ModeInfoSize) + for (int row = this.FrameInfo.TilesInfo.TileRowStartModeInfo[tileRowIndex]; row < this.FrameInfo.TilesInfo.TileRowStartModeInfo[tileRowIndex + 1]; row += this.SequenceHeader.ModeInfoSize) { int superBlockRow = row << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2; this.leftContext.Clear(); - for (int column = this.TileInfo.TileColumnStartModeInfo[tileColumnIndex]; column < this.TileInfo.TileColumnStartModeInfo[tileColumnIndex + 1]; column += this.SequenceHeader.ModeInfoSize) + for (int column = this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex]; column < this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex + 1]; column += this.SequenceHeader.ModeInfoSize) { int superBlockColumn = column << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2; bool subSamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; @@ -165,8 +162,8 @@ internal class Av1TileDecoder : IAv1TileDecoder private void ParsePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo) { - if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] || - columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex]) + if (rowIndex >= this.FrameInfo.TilesInfo.TileRowStartModeInfo[rowIndex] || + columnIndex >= this.FrameInfo.TilesInfo.TileColumnStartModeInfo[columnIndex]) { return; } @@ -339,8 +336,8 @@ internal class Av1TileDecoder : IAv1TileDecoder Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); int txsWide = planeBlockSize.GetWidth() >> 2; int txsHigh = planeBlockSize.GetHeight() >> 2; - int aboveOffset = (partitionInfo.ColumnIndex - this.TileInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0); - int leftOffset = (partitionInfo.RowIndex - this.TileInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0); + int aboveOffset = (partitionInfo.ColumnIndex - this.FrameInfo.TilesInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0); + int leftOffset = (partitionInfo.RowIndex - this.FrameInfo.TilesInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0); int[] aboveContext = this.aboveContext.AboveContext[i + aboveOffset]; int[] leftContext = this.leftContext.LeftContext[i + leftOffset]; Array.Fill(aboveContext, 0); @@ -870,7 +867,7 @@ internal class Av1TileDecoder : IAv1TileDecoder if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent) { int frameLoopFilterCount = 1; - if (this.FrameInfo.DeltaLoopFilterParameters.Multi) + if (this.FrameInfo.DeltaLoopFilterParameters.IsMulti) { frameLoopFilterCount = this.SequenceHeader.ColorConfig.ChannelCount > 1 ? Av1Constants.FrameLoopFilterCount : Av1Constants.FrameLoopFilterCount - 2; } @@ -942,10 +939,10 @@ internal class Av1TileDecoder : IAv1TileDecoder } private bool IsInside(int rowIndex, int columnIndex) => - columnIndex >= this.TileInfo.TileColumnCount && - columnIndex < this.TileInfo.TileColumnCount && - rowIndex >= this.TileInfo.TileRowCount && - rowIndex < this.TileInfo.TileRowCount; + columnIndex >= this.FrameInfo.TilesInfo.TileColumnCount && + columnIndex < this.FrameInfo.TilesInfo.TileColumnCount && + rowIndex >= this.FrameInfo.TilesInfo.TileRowCount && + rowIndex < this.FrameInfo.TilesInfo.TileRowCount; /* private static bool IsChroma(int rowIndex, int columnIndex, Av1BlockModeInfo blockMode, bool subSamplingX, bool subSamplingY) @@ -961,8 +958,8 @@ internal class Av1TileDecoder : IAv1TileDecoder { // Maximum partition point is 8x8. Offset the log value occordingly. int blockSizeLog = blockSize.Get4x4WidthLog2() - Av1BlockSize.Block8x8.Get4x4WidthLog2(); - int aboveCtx = this.aboveContext.PartitionWidth + columnIndex - this.TileInfo.TileColumnStartModeInfo[columnIndex]; - int leftCtx = this.leftContext.PartitionHeight + rowIndex - this.TileInfo.TileRowStartModeInfo[rowIndex]; + int aboveCtx = this.aboveContext.PartitionWidth + columnIndex - this.FrameInfo.TilesInfo.TileColumnStartModeInfo[columnIndex]; + int leftCtx = this.leftContext.PartitionHeight + rowIndex - this.FrameInfo.TilesInfo.TileRowStartModeInfo[rowIndex]; int above = (aboveCtx >> blockSizeLog) & 0x1; int left = (leftCtx >> blockSizeLog) & 0x1; return (left * 2) + above + (blockSizeLog * PartitionProbabilitySet); diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs index c24eac5555..2340fe427b 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs @@ -31,7 +31,7 @@ public class ObuFrameHeaderTests // Assert Assert.NotNull(obuReader.SequenceHeader); Assert.NotNull(obuReader.FrameHeader); - Assert.NotNull(obuReader.TileGroupHeader); + Assert.NotNull(obuReader.FrameHeader.TilesInfo); } /* [Theory] @@ -78,7 +78,7 @@ public class ObuFrameHeaderTests MemoryStream encoded = new(); // Act 2 - ObuWriter.Write(encoded, obuReader1.SequenceHeader, obuReader1.FrameHeader, obuReader1.TileGroupHeader); + ObuWriter.Write(encoded, obuReader1.SequenceHeader, obuReader1.FrameHeader); // Assign 2 Span encodedBuffer = encoded.ToArray(); @@ -93,7 +93,7 @@ public class ObuFrameHeaderTests Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig)); Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader), PrettyPrintProperties(obuReader2.SequenceHeader)); Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader), PrettyPrintProperties(obuReader2.FrameHeader)); - Assert.Equal(PrettyPrintProperties(obuReader1.TileGroupHeader), PrettyPrintProperties(obuReader2.TileGroupHeader)); + Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader.TilesInfo), PrettyPrintProperties(obuReader2.FrameHeader.TilesInfo)); } private static string PrettyPrintProperties(object obj)