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