Browse Source

Introduce Av1PartitionInfo and fill it during decoding

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
881199e1e8
  1. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
  2. 5
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
  3. 22
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  4. 10
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationFeature.cs
  5. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
  6. 4
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
  7. 8
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
  8. 49
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1BlockModeInfo.cs
  9. 45
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs
  10. 13
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1FilterIntraMode.cs
  11. 11
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1IntraFilterModeInfo.cs
  12. 10
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs
  13. 4
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs
  14. 45
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PartitionInfo.cs
  15. 10
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1PlaneType.cs
  16. 11
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SuperblockInfo.cs
  17. 40
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs
  18. 439
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs

2
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs

@ -9,7 +9,7 @@ internal class ObuColorConfig
public int ChannelCount { get; set; } public int ChannelCount { get; set; }
public bool Monochrome { get; set; } public bool IsMonochrome { get; set; }
public ObuColorPrimaries ColorPrimaries { get; set; } public ObuColorPrimaries ColorPrimaries { get; set; }

5
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs

@ -113,4 +113,9 @@ internal static class ObuConstants
/// Maximum magnitude of AngleDeltaY and AngleDeltaUV. /// Maximum magnitude of AngleDeltaY and AngleDeltaUV.
/// </summary> /// </summary>
public const int MaxAngleDelta = 3; public const int MaxAngleDelta = 3;
/// <summary>
/// Number of segments allowed in segmentation map.
/// </summary>
public const int MaxSegments = 8;
} }

22
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs

@ -263,8 +263,8 @@ internal class ObuReader
sequenceHeader.EnableDualFilter = false; sequenceHeader.EnableDualFilter = false;
sequenceHeader.OrderHintInfo.EnableJointCompound = false; sequenceHeader.OrderHintInfo.EnableJointCompound = false;
sequenceHeader.OrderHintInfo.EnableReferenceFrameMotionVectors = false; sequenceHeader.OrderHintInfo.EnableReferenceFrameMotionVectors = false;
sequenceHeader.SequenceForceScreenContentTools = 2; sequenceHeader.ForceScreenContentTools = 2;
sequenceHeader.SequenceForceIntegerMotionVector = 2; sequenceHeader.ForceIntegerMotionVector = 2;
sequenceHeader.OrderHintInfo.OrderHintBits = 0; sequenceHeader.OrderHintInfo.OrderHintBits = 0;
// Video related flags removed // Video related flags removed
@ -280,13 +280,13 @@ internal class ObuReader
{ {
ObuColorConfig colorConfig = new(); ObuColorConfig colorConfig = new();
ReadBitDepth(ref reader, colorConfig, sequenceHeader); ReadBitDepth(ref reader, colorConfig, sequenceHeader);
colorConfig.Monochrome = false; colorConfig.IsMonochrome = false;
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High) 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.IsColorDescriptionPresent = reader.ReadBoolean();
colorConfig.ColorPrimaries = ObuColorPrimaries.Unspecified; colorConfig.ColorPrimaries = ObuColorPrimaries.Unspecified;
colorConfig.TransferCharacteristics = ObuTransferCharacteristics.Unspecified; colorConfig.TransferCharacteristics = ObuTransferCharacteristics.Unspecified;
@ -303,7 +303,7 @@ internal class ObuReader
colorConfig.SubSamplingY = false; colorConfig.SubSamplingY = false;
colorConfig.ChromaSamplePosition = ObuChromoSamplePosition.Unknown; colorConfig.ChromaSamplePosition = ObuChromoSamplePosition.Unknown;
colorConfig.HasSeparateUvDelta = false; colorConfig.HasSeparateUvDelta = false;
if (colorConfig.Monochrome) if (colorConfig.IsMonochrome)
{ {
colorConfig.ColorRange = reader.ReadBoolean(); colorConfig.ColorRange = reader.ReadBoolean();
colorConfig.SubSamplingX = true; colorConfig.SubSamplingX = true;
@ -669,7 +669,7 @@ internal class ObuReader
} }
frameInfo.DisableCdfUpdate = reader.ReadBoolean(); frameInfo.DisableCdfUpdate = reader.ReadBoolean();
frameInfo.AllowScreenContentTools = sequenceHeader.SequenceForceScreenContentTools == 1; frameInfo.AllowScreenContentTools = sequenceHeader.ForceScreenContentTools == 1;
if (frameInfo.AllowScreenContentTools) if (frameInfo.AllowScreenContentTools)
{ {
frameInfo.AllowScreenContentTools = reader.ReadBoolean(); frameInfo.AllowScreenContentTools = reader.ReadBoolean();
@ -677,13 +677,13 @@ internal class ObuReader
if (frameInfo.AllowScreenContentTools) if (frameInfo.AllowScreenContentTools)
{ {
if (sequenceHeader.SequenceForceIntegerMotionVector == 1) if (sequenceHeader.ForceIntegerMotionVector == 1)
{ {
frameInfo.ForceIntegerMotionVector = reader.ReadBoolean(); frameInfo.ForceIntegerMotionVector = reader.ReadBoolean();
} }
else else
{ {
frameInfo.ForceIntegerMotionVector = sequenceHeader.SequenceForceIntegerMotionVector != 0; frameInfo.ForceIntegerMotionVector = sequenceHeader.ForceIntegerMotionVector != 0;
} }
} }
else else
@ -899,7 +899,7 @@ internal class ObuReader
} }
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature) 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) private static int GetQIndex(ObuSegmentationParameters segmentationParameters, int segmentId, int baseQIndex)
{ {
@ -919,7 +919,7 @@ internal class ObuReader
{ {
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader; ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo; ObuFrameHeader frameInfo = decoder.FrameInfo;
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3; int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = reader.BitPosition; int startBitPosition = reader.BitPosition;
ReadUncompressedFrameHeader(ref reader, decoder, header, planeCount); ReadUncompressedFrameHeader(ref reader, decoder, header, planeCount);
if (trailingBit) if (trailingBit)

10
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationFeature.cs

@ -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,
}

2
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs

@ -17,6 +17,6 @@ internal class ObuSegmentationParameters
public int LastActiveSegmentId { get; internal set; } 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]; => this.FeatureEnabled[segmentId, (int)feature];
} }

