diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs
index 391e26d52..9d459c6ce 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs
@@ -120,9 +120,7 @@ internal class Av1FrameDecoder : IAv1FrameDecoder
///
private void DecodePartition(Point modeInfoPosition, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
- Av1BlockModeInfo modeInfo = superblockInfo.GetModeInfo(modeInfoPosition);
-
- for (int i = 0; i < superblockInfo.BlockCount; i++)
+ foreach (Av1BlockModeInfo modeInfo in superblockInfo.GetModeInfos())
{
Point subPosition = modeInfo.PositionInSuperblock;
Av1BlockSize subSize = modeInfo.BlockSize;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
index a61f14657..a004797c1 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
@@ -119,6 +119,16 @@ internal partial class Av1FrameInfo
return this.modeInfos[index];
}
+ ///
+ /// Gets the mode information records parsed for the specified superblock in bitstream order.
+ ///
+ public Span GetModeInfos(Point superblockIndex, int count)
+ {
+ Point location = this.GetModeInfoPosition(superblockIndex, Point.Empty);
+ int index = this.modeInfoMap[location];
+ return this.modeInfos.AsSpan(index, count);
+ }
+
public Span GetSuperblockTransform(int plane, Point index)
{
if (plane == 0)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
index 4f84d21a7..11a852218 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
@@ -47,6 +47,11 @@ internal class Av1SuperblockInfo
public Span GetTransformInfoUv() => this.frameInfo.GetSuperblockTransformUv(this.Position);
+ ///
+ /// Gets the mode information records parsed for this superblock in bitstream order.
+ ///
+ public Span GetModeInfos() => this.frameInfo.GetModeInfos(this.Position, this.BlockCount);
+
public Av1BlockModeInfo GetModeInfo(Point index) => this.frameInfo.GetModeInfo(this.Position, index);
public Span GetCoefficients(Av1Plane plane) => plane switch
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs
index bc85d6ebd..efd583760 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TilingTests.cs
@@ -112,6 +112,43 @@ public class Av1TilingTests
Assert.Equal(superblockCount, frameDecoder.SuperblockCount);
}
+ [Fact]
+ public void ParsedSuperblocksExposeEveryModeInfoInBitstreamOrder()
+ {
+ string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Heif.XnConvert);
+ byte[] content = File.ReadAllBytes(filePath);
+ const int dataOffset = 0x010E;
+ const int dataSize = 0x03CC;
+ const int tileOffset = 18;
+ Span headerSpan = content.AsSpan(dataOffset, dataSize);
+ Span tileSpan = content.AsSpan(tileOffset, dataSize - tileOffset);
+ Av1BitStreamReader bitStreamReader = new(headerSpan);
+ IAv1TileReader stub = new Av1TileDecoderStub();
+ ObuReader obuReader = new();
+ obuReader.ReadAll(ref bitStreamReader, dataSize, () => stub);
+ Av1TileReader tileReader = new(Configuration.Default, obuReader.SequenceHeader, obuReader.FrameHeader);
+
+ tileReader.ReadTile(tileSpan, 0);
+
+ int parsedModeInfoCount = 0;
+ int superblockSize = obuReader.SequenceHeader.SuperblockModeInfoSize;
+ for (int row = 0; row < obuReader.FrameHeader.ModeInfoRowCount; row += superblockSize)
+ {
+ for (int column = 0; column < obuReader.FrameHeader.ModeInfoColumnCount; column += superblockSize)
+ {
+ Point superblockPosition = new(column / superblockSize, row / superblockSize);
+ Av1SuperblockInfo superblockInfo = tileReader.FrameInfo.GetSuperblock(superblockPosition);
+ Span modeInfos = superblockInfo.GetModeInfos();
+
+ Assert.Equal(superblockInfo.BlockCount, modeInfos.Length);
+ Assert.DoesNotContain(modeInfos.ToArray(), modeInfo => modeInfo is null);
+ parsedModeInfoCount += modeInfos.Length;
+ }
+ }
+
+ Assert.True(parsedModeInfoCount > 16);
+ }
+
[Theory]
[InlineData(TestImages.Heif.XnConvert, 0x010E, 0x03CC, 18, 16)]
[InlineData(TestImages.Heif.Orange4x4, 0x010E, 0x001d, 21, 1)]