diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs index 0808d4844a..718497703b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs @@ -82,8 +82,16 @@ internal partial class Av1FrameInfo this.deltaLoopFilter = new int[superblockCount << this.deltaLoopFactorLog2]; } + /// + /// Gets the number of mode info blocks in a single superblock. + /// public int ModeInfoCount => this.modeInfos.Length; + /// + /// Gets the Width or height of a single superblock, counted in mode info blocks. + /// + public int SuperblockModeInfoSize => this.modeInfoSizePerSuperblock; + public Av1SuperblockInfo GetSuperblock(Point index) { Span span = this.superblockInfos; diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs index e61c7b563e..fabeb629ed 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs @@ -66,7 +66,7 @@ internal class Av1ParseLeftNeighbor4x4Context public void UpdatePartition(Point modeInfoLocation, Av1SuperblockInfo superblockInfo, Av1BlockSize subSize, Av1BlockSize blockSize) { - int startIndex = (modeInfoLocation.Y - superblockInfo.Position.Y) & Av1PartitionContext.Mask; + int startIndex = (modeInfoLocation.Y - superblockInfo.ModeInfoPosition.Y) & Av1PartitionContext.Mask; int bh = blockSize.Get4x4HighCount(); int value = Av1PartitionContext.GetLeftContext(subSize); DebugGuard.MustBeLessThanOrEqualTo(startIndex, this.LeftTransformHeight.Length - bh, nameof(startIndex)); @@ -75,7 +75,7 @@ internal class Av1ParseLeftNeighbor4x4Context public void UpdateTransformation(Point modeInfoLocation, Av1SuperblockInfo superblockInfo, Av1TransformSize transformSize, Av1BlockSize blockSize, bool skip) { - int startIndex = modeInfoLocation.Y - superblockInfo.Position.Y; + int startIndex = modeInfoLocation.Y - superblockInfo.ModeInfoPosition.Y; int transformHeight = transformSize.GetHeight(); int n4h = blockSize.Get4x4HighCount(); if (skip) diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs index 05ac285fdb..e35658d9e2 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs @@ -18,6 +18,11 @@ internal class Av1SuperblockInfo /// public Point Position { get; } + /// + /// Gets the position of this superblock inside the tile, counted in mode info blocks (of 4x4 pixels). + /// + public Point ModeInfoPosition => this.Position * this.frameInfo.SuperblockModeInfoSize; + public ref int SuperblockDeltaQ => ref this.frameInfo.GetDeltaQuantizationIndex(this.Position); public Av1BlockModeInfo SuperblockModeInfo => this.GetModeInfo(new Point(0, 0)); diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs index ae1050a6c9..6a4dcbde81 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs @@ -372,7 +372,7 @@ internal class Av1TileReader : IAv1TileReader int txsWide = planeBlockSize.GetWidth() >> 2; int txsHigh = planeBlockSize.GetHeight() >> 2; int aboveOffset = (partitionInfo.ColumnIndex - tileInfo.ModeInfoColumnStart) >> subX; - int leftOffset = (partitionInfo.RowIndex - partitionInfo.SuperblockInfo.Position.Y) >> subY; + int leftOffset = (partitionInfo.RowIndex - partitionInfo.SuperblockInfo.ModeInfoPosition.Y) >> subY; this.aboveNeighborContext.ClearContext(i, aboveOffset, txsWide); this.leftNeighborContext.ClearContext(i, leftOffset, txsHigh); } @@ -1267,7 +1267,7 @@ internal class Av1TileReader : IAv1TileReader 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 leftHeight = this.leftNeighborContext.LeftTransformHeight[partitionInfo.RowIndex - superblockInfo.ModeInfoPosition.Y]; int left = (leftHeight >= maxTransformSize.GetHeight()) ? 1 : 0; bool hasAbove = partitionInfo.AvailableAbove; bool hasLeft = partitionInfo.AvailableLeft; @@ -1880,7 +1880,7 @@ internal class Av1TileReader : IAv1TileReader { // Maximum partition point is 8x8. Offset the log value occordingly. int aboveCtx = this.aboveNeighborContext.AbovePartitionWidth[location.X - tileInfo.ModeInfoColumnStart]; - int leftCtx = this.leftNeighborContext.LeftPartitionHeight[(location.Y - superblockInfo.Position.Y) & Av1PartitionContext.Mask]; + int leftCtx = this.leftNeighborContext.LeftPartitionHeight[(location.Y - superblockInfo.ModeInfoPosition.Y) & Av1PartitionContext.Mask]; int blockSizeLog = blockSize.Get4x4WidthLog2() - Av1BlockSize.Block8x8.Get4x4WidthLog2(); int above = (aboveCtx >> blockSizeLog) & 0x1; int left = (leftCtx >> blockSizeLog) & 0x1; diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1FrameDecoderStub.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1FrameDecoderStub.cs new file mode 100644 index 0000000000..c7378c14af --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1FrameDecoderStub.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; +using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; + +namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; + +internal class Av1FrameDecoderStub : IAv1FrameDecoder +{ + private readonly List superblocks = []; + + public void DecodeSuperblock(Point modeInfoPosition, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo) + => this.superblocks.Add(superblockInfo); + + public int SuperblockCount => this.superblocks.Count; +} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs index bab5512167..815c667bbe 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; -using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; @@ -12,9 +11,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; public class Av1TilingTests { [Theory] - [InlineData(TestImages.Heif.XnConvert, 0x010E, 0x03CC, 18)] - // [InlineData(TestImages.Heif.Orange4x4, 0x010E, 0x001d, 21)] - public void ReadFirstTile(string filename, int dataOffset, int dataSize, int tileOffset) + [InlineData(TestImages.Heif.XnConvert, 0x010E, 0x03CC, 18, 16)] + [InlineData(TestImages.Heif.Orange4x4, 0x010E, 0x001d, 21, 1)] + public void ReadFirstTile(string filename, int dataOffset, int dataSize, int tileOffset, int superblockCount) { // Assign string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, filename); @@ -27,7 +26,7 @@ public class Av1TilingTests obuReader.ReadAll(ref bitStreamReader, dataSize, stub); Av1FrameBuffer frameBuffer = new(Configuration.Default, obuReader.SequenceHeader, Av1ColorFormat.Yuv444, false); Av1FrameInfo frameInfo = new(obuReader.SequenceHeader); - Av1FrameDecoder frameDecoder = new(obuReader.SequenceHeader, obuReader.FrameHeader, frameInfo, frameBuffer); + Av1FrameDecoderStub frameDecoder = new(); Av1TileReader tileReader = new(Configuration.Default, obuReader.SequenceHeader, obuReader.FrameHeader, frameDecoder); // Act @@ -35,5 +34,6 @@ public class Av1TilingTests // Assert Assert.Equal(dataSize * 8, bitStreamReader.BitPosition); + Assert.Equal(superblockCount, frameDecoder.SuperblockCount); } }