4
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs

@ -55,9 +55,9 @@ internal class ObuSequenceHeader
public bool EnableDualFilter { get; set; } 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; } public bool EnableSuperResolution { get; set; }

8
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs

@ -109,11 +109,11 @@ internal class ObuWriter
WriteBitDepth(ref writer, colorConfig, sequenceHeader); WriteBitDepth(ref writer, colorConfig, sequenceHeader);
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High) if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High)
{ {
writer.WriteBoolean(colorConfig.Monochrome); writer.WriteBoolean(colorConfig.IsMonochrome);
} }
writer.WriteBoolean(false); // colorConfig.IsColorDescriptionPresent writer.WriteBoolean(false); // colorConfig.IsColorDescriptionPresent
if (colorConfig.Monochrome) if (colorConfig.IsMonochrome)
{ {
writer.WriteBoolean(colorConfig.ColorRange); writer.WriteBoolean(colorConfig.ColorRange);
return; return;
@ -321,7 +321,7 @@ internal class ObuWriter
if (frameInfo.AllowScreenContentTools) if (frameInfo.AllowScreenContentTools)
{ {
if (sequenceHeader.SequenceForceIntegerMotionVector == 1) if (sequenceHeader.ForceIntegerMotionVector == 1)
{ {
writer.WriteBoolean(frameInfo.ForceIntegerMotionVector); writer.WriteBoolean(frameInfo.ForceIntegerMotionVector);
} }
@ -448,7 +448,7 @@ internal class ObuWriter
{ {
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader; ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo; ObuFrameHeader frameInfo = decoder.FrameInfo;
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3; int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = writer.BitPosition; int startBitPosition = writer.BitPosition;
WriteUncompressedFrameHeader(ref writer, sequenceHeader, frameInfo, planeCount); WriteUncompressedFrameHeader(ref writer, sequenceHeader, frameInfo, planeCount);
if (writeTrailingBits) if (writeTrailingBits)

49
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];
}

45
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 DeltaQuantizerAbsolute => new(28160, 32120, 32677);
public static Av1Distribution[] SegmentId => [new(128 * 128), new(128 * 128), new(128 * 128)]; 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)
];
} }

