diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1PartitionType.cs b/src/ImageSharp/Formats/Heif/Av1/Av1PartitionType.cs
index 0a6092ce8..11f973a06 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1PartitionType.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1PartitionType.cs
@@ -10,51 +10,139 @@ internal enum Av1PartitionType
///
/// Not partitioned any further.
///
+ ///
+ ///
+ /// ***
+ /// * *
+ /// ***
+ ///
+ ///
None = 0,
///
/// Horizontally split in 2 partitions.
///
+ ///
+ ///
+ /// ***
+ /// * *
+ /// ***
+ /// * *
+ /// ***
+ ///
+ ///
Horizontal = 1,
///
/// Vertically split in 2 partitions.
///
+ ///
+ ///
+ /// *****
+ /// * * *
+ /// *****
+ ///
+ ///
Vertical = 2,
///
/// 4 equally sized partitions.
///
+ ///
+ ///
+ /// *****
+ /// * * *
+ /// *****
+ /// * * *
+ /// *****
+ ///
+ ///
Split = 3,
///
/// Horizontal split and the top partition is split again.
///
+ ///
+ ///
+ /// *****
+ /// * * *
+ /// *****
+ /// * *
+ /// *****
+ ///
+ ///
HorizontalA = 4,
///
/// Horizontal split and the bottom partition is split again.
///
+ ///
+ ///
+ /// *****
+ /// * *
+ /// *****
+ /// * * *
+ /// *****
+ ///
+ ///
HorizontalB = 5,
///
/// Vertical split and the left partition is split again.
///
+ ///
+ ///
+ /// *****
+ /// * * *
+ /// *** *
+ /// * * *
+ /// *****
+ ///
+ ///
VerticalA = 6,
///
- /// Vertical split and the right partitino is split again.
+ /// Vertical split and the right partition is split again.
///
+ ///
+ ///
+ /// *****
+ /// * * *
+ /// * ***
+ /// * * *
+ /// *****
+ ///
+ ///
VerticalB = 7,
///
/// 4:1 horizontal partition.
///
+ ///
+ ///
+ /// ***
+ /// * *
+ /// ***
+ /// * *
+ /// ***
+ /// * *
+ /// ***
+ /// * *
+ /// ***
+ ///
+ ///
Horizontal4 = 8,
///
/// 4:1 vertical partition.
///
+ ///
+ ///
+ /// *********
+ /// * * * * *
+ /// *********
+ ///
+ ///
Vertical4 = 9,
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
index 569f633b1..a66a8ee46 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
@@ -336,13 +336,14 @@ internal class Av1TileDecoder : IAv1TileDecoder
int planesCount = this.SequenceHeader.ColorConfig.PlaneCount;
for (int i = 0; i < planesCount; i++)
{
- bool subX = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingX;
- bool subY = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingY;
+ int subX = (i > 0 && this.SequenceHeader.ColorConfig.SubSamplingX) ? 1 : 0;
+ int subY = (i > 0 && this.SequenceHeader.ColorConfig.SubSamplingY) ? 1 : 0;
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
+ DebugGuard.IsTrue(planeBlockSize != Av1BlockSize.Invalid, nameof(planeBlockSize));
int txsWide = planeBlockSize.GetWidth() >> 2;
int txsHigh = planeBlockSize.GetHeight() >> 2;
- 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 aboveOffset = (partitionInfo.ColumnIndex - this.FrameInfo.TilesInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> subX;
+ int leftOffset = (partitionInfo.RowIndex - this.FrameInfo.TilesInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> subY;
this.aboveNeighborContext.ClearContext(i, aboveOffset, txsWide);
this.leftNeighborContext.ClearContext(i, leftOffset, txsHigh);
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs
index 79b32e92a..7e3506aaa 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs
@@ -71,6 +71,8 @@ public class Av1BlockSizeTests
Av1BlockSize blockSize = (Av1BlockSize)s;
int originalWidth = blockSize.GetWidth();
int originalHeight = blockSize.GetHeight();
+ int halfWidth = originalWidth / 2;
+ int halfHeight = originalHeight / 2;
// Act
Av1BlockSize actualNoNo = blockSize.GetSubsampled(false, false);
@@ -84,18 +86,18 @@ public class Av1BlockSizeTests
if (actualYesNo != Av1BlockSize.Invalid)
{
- Assert.Equal(originalWidth, actualYesNo.GetWidth() * 2);
+ Assert.Equal(halfWidth, actualYesNo.GetWidth());
Assert.Equal(originalHeight, actualYesNo.GetHeight());
}
if (actualNoYes != Av1BlockSize.Invalid)
{
Assert.Equal(originalWidth, actualNoYes.GetWidth());
- Assert.Equal(originalHeight, actualNoYes.GetHeight() * 2);
+ Assert.Equal(halfHeight, actualNoYes.GetHeight());
}
- Assert.Equal(originalWidth, actualYesYes.GetWidth() * 2);
- Assert.Equal(originalHeight, actualYesYes.GetHeight() * 2);
+ Assert.Equal(halfWidth, actualYesYes.GetWidth());
+ Assert.Equal(halfHeight, actualYesYes.GetHeight());
}
public static TheoryData GetAllSizes()
@@ -113,7 +115,7 @@ public class Av1BlockSizeTests
{
int width = blockSize.GetWidth();
int height = blockSize.GetHeight();
- int ratio = width > height ? width / height : height / width;
+ int ratio = width >= height ? width / height : -height / width;
return ratio;
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PartitionTypeTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PartitionTypeTests.cs
new file mode 100644
index 000000000..e7b073264
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PartitionTypeTests.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Heif.Av1;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
+
+[Trait("Format", "Avif")]
+public class Av1PartitionTypeTests
+{
+ [Theory]
+ [MemberData(nameof(GetAllCombinations))]
+ internal void GetSubBlockSizeReturnsCorrectRatio(int t, int s)
+ {
+ // Assign
+ Av1PartitionType partitionType = (Av1PartitionType)t;
+ Av1BlockSize blockSize = (Av1BlockSize)s;
+ int expectedRatio = partitionType switch
+ {
+ Av1PartitionType.None or Av1PartitionType.Split => 1,
+ Av1PartitionType.HorizontalA or Av1PartitionType.HorizontalB or Av1PartitionType.Horizontal => 2,
+ Av1PartitionType.VerticalA or Av1PartitionType.VerticalB or Av1PartitionType.Vertical => -2,
+ Av1PartitionType.Horizontal4 => 4,
+ Av1PartitionType.Vertical4 => -4,
+ _ => -1
+ };
+
+ // Act
+ Av1BlockSize subBlockSize = partitionType.GetBlockSubSize(blockSize);
+
+ // Assert
+ if (subBlockSize != Av1BlockSize.Invalid)
+ {
+ int actualRatio = GetRatio(subBlockSize);
+ Assert.Equal(expectedRatio, actualRatio);
+ }
+ }
+
+ public static TheoryData GetAllCombinations()
+ {
+ TheoryData combinations = [];
+ for (int t = 0; t <= (int)Av1PartitionType.Vertical4; t++)
+ {
+ for (int s = 0; s < (int)Av1BlockSize.AllSizes; s++)
+ {
+ combinations.Add(t, s);
+ }
+ }
+
+ return combinations;
+ }
+
+ private static int GetRatio(Av1BlockSize blockSize)
+ {
+ int width = blockSize.GetWidth();
+ int height = blockSize.GetHeight();
+ int ratio = width >= height ? width / height : -height / width;
+ return ratio;
+ }
+}