diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
index 9d6b15e6f4..fe1fee2091 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
@@ -11,26 +11,31 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
internal static class Av1SymbolContextHelper
{
public static readonly int[][] ExtendedTransformIndices = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 3, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 5, 6, 4, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0],
- [3, 4, 5, 8, 6, 7, 9, 10, 11, 0, 1, 2, 0, 0, 0, 0],
- [7, 8, 9, 12, 10, 11, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // DCT only
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // Inter set 3
+ [1, 3, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // Intra set 2
+ [1, 5, 6, 4, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0], // Intra set 1
+ [3, 4, 5, 8, 6, 7, 9, 10, 11, 0, 1, 2, 0, 0, 0, 0], // Inter set 2
+ [7, 8, 9, 12, 10, 11, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6], // All 16, inter set 1
];
- public static readonly int[][] ExtendedTransformIndicesInverse = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [9, 0, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [9, 0, 10, 11, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [9, 10, 11, 0, 1, 2, 4, 5, 3, 6, 7, 8, 0, 0, 0, 0],
- [9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 4, 5, 3, 6, 7, 8],
+ // Maps tx set types to the distribution indices. INTRA values only
+ private static readonly int[] ExtendedTransformSetToIndex = [0, -1, 2, 1, -1, -1];
+
+ ///
+ /// Section 5.11.48: Transform type syntax
+ ///
+ public static readonly Av1TransformType[][] ExtendedTransformInverse = [
+ [Av1TransformType.DctDct], // DCT only
+ [], // Inter set 3
+ [Av1TransformType.Identity, Av1TransformType.DctDct, Av1TransformType.AdstAdst, Av1TransformType.AdstDct, Av1TransformType.DctAdst], // Intra set 2
+ [Av1TransformType.Identity, Av1TransformType.DctDct, Av1TransformType.VerticalDct, Av1TransformType.HorizontalDct, Av1TransformType.AdstAdst, Av1TransformType.AdstDct, Av1TransformType.DctAdst], // Intra set 1
+ [], // Inter set 2
+ [], // All 16, inter set 1
];
public static readonly int[] EndOfBlockOffsetBits = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public static readonly int[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513];
- private static readonly int[] TransformCountInSet = [1, 2, 5, 7, 12, 16];
private static readonly byte[] EndOfBlockToPositionSmall = [
0, 1, 2, // 0-2
3, 3, // 3-4
@@ -59,9 +64,6 @@ internal static class Av1SymbolContextHelper
11 // 513-
];
- // Maps tx set types to the indices. INTRA values only
- private static readonly int[] ExtendedTransformSetToIndex = [0, -1, 2, 1, -1, -1];
-
internal static Av1TransformSize GetTransformSizeContext(Av1TransformSize originalSize)
=> (Av1TransformSize)(((int)originalSize.GetSquareSize() + (int)originalSize.GetSquareUpSize() + 1) >> 1);
@@ -239,11 +241,11 @@ internal static class Av1SymbolContextHelper
if (useReducedSet)
{
- return Av1TransformSetType.Dtt4Identity;
+ return Av1TransformSetType.IntraSet2;
}
Av1TransformSize squareSize = transformSize.GetSquareSize();
- return squareSize == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct;
+ return squareSize == Av1TransformSize.Size16x16 ? Av1TransformSetType.IntraSet2 : Av1TransformSetType.IntraSet1;
}
internal static Av1TransformType ConvertIntraModeToTransformType(Av1BlockModeInfo modeInfo, Av1PlaneType planeType)
@@ -299,7 +301,7 @@ internal static class Av1SymbolContextHelper
///
/// SVT: get_ext_tx_types
///
- internal static int GetExtendedTransformTypeCount(Av1TransformSetType setType) => TransformCountInSet[(int)setType];
+ internal static int GetExtendedTransformTypeCount(Av1TransformSetType setType) => ExtendedTransformInverse[(int)setType].Length;
///
/// SVT: get_ext_tx_set
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
index ca46def229..182475719b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
@@ -250,21 +250,16 @@ internal ref struct Av1SymbolDecoder
// Ignoring INTER blocks here, as these should not end up here.
// int inter_block = is_inter_block_dec(mbmi);
Av1TransformSetType transformSetType = Av1SymbolContextHelper.GetExtendedTransformSetType(transformSize, useReducedTransformSet);
- if (Av1SymbolContextHelper.GetExtendedTransformTypeCount(transformSetType) > 1 && baseQIndex > 0)
+ if (transformSetType > Av1TransformSetType.DctOnly && baseQIndex > 0)
{
int extendedSet = Av1SymbolContextHelper.GetExtendedTransformSet(transformSetType);
-
- // eset == 0 should correspond to a set with only DCT_DCT and
- // there is no need to read the tx_type
- Guard.IsFalse(extendedSet == 0, nameof(extendedSet), string.Empty);
-
Av1TransformSize squareTransformSize = transformSize.GetSquareSize();
Av1PredictionMode intraMode = useFilterIntra
? filterIntraMode.ToIntraDirection()
: intraDirection;
ref Av1SymbolReader r = ref this.reader;
int symbol = r.ReadSymbol(this.intraExtendedTransform[extendedSet][(int)squareTransformSize][(int)intraMode]);
- transformType = (Av1TransformType)Av1SymbolContextHelper.ExtendedTransformIndicesInverse[(int)transformSetType][symbol];
+ transformType = Av1SymbolContextHelper.ExtendedTransformInverse[(int)transformSetType][symbol];
}
return transformType;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs
index f25d1cbf5e..627a14e7b0 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs
@@ -6,32 +6,35 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
internal enum Av1TransformSetType
{
///
- /// DCT only.
+ /// Allowed transforms: DCT only.
///
DctOnly,
///
- /// DCT + Identity only
+ /// Allowed transforms: DCT + Identity only
///
- DctIdentity,
+ InterSet3,
///
- /// Discrete Trig transforms w/o flip (4) + Identity (1)
+ /// Allowed transforms: Discrete Trig transforms w/o flip (4) + Identity (1)
///
- Dtt4Identity,
+ /// Referenced in spec as TX_SET_INTRA_2.
+ IntraSet2,
///
- /// Discrete Trig transforms w/o flip (4) + Identity (1) + 1D Hor/vert DCT (2)
+ /// Allowed transforms: Discrete Trig transforms w/o flip (4) + Identity (1) + 1D Hor/vert DCT (2)
///
- Dtt4Identity1dDct,
+ /// Referenced in spec as TX_SET_INTRA_1.
+ IntraSet1,
///
- /// Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver DCT (2)
+ /// Allowed transforms: Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver DCT (2)
///
- Dtt9Identity1dDct,
+ InterSet2,
///
- /// Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver (6)
+ /// Allowed transforms: Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver (6)
///
- All16
+ InterSet1,
+ AllSets
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
index 78028fcf64..f33c5d5e94 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
@@ -601,9 +601,28 @@ public class Av1EntropyTests
TheoryData result = [];
for (Av1TransformSize transformSize = Av1TransformSize.Size4x4; transformSize < Av1TransformSize.AllSizes; transformSize++)
{
- // TODO: Figure out why larger sizes don't round trip correctly.
+ if (transformSize == Av1TransformSize.Size16x16)
+ {
+ for (Av1PredictionMode intraDirection = Av1PredictionMode.IntraModeStart; intraDirection < Av1PredictionMode.IntraModeEnd; intraDirection++)
+ {
+ result.Add((int)transformSize, (int)Av1FilterIntraMode.AllFilterIntraModes, (int)intraDirection);
+ }
+
+ if (transformSize == Av1TransformSize.Size16x16)
+ {
+ result.Add((int)transformSize, 0, 0);
+ result.Add((int)transformSize, 1, 1);
+ result.Add((int)transformSize, 2, 2);
+ result.Add((int)transformSize, 3, 6);
+ result.Add((int)transformSize, 4, 0);
+ }
+
+ continue;
+ }
+
if (transformSize.GetSquareSize() >= Av1TransformSize.Size16x16 || transformSize is Av1TransformSize.Size32x8 or Av1TransformSize.Size8x32)
{
+ // DctOnly, doesn't make sense to test.
continue;
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
index 2a7bf4e68a..2dd7b76d57 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
@@ -38,8 +38,8 @@ public class Av1SymbolContextTests
Av1TransformSetType transformSetType = (Av1TransformSetType)setType;
// Act
- int transformType = Av1SymbolContextHelper.ExtendedTransformIndicesInverse[(int)transformSetType][index];
- int actualIndex = Av1SymbolContextHelper.ExtendedTransformIndices[(int)transformSetType][transformType];
+ Av1TransformType transformType = Av1SymbolContextHelper.ExtendedTransformInverse[(int)transformSetType][index];
+ int actualIndex = Av1SymbolContextHelper.ExtendedTransformIndices[(int)transformSetType][(int)transformType];
// Assert
Assert.Equal(actualIndex, index);
@@ -66,10 +66,10 @@ public class Av1SymbolContextTests
public static TheoryData GetExtendedTransformIndicesData()
{
TheoryData result = [];
- for (Av1TransformSetType setType = Av1TransformSetType.DctOnly; setType <= Av1TransformSetType.All16; setType++)
+ for (Av1TransformSetType setType = Av1TransformSetType.DctOnly; setType < Av1TransformSetType.AllSets; setType++)
{
int count = Av1SymbolContextHelper.GetExtendedTransformTypeCount(setType);
- for (int index = 1; index < count; index++)
+ for (int index = 0; index < count; index++)
{
result.Add((int)setType, index);
}