13
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
}

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

10
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseAboveContext.cs

@ -5,7 +5,13 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1ParseAboveContext 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 = [];
}
} }

4
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1ParseLeftContext.cs

@ -5,7 +5,9 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1ParseLeftContext 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(); internal void Clear() => throw new NotImplementedException();
} }

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

10
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
}

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

40
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs

@ -7,15 +7,19 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal ref struct Av1SymbolDecoder 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 tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes; 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[][] uvMode = Av1DefaultDistributions.UvMode;
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip; private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip;
private readonly Av1Distribution deltaLoopFilterAbsolute = Av1DefaultDistributions.DeltaLoopFilterAbsolute; private readonly Av1Distribution deltaLoopFilterAbsolute = Av1DefaultDistributions.DeltaLoopFilterAbsolute;
private readonly Av1Distribution deltaQuantizerAbsolute = Av1DefaultDistributions.DeltaQuantizerAbsolute; private readonly Av1Distribution deltaQuantizerAbsolute = Av1DefaultDistributions.DeltaQuantizerAbsolute;
private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId; private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId;
private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta; private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta;
private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode;
private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra;
private Av1SymbolReader reader; private Av1SymbolReader reader;
public Av1SymbolDecoder(Span<byte> tileData) => this.reader = new Av1SymbolReader(tileData); public Av1SymbolDecoder(Span<byte> tileData) => this.reader = new Av1SymbolReader(tileData);
@ -76,17 +80,31 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(distribution) > 0; return r.ReadSymbol(distribution) > 0;
} }
public Av1PredictionMode ReadIntraFrameYMode(Av1BlockSize blockSize) public Av1PredictionMode ReadYMode(Av1BlockModeInfo? aboveModeInfo, Av1BlockModeInfo? leftModeInfo)
{ {
ref Av1SymbolReader r = ref this.reader; 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; int chromaForLumaIndex = chromaFromLumaAllowed ? 1 : 0;
ref Av1SymbolReader r = ref this.reader; 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) public bool ReadSkip(int ctx)
@ -119,9 +137,17 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.angleDelta[((int)mode) - 1]); 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) private static uint GetElementProbability(Av1Distribution probability, Av1PartitionType element)
=> probability[(int)element - 1] - probability[(int)element]; => probability[(int)element - 1] - probability[(int)element];

439
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1TileDecoder.cs

