diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
index c70c189a55..7ad04f6bba 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
@@ -9,7 +9,7 @@ internal class ObuColorConfig
public int ChannelCount { get; set; }
- public bool Monochrome { get; set; }
+ public bool IsMonochrome { get; set; }
public ObuColorPrimaries ColorPrimaries { get; set; }
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
index 2742264498..d3d2194a2e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
@@ -113,4 +113,9 @@ internal static class ObuConstants
/// Maximum magnitude of AngleDeltaY and AngleDeltaUV.
///
public const int MaxAngleDelta = 3;
+
+ ///
+ /// Number of segments allowed in segmentation map.
+ ///
+ public const int MaxSegments = 8;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
index 756f71f1f9..fcae54d647 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
@@ -263,8 +263,8 @@ internal class ObuReader
sequenceHeader.EnableDualFilter = false;
sequenceHeader.OrderHintInfo.EnableJointCompound = false;
sequenceHeader.OrderHintInfo.EnableReferenceFrameMotionVectors = false;
- sequenceHeader.SequenceForceScreenContentTools = 2;
- sequenceHeader.SequenceForceIntegerMotionVector = 2;
+ sequenceHeader.ForceScreenContentTools = 2;
+ sequenceHeader.ForceIntegerMotionVector = 2;
sequenceHeader.OrderHintInfo.OrderHintBits = 0;
// Video related flags removed
@@ -280,13 +280,13 @@ internal class ObuReader
{
ObuColorConfig colorConfig = new();
ReadBitDepth(ref reader, colorConfig, sequenceHeader);
- colorConfig.Monochrome = false;
+ colorConfig.IsMonochrome = false;
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High)
{
- colorConfig.Monochrome = reader.ReadBoolean();
+ colorConfig.IsMonochrome = reader.ReadBoolean();
}
- colorConfig.ChannelCount = colorConfig.Monochrome ? 1 : 3;
+ colorConfig.ChannelCount = colorConfig.IsMonochrome ? 1 : 3;
colorConfig.IsColorDescriptionPresent = reader.ReadBoolean();
colorConfig.ColorPrimaries = ObuColorPrimaries.Unspecified;
colorConfig.TransferCharacteristics = ObuTransferCharacteristics.Unspecified;
@@ -303,7 +303,7 @@ internal class ObuReader
colorConfig.SubSamplingY = false;
colorConfig.ChromaSamplePosition = ObuChromoSamplePosition.Unknown;
colorConfig.HasSeparateUvDelta = false;
- if (colorConfig.Monochrome)
+ if (colorConfig.IsMonochrome)
{
colorConfig.ColorRange = reader.ReadBoolean();
colorConfig.SubSamplingX = true;
@@ -669,7 +669,7 @@ internal class ObuReader
}
frameInfo.DisableCdfUpdate = reader.ReadBoolean();
- frameInfo.AllowScreenContentTools = sequenceHeader.SequenceForceScreenContentTools == 1;
+ frameInfo.AllowScreenContentTools = sequenceHeader.ForceScreenContentTools == 1;
if (frameInfo.AllowScreenContentTools)
{
frameInfo.AllowScreenContentTools = reader.ReadBoolean();
@@ -677,13 +677,13 @@ internal class ObuReader
if (frameInfo.AllowScreenContentTools)
{
- if (sequenceHeader.SequenceForceIntegerMotionVector == 1)
+ if (sequenceHeader.ForceIntegerMotionVector == 1)
{
frameInfo.ForceIntegerMotionVector = reader.ReadBoolean();
}
else
{
- frameInfo.ForceIntegerMotionVector = sequenceHeader.SequenceForceIntegerMotionVector != 0;
+ frameInfo.ForceIntegerMotionVector = sequenceHeader.ForceIntegerMotionVector != 0;
}
}
else
@@ -899,7 +899,7 @@ internal class ObuReader
}
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature)
- => segmentationParameters.Enabled && segmentationParameters.FeatureEnabled[segmentId, (int)feature];
+ => segmentationParameters.Enabled && segmentationParameters.IsFeatureActive(segmentId, feature);
private static int GetQIndex(ObuSegmentationParameters segmentationParameters, int segmentId, int baseQIndex)
{
@@ -919,7 +919,7 @@ internal class ObuReader
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
- int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3;
+ int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = reader.BitPosition;
ReadUncompressedFrameHeader(ref reader, decoder, header, planeCount);
if (trailingBit)
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationFeature.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationFeature.cs
deleted file mode 100644
index 1cfade4063..0000000000
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationFeature.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
-
-internal enum ObuSegmentationFeature
-{
- None = 0,
- LevelSkip,
-}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
index be428efc32..c74a698b2f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
@@ -17,6 +17,6 @@ internal class ObuSegmentationParameters
public int LastActiveSegmentId { get; internal set; }
- internal bool IsFeatureActive(int segmentId, ObuSegmentationFeature feature)
+ internal bool IsFeatureActive(int segmentId, ObuSegmentationLevelFeature feature)
=> this.FeatureEnabled[segmentId, (int)feature];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
index 8bb6a11215..0b970091af 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
@@ -55,9 +55,9 @@ internal class ObuSequenceHeader
public bool EnableDualFilter { get; set; }
- public int SequenceForceIntegerMotionVector { get; set; }
+ public int ForceIntegerMotionVector { get; set; }
- public int SequenceForceScreenContentTools { get; set; }
+ public int ForceScreenContentTools { get; set; }
public bool EnableSuperResolution { get; set; }
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
index 80b0675850..45a05653d8 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
@@ -109,11 +109,11 @@ internal class ObuWriter
WriteBitDepth(ref writer, colorConfig, sequenceHeader);
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High)
{
- writer.WriteBoolean(colorConfig.Monochrome);
+ writer.WriteBoolean(colorConfig.IsMonochrome);
}
writer.WriteBoolean(false); // colorConfig.IsColorDescriptionPresent
- if (colorConfig.Monochrome)
+ if (colorConfig.IsMonochrome)
{
writer.WriteBoolean(colorConfig.ColorRange);
return;
@@ -321,7 +321,7 @@ internal class ObuWriter
if (frameInfo.AllowScreenContentTools)
{
- if (sequenceHeader.SequenceForceIntegerMotionVector == 1)
+ if (sequenceHeader.ForceIntegerMotionVector == 1)
{
writer.WriteBoolean(frameInfo.ForceIntegerMotionVector);
}
@@ -448,7 +448,7 @@ internal class ObuWriter
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
- int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3;
+ int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = writer.BitPosition;
WriteUncompressedFrameHeader(ref writer, sequenceHeader, frameInfo, planeCount);
if (writeTrailingBits)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1BlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1BlockModeInfo.cs
new file mode 100644
index 0000000000..27ba271ab3
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1BlockModeInfo.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal class Av1BlockModeInfo
+{
+ private int[] paletteSize;
+
+ public Av1BlockModeInfo(int numPlanes, Av1BlockSize blockSize)
+ {
+ this.BlockSize = blockSize;
+ this.AngleDelta = new int[numPlanes];
+ this.paletteSize = new int[numPlanes - 1];
+ this.FilterIntraModeInfo = new();
+ }
+
+ public Av1BlockSize BlockSize { get; }
+
+ public Av1PredictionMode YMode { get; set; }
+
+ public bool Skip { get; set; }
+
+ public Av1PartitionType PartitionType { get; }
+
+ public bool SkipMode { get; set; }
+
+ public int SegmentId { get; set; }
+
+ public Av1PredictionMode UvMode { get; set; }
+
+ public bool UseUltraBlockCopy { get; set; }
+
+ public int ChromaFromLumaAlphaIndex { get; set; }
+
+ public int ChromaFromLumaAlphaSign { get; set; }
+
+ public int[] AngleDelta { get; set; }
+
+ public Size IndexInSuperblock { get; set; }
+
+ public Av1IntraFilterModeInfo FilterIntraModeInfo { get; internal set; }
+
+ public int GetPaletteSize(Av1PlaneType planeType) => this.paletteSize[(int)planeType];
+
+ public void SetPaletteSizes(int ySize, int uvSize) => this.paletteSize = [ySize, uvSize];
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs
index 3b9f555ef5..cb65bfafe4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs
@@ -126,4 +126,49 @@ internal static class Av1DefaultDistributions
public static Av1Distribution DeltaQuantizerAbsolute => new(28160, 32120, 32677);
public static Av1Distribution[] SegmentId => [new(128 * 128), new(128 * 128), new(128 * 128)];
+
+ public static Av1Distribution[][] KeyFrameYMode =>
+ [
+ [
+ new(15588, 17027, 19338, 20218, 20682, 21110, 21825, 23244, 24189, 28165, 29093, 30466),
+ new(12016, 18066, 19516, 20303, 20719, 21444, 21888, 23032, 24434, 28658, 30172, 31409),
+ new(10052, 10771, 22296, 22788, 23055, 23239, 24133, 25620, 26160, 29336, 29929, 31567),
+ new(14091, 15406, 16442, 18808, 19136, 19546, 19998, 22096, 24746, 29585, 30958, 32462),
+ new(12122, 13265, 15603, 16501, 18609, 20033, 22391, 25583, 26437, 30261, 31073, 32475),
+ ], [
+ new(10023, 19585, 20848, 21440, 21832, 22760, 23089, 24023, 25381, 29014, 30482, 31436),
+ new(5983, 24099, 24560, 24886, 25066, 25795, 25913, 26423, 27610, 29905, 31276, 31794),
+ new(7444, 12781, 20177, 20728, 21077, 21607, 22170, 23405, 24469, 27915, 29090, 30492),
+ new(8537, 14689, 15432, 17087, 17408, 18172, 18408, 19825, 24649, 29153, 31096, 32210),
+ new(7543, 14231, 15496, 16195, 17905, 20717, 21984, 24516, 26001, 29675, 30981, 31994)
+ ], [
+ new(12613, 13591, 21383, 22004, 22312, 22577, 23401, 25055, 25729, 29538, 30305, 32077),
+ new(9687, 13470, 18506, 19230, 19604, 20147, 20695, 22062, 23219, 27743, 29211, 30907),
+ new(6183, 6505, 26024, 26252, 26366, 26434, 27082, 28354, 28555, 30467, 30794, 32086),
+ new(10718, 11734, 14954, 17224, 17565, 17924, 18561, 21523, 23878, 28975, 30287, 32252),
+ new(9194, 9858, 16501, 17263, 18424, 19171, 21563, 25961, 26561, 30072, 30737, 32463)
+ ], [
+ new(12602, 14399, 15488, 18381, 18778, 19315, 19724, 21419, 25060, 29696, 30917, 32409),
+ new(8203, 13821, 14524, 17105, 17439, 18131, 18404, 19468, 25225, 29485, 31158, 32342),
+ new(8451, 9731, 15004, 17643, 18012, 18425, 19070, 21538, 24605, 29118, 30078, 32018),
+ new(7714, 9048, 9516, 16667, 16817, 16994, 17153, 18767, 26743, 30389, 31536, 32528),
+ new(8843, 10280, 11496, 15317, 16652, 17943, 19108, 22718, 25769, 29953, 30983, 32485)
+ ], [
+ new(12578, 13671, 15979, 16834, 19075, 20913, 22989, 25449, 26219, 30214, 31150, 32477),
+ new(9563, 13626, 15080, 15892, 17756, 20863, 22207, 24236, 25380, 29653, 31143, 32277),
+ new(8356, 8901, 17616, 18256, 19350, 20106, 22598, 25947, 26466, 29900, 30523, 32261),
+ new(10835, 11815, 13124, 16042, 17018, 18039, 18947, 22753, 24615, 29489, 30883, 32482),
+ new(7618, 8288, 9859, 10509, 15386, 18657, 22903, 28776, 29180, 31355, 31802, 32593)
+ ]
+ ];
+
+ public static Av1Distribution FilterIntraMode => new(8949, 12776, 17211, 29558);
+
+ public static Av1Distribution[] FilterIntra =>
+ [
+ new(4621), new(6743), new(5893), new(7866), new(12551), new(9394),
+ new(12408), new(14301), new(12756), new(22343), new(16384), new(16384),
+ new(16384), new(16384), new(16384), new(16384), new(12770), new(10368),
+ new(20229), new(18101), new(16384), new(16384)
+ ];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1FilterIntraMode.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1FilterIntraMode.cs
new file mode 100644
index 0000000000..b93b8522df
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1FilterIntraMode.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal enum Av1FilterIntraMode
+{
+ DC,
+ Vertical,
+ Horizontal,
+ Directional157,
+ Paeth
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1IntraFilterModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1IntraFilterModeInfo.cs
new file mode 100644
index 0000000000..6adb43dabd
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1IntraFilterModeInfo.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal class Av1IntraFilterModeInfo
+{
+ public bool UseFilterIntra { get; set; }
+
+ public Av1FilterIntraMode Mode { get; set; }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs
index f783c9cdda..e213005dff 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs
@@ -5,7 +5,13 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1ParseAboveContext
{
- public int PartitionWidth { get; internal set; }
+ public int PartitionWidth { get; set; }
- internal void Clear() => throw new NotImplementedException();
+ public int[][] AboveContext { get; set; } = [];
+
+ internal void Clear(int startColumnIndex, int endColumnIndex)
+ {
+ this.PartitionWidth = -1;
+ this.AboveContext = [];
+ }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs
index 7f521c273e..01219dcfcb 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs
@@ -5,7 +5,9 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1ParseLeftContext
{
- public int PartitionHeight { get; internal set; }
+ public int PartitionHeight { get; set; }
+
+ public int[][] LeftContext { get; set; } = [];
internal void Clear() => throw new NotImplementedException();
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PartitionInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PartitionInfo.cs
new file mode 100644
index 0000000000..d6dc9ffa65
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PartitionInfo.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal class Av1PartitionInfo
+{
+ public Av1PartitionInfo(Av1BlockModeInfo modeInfo, Av1SuperblockInfo superblockInfo, bool isChroma, Av1PartitionType partitionType)
+ {
+ this.ModeInfo = modeInfo;
+ this.SuperblockInfo = superblockInfo;
+ this.IsChroma = isChroma;
+ this.PartitionType = partitionType;
+ this.CdefStrength = [];
+ this.ReferenceFrame = [-1, -1];
+ }
+
+ public Av1BlockModeInfo ModeInfo { get; }
+
+ public Av1SuperblockInfo SuperblockInfo { get; }
+
+ public bool IsChroma { get; }
+
+ public Av1PartitionType PartitionType { get; }
+
+ public bool AvailableUp { get; set; }
+
+ public bool AvailableLeft { get; set; }
+
+ public bool AvailableUpForChroma { get; set; }
+
+ public bool AvailableLeftForChroma { get; set; }
+
+ public int ColumnIndex { get; set; }
+
+ public int RowIndex { get; set; }
+
+ public Av1BlockModeInfo? AboveModeInfo { get; set; }
+
+ public Av1BlockModeInfo? LeftModeInfo { get; set; }
+
+ public int[][] CdefStrength { get; set; }
+
+ public int[] ReferenceFrame { get; set; }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PlaneType.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PlaneType.cs
new file mode 100644
index 0000000000..d41e4e6e10
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PlaneType.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal enum Av1PlaneType : int
+{
+ Y,
+ Uv
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SuperblockInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SuperblockInfo.cs
new file mode 100644
index 0000000000..fccc6f6079
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SuperblockInfo.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
+
+internal class Av1SuperblockInfo
+{
+ public int[] SuperblockDeltaQ { get; internal set; } = [];
+
+ public Av1BlockModeInfo GetModeInfo(int rowIndex, int columnIndex) => throw new NotImplementedException();
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs
index faec9c8214..3d76994391 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs
@@ -7,15 +7,19 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal ref struct Av1SymbolDecoder
{
+ private static readonly int[] IntraModeContext = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
+
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
- private readonly Av1Distribution[] frameYMode = Av1DefaultDistributions.FrameYMode;
+ private readonly Av1Distribution[][] keyFrameYMode = Av1DefaultDistributions.KeyFrameYMode;
private readonly Av1Distribution[][] uvMode = Av1DefaultDistributions.UvMode;
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip;
private readonly Av1Distribution deltaLoopFilterAbsolute = Av1DefaultDistributions.DeltaLoopFilterAbsolute;
private readonly Av1Distribution deltaQuantizerAbsolute = Av1DefaultDistributions.DeltaQuantizerAbsolute;
private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId;
private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta;
+ private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode;
+ private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra;
private Av1SymbolReader reader;
public Av1SymbolDecoder(Span tileData) => this.reader = new Av1SymbolReader(tileData);
@@ -76,17 +80,31 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(distribution) > 0;
}
- public Av1PredictionMode ReadIntraFrameYMode(Av1BlockSize blockSize)
+ public Av1PredictionMode ReadYMode(Av1BlockModeInfo? aboveModeInfo, Av1BlockModeInfo? leftModeInfo)
{
ref Av1SymbolReader r = ref this.reader;
- return (Av1PredictionMode)r.ReadSymbol(this.frameYMode[(int)blockSize]);
+ Av1PredictionMode aboveMode = Av1PredictionMode.DC;
+ if (aboveModeInfo != null)
+ {
+ aboveMode = aboveModeInfo.YMode;
+ }
+
+ Av1PredictionMode leftMode = Av1PredictionMode.DC;
+ if (leftModeInfo != null)
+ {
+ leftMode = leftModeInfo.YMode;
+ }
+
+ int aboveContext = IntraModeContext[(int)aboveMode];
+ int leftContext = IntraModeContext[(int)leftMode];
+ return (Av1PredictionMode)r.ReadSymbol(this.keyFrameYMode[aboveContext][leftContext]);
}
- public Av1PredictionMode ReadUvMode(Av1BlockSize blockSize, bool chromaFromLumaAllowed)
+ public Av1PredictionMode ReadIntraModeUv(Av1PredictionMode mode, bool chromaFromLumaAllowed)
{
int chromaForLumaIndex = chromaFromLumaAllowed ? 1 : 0;
ref Av1SymbolReader r = ref this.reader;
- return (Av1PredictionMode)r.ReadSymbol(this.uvMode[chromaForLumaIndex][(int)blockSize]);
+ return (Av1PredictionMode)r.ReadSymbol(this.uvMode[chromaForLumaIndex][(int)mode]);
}
public bool ReadSkip(int ctx)
@@ -119,9 +137,17 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.angleDelta[((int)mode) - 1]);
}
- public bool ReadUseFilterUltra() => throw new NotImplementedException();
+ public bool ReadUseFilterUltra(Av1BlockSize blockSize)
+ {
+ ref Av1SymbolReader r = ref this.reader;
+ return r.ReadSymbol(this.filterIntra[(int)blockSize]) > 0;
+ }
- public object ReadFilterUltraMode() => throw new NotImplementedException();
+ public Av1FilterIntraMode ReadFilterUltraMode()
+ {
+ ref Av1SymbolReader r = ref this.reader;
+ return (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
+ }
private static uint GetElementProbability(Av1Distribution probability, Av1PartitionType element)
=> probability[(int)element - 1] - probability[(int)element];
diff --git a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs
index 6c8a6bbff2..00812e521b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs
@@ -18,37 +18,16 @@ internal class Av1TileDecoder : IAv1TileDecoder
private bool[][][] blockDecoded = [];
private int[][] referenceSgrXqd = [];
private int[][][] referenceLrWiener = [];
- private bool availableUp;
- private bool availableLeft;
- private bool availableUpForChroma;
- private bool availableLeftForChroma;
private Av1ParseAboveContext aboveContext = new();
private Av1ParseLeftContext leftContext = new();
- private bool skip;
- private bool readDeltas;
private int currentQuantizerIndex;
- private Av1PredictionMode[][] yModes = [];
- private Av1PredictionMode yMode = Av1PredictionMode.DC;
- private Av1PredictionMode uvMode = Av1PredictionMode.DC;
- private Av1PredictionMode[][] uvModes = [];
- private object[] referenceFrame = [];
- private object[][][] referenceFrames = [];
- private int paletteSizeY;
- private int paletteSizeUv;
- private object[][] aboveLevelContext = [];
- private object[][] aboveDcContext = [];
- private object[][] leftLevelContext = [];
- private object[][] leftDcContext = [];
- private Av1TransformSize transformSize = Av1TransformSize.Size4x4;
- private object filterUltraMode = -1;
- private int angleDeltaY;
- private int angleDeltaUv;
- private bool lossless;
+ private int[][] aboveLevelContext = [];
+ private int[][] aboveDcContext = [];
+ private int[][] leftLevelContext = [];
+ private int[][] leftDcContext = [];
private int[][] segmentIds = [];
private int maxLumaWidth;
private int maxLumaHeight;
- private int segmentId;
- private int[][] cdefIndex = [];
private int deltaLoopFilterResolution = -1;
private int deltaQuantizerResolution = -1;
@@ -74,9 +53,10 @@ internal class Av1TileDecoder : IAv1TileDecoder
public void DecodeTile(Span tileData, int tileNum)
{
Av1SymbolDecoder reader = new(tileData);
- int tileRowIndex = tileNum / this.TileInfo.TileColumnCount;
int tileColumnIndex = tileNum % this.TileInfo.TileColumnCount;
- this.aboveContext.Clear();
+ int tileRowIndex = tileNum / this.TileInfo.TileColumnCount;
+
+ this.aboveContext.Clear(this.TileInfo.TileColumnStartModeInfo[tileColumnIndex], this.TileInfo.TileColumnStartModeInfo[tileColumnIndex - 1]);
this.ClearLoopFilterDelta();
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
this.referenceSgrXqd = new int[planesCount][];
@@ -111,12 +91,13 @@ internal class Av1TileDecoder : IAv1TileDecoder
// this.ClearCdef(row, column);
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size);
this.ReadLoopRestoration(row, column, superBlockSize);
- this.DecodePartition(ref reader, row, column, superBlockSize);
+ this.ParsePartition(ref reader, row, column, superBlockSize);
}
}
}
- private void ClearLoopFilterDelta() => this.deltaLoopFilter = new int[4];
+ private void ClearLoopFilterDelta()
+ => this.deltaLoopFilter = new int[4];
private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size)
{
@@ -169,16 +150,17 @@ internal class Av1TileDecoder : IAv1TileDecoder
// TODO: Implement
}
- private void DecodePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
+ private void ParsePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
{
+ Av1SuperblockInfo superblockInfo = new();
if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] ||
columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex])
{
return;
}
- this.availableUp = this.IsInside(rowIndex - 1, columnIndex);
- this.availableLeft = this.IsInside(rowIndex, columnIndex - 1);
+ bool availableUp = this.IsInside(rowIndex - 1, columnIndex);
+ bool availableLeft = this.IsInside(rowIndex, columnIndex - 1);
int block4x4Size = blockSize.Get4x4WideCount();
int halfBlock4x4Size = block4x4Size >> 1;
int quarterBlock4x4Size = halfBlock4x4Size >> 2;
@@ -212,82 +194,86 @@ internal class Av1TileDecoder : IAv1TileDecoder
switch (partitionType)
{
case Av1PartitionType.Split:
- this.DecodePartition(ref reader, rowIndex, columnIndex, subSize);
- this.DecodePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize);
- this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize);
- this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize);
+ this.ParsePartition(ref reader, rowIndex, columnIndex, subSize);
+ this.ParsePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize);
+ this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize);
+ this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize);
break;
case Av1PartitionType.None:
- this.DecodeBlock(ref reader, rowIndex, columnIndex, subSize);
+ this.ParseBlock(ref reader, rowIndex, columnIndex, subSize, superblockInfo, Av1PartitionType.None);
break;
default:
throw new NotImplementedException($"Partition type: {partitionType} is not supported.");
}
}
- private void DecodeBlock(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
+ private void ParseBlock(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1PartitionType partitionType)
{
int block4x4Width = blockSize.Get4x4WideCount();
int block4x4Height = blockSize.Get4x4HighCount();
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
- bool hasChroma = planesCount > 1;
- if (block4x4Height == 1 && this.SequenceHeader.ColorConfig.SubSamplingY && (rowIndex & 0x1) == 0)
- {
- hasChroma = false;
- }
-
- if (block4x4Width == 1 && this.SequenceHeader.ColorConfig.SubSamplingX && (columnIndex & 0x1) == 0)
- {
- hasChroma = false;
- }
-
- this.availableUp = this.IsInside(rowIndex - 1, columnIndex);
- this.availableLeft = this.IsInside(rowIndex, columnIndex - 1);
- this.availableUpForChroma = this.availableUp;
- this.availableLeftForChroma = this.availableLeft;
+ Av1BlockModeInfo blockModeInfo = new(planesCount, blockSize);
+ bool hasChroma = this.HasChroma(rowIndex, columnIndex, blockSize);
+ Av1PartitionInfo partitionInfo = new(blockModeInfo, superblockInfo, hasChroma, partitionType);
+ partitionInfo.ColumnIndex = columnIndex;
+ partitionInfo.RowIndex = rowIndex;
+ partitionInfo.AvailableUp = this.IsInside(rowIndex - 1, columnIndex);
+ partitionInfo.AvailableLeft = this.IsInside(rowIndex, columnIndex - 1);
+ partitionInfo.AvailableUpForChroma = partitionInfo.AvailableUp;
+ partitionInfo.AvailableLeftForChroma = partitionInfo.AvailableLeft;
if (hasChroma)
{
if (this.SequenceHeader.ColorConfig.SubSamplingY && block4x4Height == 1)
{
- this.availableUpForChroma = this.IsInside(rowIndex - 2, columnIndex);
+ partitionInfo.AvailableUpForChroma = this.IsInside(rowIndex - 2, columnIndex);
}
if (this.SequenceHeader.ColorConfig.SubSamplingX && block4x4Width == 1)
{
- this.availableLeftForChroma = this.IsInside(rowIndex, columnIndex - 2);
+ partitionInfo.AvailableLeftForChroma = this.IsInside(rowIndex, columnIndex - 2);
}
}
- this.ReadModeInfo(ref reader, rowIndex, columnIndex, blockSize);
- this.ReadPaletteTokens(ref reader);
- ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
- if (this.skip)
+ if (partitionInfo.AvailableUp)
{
- this.ResetBlockContext(rowIndex, columnIndex, blockSize);
+ partitionInfo.AboveModeInfo = superblockInfo.GetModeInfo(rowIndex - 1, columnIndex);
}
- // bool isCompound = false;
- for (int y = 0; y < block4x4Height; y++)
+ if (partitionInfo.AvailableLeft)
{
- for (int x = 0; x < block4x4Width; x++)
- {
- this.yModes[rowIndex + y][columnIndex + x] = this.yMode;
- if (this.referenceFrame[0] == (object)ObuFrameType.IntraOnlyFrame && hasChroma)
- {
- this.uvModes[rowIndex + y][columnIndex + x] = this.uvMode;
- }
+ partitionInfo.LeftModeInfo = superblockInfo.GetModeInfo(rowIndex, columnIndex - 1);
+ }
- for (int refList = 0; refList < 2; refList++)
- {
- this.referenceFrames[rowIndex + y][columnIndex + x][refList] = this.referenceFrame[refList];
- }
- }
+ this.ReadModeInfo(ref reader, partitionInfo);
+ ReadPaletteTokens(ref reader, partitionInfo);
+ ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
+ if (partitionInfo.ModeInfo.Skip)
+ {
+ this.ResetSkipContext(partitionInfo);
}
- ComputePrediction();
this.Residual(rowIndex, columnIndex, blockSize);
}
+ private void ResetSkipContext(Av1PartitionInfo partitionInfo)
+ {
+ int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
+ for (int i = 0; i < planesCount; i++)
+ {
+ bool subX = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingX;
+ bool subY = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingY;
+ Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
+ int txsWide = planeBlockSize.GetWidth() >> 2;
+ int txsHigh = planeBlockSize.GetHeight() >> 2;
+ int aboveOffset = (partitionInfo.ColumnIndex - this.TileInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0);
+ int leftOffset = (partitionInfo.RowIndex - this.TileInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0);
+ int[] aboveContext = this.aboveContext.AboveContext[i + aboveOffset];
+ int[] leftContext = this.leftContext.LeftContext[i + leftOffset];
+ Array.Fill(aboveContext, 0);
+ Array.Fill(leftContext, 0);
+ }
+ }
+
private void Residual(int rowIndex, int columnIndex, Av1BlockSize blockSize)
{
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@@ -307,7 +293,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
int subBlockColumn = columnChunk & superBlockMask;
for (int plane = 0; plane < 1 + (this.HasChroma(rowIndex, columnIndex, blockSize) ? 2 : 0); plane++)
{
- Av1TransformSize transformSize = this.FrameInfo.CodedLossless ? Av1TransformSize.Size4x4 : this.GetSize(plane, this.transformSize);
+ Av1TransformSize transformSize = this.FrameInfo.CodedLossless ? Av1TransformSize.Size4x4 : this.GetSize(plane, -1);
int stepX = transformSize.GetWidth() >> 2;
int stepY = transformSize.GetHeight() >> 2;
Av1BlockSize planeSize = this.GetPlaneResidualSize(sizeChunk, plane);
@@ -348,6 +334,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
private void TransformBlock(int plane, int baseX, int baseY, Av1TransformSize transformSize, int x, int y)
{
+ Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(), false, Av1PartitionType.None);
int startX = (baseX + 4) * x;
int startY = (baseY + 4) * y;
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@@ -368,28 +355,28 @@ internal class Av1TileDecoder : IAv1TileDecoder
return;
}
- if ((plane == 0 && this.paletteSizeY > 0) ||
- (plane != 0 && this.paletteSizeUv > 0))
+ if ((plane == 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) > 0) ||
+ (plane != 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) > 0))
{
this.PredictPalette(plane, startX, startY, x, y, transformSize);
}
else
{
- bool isChromaFromLuma = plane > 0 && this.uvMode == Av1PredictionMode.UvChromaFromLuma;
+ bool isChromaFromLuma = plane > 0 && partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma;
Av1PredictionMode mode;
if (plane == 0)
{
- mode = this.yMode;
+ mode = partitionInfo.ModeInfo.YMode;
}
else
{
- mode = isChromaFromLuma ? Av1PredictionMode.DC : this.uvMode;
+ mode = isChromaFromLuma ? Av1PredictionMode.DC : partitionInfo.ModeInfo.UvMode;
}
int log2Width = transformSize.GetWidthLog2();
int log2Height = transformSize.GetHeightLog2();
- bool leftAvailable = x > 0 || plane == 0 ? this.availableLeft : this.availableLeftForChroma;
- bool upAvailable = y > 0 || plane == 0 ? this.availableUp : this.availableUpForChroma;
+ bool leftAvailable = x > 0 || plane == 0 ? partitionInfo.AvailableLeft : partitionInfo.AvailableLeftForChroma;
+ bool upAvailable = y > 0 || plane == 0 ? partitionInfo.AvailableUp : partitionInfo.AvailableUpForChroma;
bool haveAboveRight = this.blockDecoded[plane][(subBlockRow >> subY) - 1][(subBlockColumn >> subX) + stepX];
bool haveBelowLeft = this.blockDecoded[plane][(subBlockRow >> subY) + stepY][(subBlockColumn >> subX) - 1];
this.PredictIntra(plane, startX, startY, leftAvailable, upAvailable, haveAboveRight, haveBelowLeft, mode, log2Width, log2Height);
@@ -405,7 +392,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.maxLumaHeight = startY + (stepY * 4);
}
- if (!this.skip)
+ if (!partitionInfo.ModeInfo.Skip)
{
int eob = this.Coefficients(plane, startX, startY, transformSize);
if (eob > 0)
@@ -434,11 +421,6 @@ internal class Av1TileDecoder : IAv1TileDecoder
private void PredictPalette(int plane, int startX, int startY, int x, int y, Av1TransformSize transformSize) => throw new NotImplementedException();
- private static void ComputePrediction()
- {
- // Not applicable for INTRA frames.
- }
-
private void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize)
{
int block4x4Width = blockSize.Get4x4WideCount();
@@ -446,21 +428,22 @@ internal class Av1TileDecoder : IAv1TileDecoder
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY;
int endPlane = this.HasChroma(rowIndex, columnIndex, blockSize) ? 3 : 1;
+ this.aboveLevelContext = new int[3][];
+ this.aboveDcContext = new int[3][];
+ this.leftLevelContext = new int[3][];
+ this.leftDcContext = new int[3][];
for (int plane = 0; plane < endPlane; plane++)
{
int subX = plane > 0 && subsamplingX ? 1 : 0;
int subY = plane > 0 && subsamplingY ? 1 : 0;
- for (int i = columnIndex >> subX; i < (columnIndex + block4x4Width) >> subX; i++)
- {
- this.aboveLevelContext[plane][i] = 0;
- this.aboveDcContext[plane][i] = 0;
- }
-
- for (int i = rowIndex >> subY; i < (rowIndex + block4x4Height) >> subY; i++)
- {
- this.leftLevelContext[plane][i] = 0;
- this.leftDcContext[plane][i] = 0;
- }
+ this.aboveLevelContext[plane] = new int[(columnIndex + block4x4Width) >> subX];
+ this.aboveDcContext[plane] = new int[(columnIndex + block4x4Width) >> subX];
+ this.leftLevelContext[plane] = new int[(rowIndex + block4x4Height) >> subY];
+ this.leftDcContext[plane] = new int[(rowIndex + block4x4Height) >> subY];
+ Array.Fill(this.aboveLevelContext[plane], 0);
+ Array.Fill(this.aboveDcContext[plane], 0);
+ Array.Fill(this.leftLevelContext[plane], 0);
+ Array.Fill(this.leftDcContext[plane], 0);
}
}
@@ -480,209 +463,250 @@ internal class Av1TileDecoder : IAv1TileDecoder
}*/
}
- private void ReadPaletteTokens(ref Av1SymbolDecoder reader)
+ private static void ReadPaletteTokens(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
reader.ReadLiteral(-1);
- if (this.paletteSizeY != 0)
+ if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) != 0)
{
// Todo: Implement.
throw new NotImplementedException();
}
- if (this.paletteSizeUv != 0)
+ if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0)
{
// Todo: Implement.
throw new NotImplementedException();
}
}
- private void ReadModeInfo(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
- => this.ReadIntraFrameModeInfo(ref reader, rowIndex, columnIndex, blockSize);
+ private void ReadModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
+ {
+ DebugGuard.IsTrue(this.FrameInfo.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame, "Only INTRA frames supported.");
+ this.ReadIntraFrameModeInfo(ref reader, partitionInfo);
+ }
- private void ReadIntraFrameModeInfo(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
+ private void ReadIntraFrameModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
- this.skip = false;
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip)
{
- this.ReadIntraSegmentId(ref reader);
+ this.IntraSegmentId(ref reader, partitionInfo);
}
// this.skipMode = false;
- this.ReadSkip(ref reader);
+ partitionInfo.ModeInfo.Skip = this.ReadSkip(ref reader, partitionInfo);
if (!this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip)
{
- this.IntraSegmentId(ref reader, rowIndex, columnIndex);
+ this.IntraSegmentId(ref reader, partitionInfo);
}
- this.ReadCdef(ref reader, rowIndex, columnIndex, blockSize);
- this.ReadDeltaQuantizerIndex(ref reader, blockSize);
- this.ReadDeltaLoopFilter(ref reader, blockSize);
- this.readDeltas = false;
- this.referenceFrame[0] = -1; // IntraFrame;
- this.referenceFrame[1] = -1; // None;
+ this.ReadCdef(ref reader, partitionInfo);
+
+ bool readDeltas = false;
+ if (readDeltas)
+ {
+ this.ReadDeltaQuantizerIndex(ref reader, partitionInfo);
+ this.ReadDeltaLoopFilter(ref reader, partitionInfo);
+ }
+
+ partitionInfo.ReferenceFrame[0] = 0; // IntraFrame;
+ partitionInfo.ReferenceFrame[1] = -1; // None;
+ partitionInfo.ModeInfo.SetPaletteSizes(0, 0);
bool useIntraBlockCopy = false;
- if (this.FrameInfo.AllowIntraBlockCopy)
+ if (this.AllowIntraBlockCopy())
{
useIntraBlockCopy = reader.ReadUseIntraBlockCopy();
}
if (useIntraBlockCopy)
{
- // TODO: Implement
+ partitionInfo.ModeInfo.YMode = Av1PredictionMode.DC;
+ partitionInfo.ModeInfo.UvMode = Av1PredictionMode.DC;
}
else
{
// this.IsInter = false;
- this.yMode = reader.ReadIntraFrameYMode(blockSize);
- this.IntraAngleInfoY(ref reader, blockSize);
- if (this.HasChroma(rowIndex, columnIndex, blockSize))
+ partitionInfo.ModeInfo.YMode = reader.ReadYMode(partitionInfo.AboveModeInfo, partitionInfo.LeftModeInfo);
+ partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Y] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.YMode, partitionInfo.ModeInfo.BlockSize);
+ if (partitionInfo.IsChroma && !this.SequenceHeader.ColorConfig.IsMonochrome)
{
- this.uvMode = reader.ReadUvMode(blockSize, this.IsChromaForLumaAllowed(blockSize));
- if (this.uvMode == Av1PredictionMode.UvChromaFromLuma)
+ partitionInfo.ModeInfo.UvMode = reader.ReadIntraModeUv(partitionInfo.ModeInfo.YMode, this.IsChromaForLumaAllowed(partitionInfo));
+ if (partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma)
{
- this.ReadChromaFromLumaAlphas(ref reader);
+ this.ReadChromaFromLumaAlphas(ref reader, partitionInfo);
}
- this.IntraAngleInfoUv(ref reader, blockSize);
+ partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Uv] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.UvMode, partitionInfo.ModeInfo.BlockSize);
+ }
+ else
+ {
+ partitionInfo.ModeInfo.UvMode = Av1PredictionMode.DC;
}
- this.paletteSizeY = 0;
- this.paletteSizeUv = 0;
- if (this.SequenceHeader.ModeInfoSize >= (int)Av1BlockSize.Block8x8 &&
- ((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4WideCount() <= 64 &&
- ((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4HighCount() <= 64 &&
+ if (partitionInfo.ModeInfo.BlockSize >= Av1BlockSize.Block8x8 &&
+ partitionInfo.ModeInfo.BlockSize.GetWidth() <= 64 &&
+ partitionInfo.ModeInfo.BlockSize.GetHeight() <= 64 &&
this.FrameInfo.AllowScreenContentTools)
{
- this.PaletteModeInfo(ref reader);
+ this.PaletteModeInfo(ref reader, partitionInfo);
}
- this.FilterIntraModeInfo(ref reader, blockSize);
+ this.FilterIntraModeInfo(ref reader, partitionInfo);
}
}
- private bool IsChromaForLumaAllowed(Av1BlockSize blockSize)
+ private bool AllowIntraBlockCopy()
+ => (this.FrameInfo.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame) &&
+ (this.SequenceHeader.ForceScreenContentTools > 0) &&
+ this.FrameInfo.AllowIntraBlockCopy;
+
+ private bool IsChromaForLumaAllowed(Av1PartitionInfo partitionInfo)
{
- if (this.lossless)
+ if (this.FrameInfo.LosslessArray[partitionInfo.ModeInfo.SegmentId])
{
// In lossless, CfL is available when the partition size is equal to the
// transform size.
bool subX = this.SequenceHeader.ColorConfig.SubSamplingX;
bool subY = this.SequenceHeader.ColorConfig.SubSamplingY;
- Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY);
+ Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
return planeBlockSize == Av1BlockSize.Block4x4;
}
// Spec: CfL is available to luma partitions lesser than or equal to 32x32
- return blockSize.Get4x4WideCount() <= 32 && blockSize.Get4x4HighCount() <= 32;
+ return partitionInfo.ModeInfo.BlockSize.GetWidth() <= 32 && partitionInfo.ModeInfo.BlockSize.GetHeight() <= 32;
}
- private void ReadIntraSegmentId(ref Av1SymbolDecoder reader) => throw new NotImplementedException();
-
- private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
+ private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
- bool useFilterIntra = false;
if (this.SequenceHeader.EnableFilterIntra &&
- this.yMode == Av1PredictionMode.DC && this.paletteSizeY == 0 &&
- Math.Max(blockSize.GetWidth(), blockSize.GetHeight()) <= 32)
+ partitionInfo.ModeInfo.YMode == Av1PredictionMode.DC &&
+ partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) == 0 &&
+ Math.Max(partitionInfo.ModeInfo.BlockSize.GetWidth(), partitionInfo.ModeInfo.BlockSize.GetHeight()) <= 32)
{
- useFilterIntra = reader.ReadUseFilterUltra();
- if (useFilterIntra)
+ partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = reader.ReadUseFilterUltra(partitionInfo.ModeInfo.BlockSize);
+ if (partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra)
{
- this.filterUltraMode = reader.ReadFilterUltraMode();
+ partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = reader.ReadFilterUltraMode();
}
}
+ else
+ {
+ partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
+ }
}
- private void PaletteModeInfo(ref Av1SymbolDecoder reader) =>
+ private void PaletteModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) =>
// TODO: Implement.
throw new NotImplementedException();
- private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader) =>
+ private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) =>
// TODO: Implement.
throw new NotImplementedException();
- private void IntraAngleInfoY(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
+ private static int IntraAngleInfo(ref Av1SymbolDecoder reader, Av1PredictionMode mode, Av1BlockSize blockSize)
{
- this.angleDeltaY = 0;
- if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(this.yMode))
+ int angleDelta = 0;
+ if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(mode))
{
- int angleDeltaY = reader.ReadAngleDelta(this.yMode);
- this.angleDeltaY = angleDeltaY - ObuConstants.MaxAngleDelta;
+ int symbol = reader.ReadAngleDelta(mode);
+ angleDelta = symbol - ObuConstants.MaxAngleDelta;
}
- }
- private void IntraAngleInfoUv(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
- {
- this.angleDeltaUv = 0;
- if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(this.uvMode))
- {
- int angleDeltaUv = reader.ReadAngleDelta(this.uvMode);
- this.angleDeltaUv = angleDeltaUv - ObuConstants.MaxAngleDelta;
- }
+ return angleDelta;
}
private static bool IsDirectionalMode(Av1PredictionMode mode)
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees;
- private void IntraSegmentId(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex)
+ private void IntraSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
if (this.FrameInfo.SegmentationParameters.Enabled)
{
- this.ReadSegmentId(ref reader, rowIndex, columnIndex);
+ this.ReadSegmentId(ref reader, partitionInfo);
}
- else
+
+ int bw4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
+ int bh4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount();
+ int x_mis = Math.Min(this.FrameInfo.ModeInfoColumnCount - partitionInfo.ColumnIndex, bw4);
+ int y_mis = Math.Min(this.FrameInfo.ModeInfoRowCount - partitionInfo.RowIndex, bh4);
+
+ for (int y = 0; y < y_mis; y++)
{
- this.segmentId = 0;
+ for (int x = 0; x < x_mis; x++)
+ {
+ this.segmentIds[partitionInfo.RowIndex + y][partitionInfo.ColumnIndex + x] = partitionInfo.ModeInfo.SegmentId;
+ }
}
-
- this.lossless = this.FrameInfo.LosslessArray[this.segmentId];
}
- private void ReadSegmentId(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex)
+ private void ReadSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
- int pred;
+ int predictor;
int prevUL = -1;
int prevU = -1;
int prevL = -1;
- if (this.availableUp && this.availableLeft)
+ int columnIndex = partitionInfo.ColumnIndex;
+ int rowIndex = partitionInfo.RowIndex;
+ if (partitionInfo.AvailableUp && partitionInfo.AvailableLeft)
{
- prevUL = this.segmentIds[rowIndex - 1][columnIndex - 1];
+ prevUL = this.GetSegmentId(partitionInfo, rowIndex - 1, columnIndex - 1);
}
- if (this.availableUp)
+ if (partitionInfo.AvailableUp)
{
- prevU = this.segmentIds[rowIndex - 1][columnIndex];
+ prevU = this.GetSegmentId(partitionInfo, rowIndex - 1, columnIndex);
}
- if (this.availableLeft)
+ if (partitionInfo.AvailableLeft)
{
- prevU = this.segmentIds[rowIndex][columnIndex - 1];
+ prevU = this.GetSegmentId(partitionInfo, rowIndex, columnIndex - 1);
}
if (prevU == -1)
{
- pred = prevL == -1 ? 0 : prevL;
+ predictor = prevL == -1 ? 0 : prevL;
}
else if (prevL == -1)
{
- pred = prevU;
+ predictor = prevU;
}
else
{
- pred = prevU == prevUL ? prevU : prevL;
+ predictor = prevU == prevUL ? prevU : prevL;
}
- if (this.skip)
+ if (partitionInfo.ModeInfo.Skip)
{
- this.segmentId = 0;
+ partitionInfo.ModeInfo.SegmentId = predictor;
}
else
{
+ int ctx = prevUL < 0 ? 0 /* Edge cases */
+ : prevUL == prevU && prevUL == prevL ? 2
+ : prevUL == prevU || prevUL == prevL || prevU == prevL ? 1 : 0;
int lastActiveSegmentId = this.FrameInfo.SegmentationParameters.LastActiveSegmentId;
- this.segmentId = NegativeDeinterleave(reader.ReadSegmentId(-1), pred, lastActiveSegmentId + 1);
+ partitionInfo.ModeInfo.SegmentId = NegativeDeinterleave(reader.ReadSegmentId(ctx), predictor, lastActiveSegmentId + 1);
+ }
+ }
+
+ private int GetSegmentId(Av1PartitionInfo partitionInfo, int rowIndex, int columnIndex)
+ {
+ int modeInfoOffset = (rowIndex * this.FrameInfo.ModeInfoColumnCount) + columnIndex;
+ int bw4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
+ int bh4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount();
+ int xMin = Math.Min(this.FrameInfo.ModeInfoColumnCount - columnIndex, bw4);
+ int yMin = Math.Min(this.FrameInfo.ModeInfoRowCount - rowIndex, bh4);
+ int segmentId = ObuConstants.MaxSegments - 1;
+ for (int y = 0; y < yMin; y++)
+ {
+ for (int x = 0; x < xMin; x++)
+ {
+ segmentId = Math.Min(segmentId, this.segmentIds[y][x]);
+ }
}
+
+ return segmentId;
}
private static int NegativeDeinterleave(int diff, int reference, int max)
@@ -731,41 +755,45 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
}
- private void ReadCdef(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize)
+ private void ReadCdef(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
- if (this.skip || this.FrameInfo.CodedLossless || !this.SequenceHeader.EnableCdef || this.FrameInfo.AllowIntraBlockCopy)
+ if (partitionInfo.ModeInfo.Skip || this.FrameInfo.CodedLossless || !this.SequenceHeader.EnableCdef || this.FrameInfo.AllowIntraBlockCopy)
{
return;
}
int cdefSize4 = Av1BlockSize.Block64x64.Get4x4WideCount();
int cdefMask4 = ~(cdefSize4 - 1);
- int r = rowIndex & cdefMask4;
- int c = columnIndex & cdefMask4;
- if (this.cdefIndex[r][c] == -1)
- {
- this.cdefIndex[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount);
- int w4 = blockSize.Get4x4WideCount();
- int h4 = blockSize.Get4x4HighCount();
- for (int i = r; i < r + h4; i += cdefSize4)
+ int r = partitionInfo.RowIndex & cdefMask4;
+ int c = partitionInfo.ColumnIndex & cdefMask4;
+ if (partitionInfo.CdefStrength[r][c] == -1)
+ {
+ partitionInfo.CdefStrength[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount);
+ if (this.SequenceHeader.SuperBlockSize == Av1BlockSize.Block128x128)
{
- for (int j = c; j < c + w4; j += cdefSize4)
+ int w4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
+ int h4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount();
+ for (int i = r; i < r + h4; i += cdefSize4)
{
- this.cdefIndex[i][j] = this.cdefIndex[r][c];
+ for (int j = c; j < c + w4; j += cdefSize4)
+ {
+ partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = partitionInfo.CdefStrength[r][c];
+ }
}
}
}
}
- private void ReadDeltaLoopFilter(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
+ private void ReadDeltaLoopFilter(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
- if (blockSize == superBlockSize && this.skip)
+ if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent ||
+ (partitionInfo.ModeInfo.BlockSize == superBlockSize && partitionInfo.ModeInfo.Skip))
{
return;
}
- if (this.readDeltas && this.FrameInfo.DeltaLoopFilterParameters.IsPresent)
+ if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent)
{
int frameLoopFilterCount = 1;
if (this.FrameInfo.DeltaLoopFilterParameters.Multi)
@@ -793,28 +821,32 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
}
- private void ReadSkip(ref Av1SymbolDecoder reader)
+ private bool ReadSkip(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
+ int segmentId = partitionInfo.ModeInfo.SegmentId;
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip &&
- this.FrameInfo.SegmentationParameters.IsFeatureActive(-1, ObuSegmentationFeature.LevelSkip))
+ this.FrameInfo.SegmentationParameters.IsFeatureActive(segmentId, ObuSegmentationLevelFeature.Skip))
{
- this.skip = true;
+ return true;
}
else
{
- this.skip = reader.ReadSkip(-1);
+ int aboveSkip = partitionInfo.AboveModeInfo != null && partitionInfo.AboveModeInfo.Skip ? 1 : 0;
+ int leftSkip = partitionInfo.LeftModeInfo != null && partitionInfo.LeftModeInfo.Skip ? 1 : 0;
+ return reader.ReadSkip(aboveSkip + leftSkip);
}
}
- private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
+ private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
- if (blockSize == superBlockSize && this.skip)
+ if (!this.FrameInfo.DeltaQParameters.IsPresent ||
+ (partitionInfo.ModeInfo.BlockSize == superBlockSize && partitionInfo.ModeInfo.Skip))
{
return;
}
- if (this.readDeltas)
+ if (partitionInfo.ModeInfo.BlockSize != this.SequenceHeader.SuperBlockSize || !partitionInfo.ModeInfo.Skip)
{
int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute();
if (deltaQuantizerAbsolute == ObuConstants.DeltaQuantizerSmall)
@@ -829,6 +861,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0;
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << this.deltaQuantizerResolution));
+ partitionInfo.SuperblockInfo.SuperblockDeltaQ[0] = this.currentQuantizerIndex;
}
}
}