@ -18,37 +18,16 @@ internal class Av1TileDecoder : IAv1TileDecoder
private bool[][][] blockDecoded = []; private bool[][][] blockDecoded = [];
private int[][] referenceSgrXqd = []; private int[][] referenceSgrXqd = [];
private int[][][] referenceLrWiener = []; private int[][][] referenceLrWiener = [];
private bool availableUp;
private bool availableLeft;
private bool availableUpForChroma;
private bool availableLeftForChroma;
private Av1ParseAboveContext aboveContext = new(); private Av1ParseAboveContext aboveContext = new();
private Av1ParseLeftContext leftContext = new(); private Av1ParseLeftContext leftContext = new();
private bool skip;
private bool readDeltas;
private int currentQuantizerIndex; private int currentQuantizerIndex;
private Av1PredictionMode[][] yModes = []; private int[][] aboveLevelContext = [];
private Av1PredictionMode yMode = Av1PredictionMode.DC; private int[][] aboveDcContext = [];
private Av1PredictionMode uvMode = Av1PredictionMode.DC; private int[][] leftLevelContext = [];
private Av1PredictionMode[][] uvModes = []; private int[][] leftDcContext = [];
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[][] segmentIds = []; private int[][] segmentIds = [];
private int maxLumaWidth; private int maxLumaWidth;
private int maxLumaHeight; private int maxLumaHeight;
private int segmentId;
private int[][] cdefIndex = [];
private int deltaLoopFilterResolution = -1; private int deltaLoopFilterResolution = -1;
private int deltaQuantizerResolution = -1; private int deltaQuantizerResolution = -1;
@ -74,9 +53,10 @@ internal class Av1TileDecoder : IAv1TileDecoder
public void DecodeTile(Span<byte> tileData, int tileNum) public void DecodeTile(Span<byte> tileData, int tileNum)
{ {
Av1SymbolDecoder reader = new(tileData); Av1SymbolDecoder reader = new(tileData);
int tileRowIndex = tileNum / this.TileInfo.TileColumnCount;
int tileColumnIndex = 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(); this.ClearLoopFilterDelta();
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
this.referenceSgrXqd = new int[planesCount][]; this.referenceSgrXqd = new int[planesCount][];
@ -111,12 +91,13 @@ internal class Av1TileDecoder : IAv1TileDecoder
// this.ClearCdef(row, column); // this.ClearCdef(row, column);
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size); this.ClearBlockDecodedFlags(row, column, superBlock4x4Size);
this.ReadLoopRestoration(row, column, superBlockSize); 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) private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size)
{ {
@ -169,16 +150,17 @@ internal class Av1TileDecoder : IAv1TileDecoder
// TODO: Implement // 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] || if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] ||
columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex]) columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex])
{ {
return; return;
} }
this.availableUp = this.IsInside(rowIndex - 1, columnIndex); bool availableUp = this.IsInside(rowIndex - 1, columnIndex);
this.availableLeft = this.IsInside(rowIndex, columnIndex - 1); bool availableLeft = this.IsInside(rowIndex, columnIndex - 1);
int block4x4Size = blockSize.Get4x4WideCount(); int block4x4Size = blockSize.Get4x4WideCount();
int halfBlock4x4Size = block4x4Size >> 1; int halfBlock4x4Size = block4x4Size >> 1;
int quarterBlock4x4Size = halfBlock4x4Size >> 2; int quarterBlock4x4Size = halfBlock4x4Size >> 2;
@ -212,82 +194,86 @@ internal class Av1TileDecoder : IAv1TileDecoder
switch (partitionType) switch (partitionType)
{ {
case Av1PartitionType.Split: case Av1PartitionType.Split:
this.DecodePartition(ref reader, rowIndex, columnIndex, subSize); this.ParsePartition(ref reader, rowIndex, columnIndex, subSize);
this.DecodePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize); this.ParsePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize);
this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize); this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize);
this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize); this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize);
break; break;
case Av1PartitionType.None: case Av1PartitionType.None:
this.DecodeBlock(ref reader, rowIndex, columnIndex, subSize); this.ParseBlock(ref reader, rowIndex, columnIndex, subSize, superblockInfo, Av1PartitionType.None);
break; break;
default: default:
throw new NotImplementedException($"Partition type: {partitionType} is not supported."); 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 block4x4Width = blockSize.Get4x4WideCount();
int block4x4Height = blockSize.Get4x4HighCount(); int block4x4Height = blockSize.Get4x4HighCount();
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; int planesCount = this.SequenceHeader.ColorConfig.ChannelCount;
bool hasChroma = planesCount > 1; Av1BlockModeInfo blockModeInfo = new(planesCount, blockSize);
if (block4x4Height == 1 && this.SequenceHeader.ColorConfig.SubSamplingY && (rowIndex & 0x1) == 0) bool hasChroma = this.HasChroma(rowIndex, columnIndex, blockSize);
{ Av1PartitionInfo partitionInfo = new(blockModeInfo, superblockInfo, hasChroma, partitionType);
hasChroma = false; partitionInfo.ColumnIndex = columnIndex;
} partitionInfo.RowIndex = rowIndex;
partitionInfo.AvailableUp = this.IsInside(rowIndex - 1, columnIndex);
if (block4x4Width == 1 && this.SequenceHeader.ColorConfig.SubSamplingX && (columnIndex & 0x1) == 0) partitionInfo.AvailableLeft = this.IsInside(rowIndex, columnIndex - 1);
{ partitionInfo.AvailableUpForChroma = partitionInfo.AvailableUp;
hasChroma = false; partitionInfo.AvailableLeftForChroma = partitionInfo.AvailableLeft;
}
this.availableUp = this.IsInside(rowIndex - 1, columnIndex);
this.availableLeft = this.IsInside(rowIndex, columnIndex - 1);
this.availableUpForChroma = this.availableUp;
this.availableLeftForChroma = this.availableLeft;
if (hasChroma) if (hasChroma)
{ {
if (this.SequenceHeader.ColorConfig.SubSamplingY && block4x4Height == 1) 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) 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); if (partitionInfo.AvailableUp)
this.ReadPaletteTokens(ref reader);
ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
if (this.skip)
{ {
this.ResetBlockContext(rowIndex, columnIndex, blockSize); partitionInfo.AboveModeInfo = superblockInfo.GetModeInfo(rowIndex - 1, columnIndex);
} }
// bool isCompound = false; if (partitionInfo.AvailableLeft)
for (int y = 0; y < block4x4Height; y++)
{ {
for (int x = 0; x < block4x4Width; x++) partitionInfo.LeftModeInfo = superblockInfo.GetModeInfo(rowIndex, columnIndex - 1);
{ }
this.yModes[rowIndex + y][columnIndex + x] = this.yMode;
if (this.referenceFrame[0] == (object)ObuFrameType.IntraOnlyFrame && hasChroma)
{
this.uvModes[rowIndex + y][columnIndex + x] = this.uvMode;
}
for (int refList = 0; refList < 2; refList++) this.ReadModeInfo(ref reader, partitionInfo);
{ ReadPaletteTokens(ref reader, partitionInfo);
this.referenceFrames[rowIndex + y][columnIndex + x][refList] = this.referenceFrame[refList]; ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize);
} if (partitionInfo.ModeInfo.Skip)
} {
this.ResetSkipContext(partitionInfo);
} }
ComputePrediction();
this.Residual(rowIndex, columnIndex, blockSize); 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) private void Residual(int rowIndex, int columnIndex, Av1BlockSize blockSize)
{ {
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@ -307,7 +293,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
int subBlockColumn = columnChunk & superBlockMask; int subBlockColumn = columnChunk & superBlockMask;
for (int plane = 0; plane < 1 + (this.HasChroma(rowIndex, columnIndex, blockSize) ? 2 : 0); plane++) 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 stepX = transformSize.GetWidth() >> 2;
int stepY = transformSize.GetHeight() >> 2; int stepY = transformSize.GetHeight() >> 2;
Av1BlockSize planeSize = this.GetPlaneResidualSize(sizeChunk, plane); 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) 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 startX = (baseX + 4) * x;
int startY = (baseY + 4) * y; int startY = (baseY + 4) * y;
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@ -368,28 +355,28 @@ internal class Av1TileDecoder : IAv1TileDecoder
return; return;
} }
if ((plane == 0 && this.paletteSizeY > 0) || if ((plane == 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) > 0) ||
(plane != 0 && this.paletteSizeUv > 0)) (plane != 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) > 0))
{ {
this.PredictPalette(plane, startX, startY, x, y, transformSize); this.PredictPalette(plane, startX, startY, x, y, transformSize);
} }
else else
{ {
bool isChromaFromLuma = plane > 0 && this.uvMode == Av1PredictionMode.UvChromaFromLuma; bool isChromaFromLuma = plane > 0 && partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma;
Av1PredictionMode mode; Av1PredictionMode mode;
if (plane == 0) if (plane == 0)
{ {
mode = this.yMode; mode = partitionInfo.ModeInfo.YMode;
} }
else else
{ {
mode = isChromaFromLuma ? Av1PredictionMode.DC : this.uvMode; mode = isChromaFromLuma ? Av1PredictionMode.DC : partitionInfo.ModeInfo.UvMode;
} }
int log2Width = transformSize.GetWidthLog2(); int log2Width = transformSize.GetWidthLog2();
int log2Height = transformSize.GetHeightLog2(); int log2Height = transformSize.GetHeightLog2();
bool leftAvailable = x > 0 || plane == 0 ? this.availableLeft : this.availableLeftForChroma; bool leftAvailable = x > 0 || plane == 0 ? partitionInfo.AvailableLeft : partitionInfo.AvailableLeftForChroma;
bool upAvailable = y > 0 || plane == 0 ? this.availableUp : this.availableUpForChroma; bool upAvailable = y > 0 || plane == 0 ? partitionInfo.AvailableUp : partitionInfo.AvailableUpForChroma;
bool haveAboveRight = this.blockDecoded[plane][(subBlockRow >> subY) - 1][(subBlockColumn >> subX) + stepX]; bool haveAboveRight = this.blockDecoded[plane][(subBlockRow >> subY) - 1][(subBlockColumn >> subX) + stepX];
bool haveBelowLeft = this.blockDecoded[plane][(subBlockRow >> subY) + stepY][(subBlockColumn >> subX) - 1]; bool haveBelowLeft = this.blockDecoded[plane][(subBlockRow >> subY) + stepY][(subBlockColumn >> subX) - 1];
this.PredictIntra(plane, startX, startY, leftAvailable, upAvailable, haveAboveRight, haveBelowLeft, mode, log2Width, log2Height); 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); this.maxLumaHeight = startY + (stepY * 4);
} }
if (!this.skip) if (!partitionInfo.ModeInfo.Skip)
{ {
int eob = this.Coefficients(plane, startX, startY, transformSize); int eob = this.Coefficients(plane, startX, startY, transformSize);
if (eob > 0) 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 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) private void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize)
{ {
int block4x4Width = blockSize.Get4x4WideCount(); int block4x4Width = blockSize.Get4x4WideCount();
@ -446,21 +428,22 @@ internal class Av1TileDecoder : IAv1TileDecoder
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY; bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY;
int endPlane = this.HasChroma(rowIndex, columnIndex, blockSize) ? 3 : 1; 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++) for (int plane = 0; plane < endPlane; plane++)
{ {
int subX = plane > 0 && subsamplingX ? 1 : 0; int subX = plane > 0 && subsamplingX ? 1 : 0;
int subY = plane > 0 && subsamplingY ? 1 : 0; int subY = plane > 0 && subsamplingY ? 1 : 0;
for (int i = columnIndex >> subX; i < (columnIndex + block4x4Width) >> subX; i++) this.aboveLevelContext[plane] = new int[(columnIndex + block4x4Width) >> subX];
{ this.aboveDcContext[plane] = new int[(columnIndex + block4x4Width) >> subX];
this.aboveLevelContext[plane][i] = 0; this.leftLevelContext[plane] = new int[(rowIndex + block4x4Height) >> subY];
this.aboveDcContext[plane][i] = 0; this.leftDcContext[plane] = new int[(rowIndex + block4x4Height) >> subY];
} Array.Fill(this.aboveLevelContext[plane], 0);
Array.Fill(this.aboveDcContext[plane], 0);
for (int i = rowIndex >> subY; i < (rowIndex + block4x4Height) >> subY; i++) Array.Fill(this.leftLevelContext[plane], 0);
{ Array.Fill(this.leftDcContext[plane], 0);
this.leftLevelContext[plane][i] = 0;
this.leftDcContext[plane][i] = 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); reader.ReadLiteral(-1);
if (this.paletteSizeY != 0) if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) != 0)
{ {
// Todo: Implement. // Todo: Implement.
throw new NotImplementedException(); throw new NotImplementedException();
} }
if (this.paletteSizeUv != 0) if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0)
{ {
// Todo: Implement. // Todo: Implement.
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
private void ReadModeInfo(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) private void ReadModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
=> this.ReadIntraFrameModeInfo(ref reader, rowIndex, columnIndex, blockSize); {
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) if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip)
{ {
this.ReadIntraSegmentId(ref reader); this.IntraSegmentId(ref reader, partitionInfo);
} }
// this.skipMode = false; // this.skipMode = false;
this.ReadSkip(ref reader); partitionInfo.ModeInfo.Skip = this.ReadSkip(ref reader, partitionInfo);
if (!this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip) if (!this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip)
{ {
this.IntraSegmentId(ref reader, rowIndex, columnIndex); this.IntraSegmentId(ref reader, partitionInfo);
} }
this.ReadCdef(ref reader, rowIndex, columnIndex, blockSize); this.ReadCdef(ref reader, partitionInfo);
this.ReadDeltaQuantizerIndex(ref reader, blockSize);
this.ReadDeltaLoopFilter(ref reader, blockSize); bool readDeltas = false;
this.readDeltas = false; if (readDeltas)
this.referenceFrame[0] = -1; // IntraFrame; {
this.referenceFrame[1] = -1; // None; 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; bool useIntraBlockCopy = false;
if (this.FrameInfo.AllowIntraBlockCopy) if (this.AllowIntraBlockCopy())
{ {
useIntraBlockCopy = reader.ReadUseIntraBlockCopy(); useIntraBlockCopy = reader.ReadUseIntraBlockCopy();
} }
if (useIntraBlockCopy) if (useIntraBlockCopy)
{ {
// TODO: Implement partitionInfo.ModeInfo.YMode = Av1PredictionMode.DC;
partitionInfo.ModeInfo.UvMode = Av1PredictionMode.DC;
} }
else else
{ {
// this.IsInter = false; // this.IsInter = false;
this.yMode = reader.ReadIntraFrameYMode(blockSize); partitionInfo.ModeInfo.YMode = reader.ReadYMode(partitionInfo.AboveModeInfo, partitionInfo.LeftModeInfo);
this.IntraAngleInfoY(ref reader, blockSize); partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Y] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.YMode, partitionInfo.ModeInfo.BlockSize);
if (this.HasChroma(rowIndex, columnIndex, blockSize)) if (partitionInfo.IsChroma && !this.SequenceHeader.ColorConfig.IsMonochrome)
{ {
this.uvMode = reader.ReadUvMode(blockSize, this.IsChromaForLumaAllowed(blockSize)); partitionInfo.ModeInfo.UvMode = reader.ReadIntraModeUv(partitionInfo.ModeInfo.YMode, this.IsChromaForLumaAllowed(partitionInfo));
if (this.uvMode == Av1PredictionMode.UvChromaFromLuma) 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; if (partitionInfo.ModeInfo.BlockSize >= Av1BlockSize.Block8x8 &&
this.paletteSizeUv = 0; partitionInfo.ModeInfo.BlockSize.GetWidth() <= 64 &&
if (this.SequenceHeader.ModeInfoSize >= (int)Av1BlockSize.Block8x8 && partitionInfo.ModeInfo.BlockSize.GetHeight() <= 64 &&
((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4WideCount() <= 64 &&
((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4HighCount() <= 64 &&
this.FrameInfo.AllowScreenContentTools) 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 // In lossless, CfL is available when the partition size is equal to the
// transform size. // transform size.
bool subX = this.SequenceHeader.ColorConfig.SubSamplingX; bool subX = this.SequenceHeader.ColorConfig.SubSamplingX;
bool subY = this.SequenceHeader.ColorConfig.SubSamplingY; bool subY = this.SequenceHeader.ColorConfig.SubSamplingY;
Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY); Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
return planeBlockSize == Av1BlockSize.Block4x4; return planeBlockSize == Av1BlockSize.Block4x4;
} }
// Spec: CfL is available to luma partitions lesser than or equal to 32x32 // 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, Av1PartitionInfo partitionInfo)
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1BlockSize blockSize)
{ {
bool useFilterIntra = false;
if (this.SequenceHeader.EnableFilterIntra && if (this.SequenceHeader.EnableFilterIntra &&
this.yMode == Av1PredictionMode.DC && this.paletteSizeY == 0 && partitionInfo.ModeInfo.YMode == Av1PredictionMode.DC &&
Math.Max(blockSize.GetWidth(), blockSize.GetHeight()) <= 32) partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) == 0 &&
Math.Max(partitionInfo.ModeInfo.BlockSize.GetWidth(), partitionInfo.ModeInfo.BlockSize.GetHeight()) <= 32)
{ {
useFilterIntra = reader.ReadUseFilterUltra(); partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = reader.ReadUseFilterUltra(partitionInfo.ModeInfo.BlockSize);
if (useFilterIntra) 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. // TODO: Implement.
throw new NotImplementedException(); throw new NotImplementedException();
private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader) => private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) =>
// TODO: Implement. // TODO: Implement.
throw new NotImplementedException(); 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; int angleDelta = 0;
if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(this.yMode)) if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(mode))
{ {
int angleDeltaY = reader.ReadAngleDelta(this.yMode); int symbol = reader.ReadAngleDelta(mode);
this.angleDeltaY = angleDeltaY - ObuConstants.MaxAngleDelta; angleDelta = symbol - ObuConstants.MaxAngleDelta;
} }
}
private void IntraAngleInfoUv(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) return angleDelta;
{
this.angleDeltaUv = 0;
if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(this.uvMode))
{
int angleDeltaUv = reader.ReadAngleDelta(this.uvMode);
this.angleDeltaUv = angleDeltaUv - ObuConstants.MaxAngleDelta;
}
} }
private static bool IsDirectionalMode(Av1PredictionMode mode) private static bool IsDirectionalMode(Av1PredictionMode mode)
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees; => 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) 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 prevUL = -1;
int prevU = -1; int prevU = -1;
int prevL = -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) if (prevU == -1)
{ {
pred = prevL == -1 ? 0 : prevL; predictor = prevL == -1 ? 0 : prevL;
} }
else if (prevL == -1) else if (prevL == -1)
{ {
pred = prevU; predictor = prevU;
} }
else 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 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; 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) 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; return;
} }
int cdefSize4 = Av1BlockSize.Block64x64.Get4x4WideCount(); int cdefSize4 = Av1BlockSize.Block64x64.Get4x4WideCount();
int cdefMask4 = ~(cdefSize4 - 1); int cdefMask4 = ~(cdefSize4 - 1);
int r = rowIndex & cdefMask4; int r = partitionInfo.RowIndex & cdefMask4;
int c = columnIndex & cdefMask4; int c = partitionInfo.ColumnIndex & cdefMask4;
if (this.cdefIndex[r][c] == -1) if (partitionInfo.CdefStrength[r][c] == -1)
{ {
this.cdefIndex[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount); partitionInfo.CdefStrength[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount);
int w4 = blockSize.Get4x4WideCount(); if (this.SequenceHeader.SuperBlockSize == Av1BlockSize.Block128x128)
int h4 = blockSize.Get4x4HighCount();
for (int i = r; i < r + h4; i += cdefSize4)
{ {
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; 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; return;
} }
if (this.readDeltas && this.FrameInfo.DeltaLoopFilterParameters.IsPresent) if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent)
{ {
int frameLoopFilterCount = 1; int frameLoopFilterCount = 1;
if (this.FrameInfo.DeltaLoopFilterParameters.Multi) 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 && 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 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; 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; return;
} }
if (this.readDeltas) if (partitionInfo.ModeInfo.BlockSize != this.SequenceHeader.SuperBlockSize || !partitionInfo.ModeInfo.Skip)
{ {
int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute(); int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute();
if (deltaQuantizerAbsolute == ObuConstants.DeltaQuantizerSmall) if (deltaQuantizerAbsolute == ObuConstants.DeltaQuantizerSmall)
@ -829,6 +861,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0; bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0;
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute; int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << this.deltaQuantizerResolution)); this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << this.deltaQuantizerResolution));
partitionInfo.SuperblockInfo.SuperblockDeltaQ[0] = this.currentQuantizerIndex;
} }
} }
} }

Loading…
Cancel
Save