Browse Source

Bug fixes in OBU parsing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
60869e1f47
  1. 2
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs
  2. 5
      src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs
  3. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
  4. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuDeltaLoopFilterParameters.cs
  5. 4
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs
  6. 122
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  7. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
  8. 15
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
  9. 37
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
  10. 6
      tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

2
src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs

@ -49,7 +49,7 @@ internal ref struct Av1BitStreamReader
public uint ReadLiteral(int bitCount) public uint ReadLiteral(int bitCount)
{ {
DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount)); DebugGuard.MustBeBetweenOrEqualTo(bitCount, 1, 32, nameof(bitCount));
uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount); uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount);
this.bitOffset += bitCount; this.bitOffset += bitCount;

5
src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs

@ -18,8 +18,6 @@ internal class Av1Decoder : IAv1TileDecoder
public ObuSequenceHeader? SequenceHeader { get; private set; } public ObuSequenceHeader? SequenceHeader { get; private set; }
public ObuTileGroupHeader? TilesHeader { get; private set; }
public void Decode(Span<byte> buffer) public void Decode(Span<byte> buffer)
{ {
Av1BitStreamReader reader = new(buffer); Av1BitStreamReader reader = new(buffer);
@ -31,8 +29,7 @@ internal class Av1Decoder : IAv1TileDecoder
{ {
this.SequenceHeader = this.obuReader.SequenceHeader; this.SequenceHeader = this.obuReader.SequenceHeader;
this.FrameHeader = this.obuReader.FrameHeader; this.FrameHeader = this.obuReader.FrameHeader;
this.TilesHeader = this.obuReader.TileGroupHeader; this.tileDecoder = new Av1TileDecoder(this.SequenceHeader!, this.FrameHeader!);
this.tileDecoder = new Av1TileDecoder(this.SequenceHeader!, this.FrameHeader!, this.TilesHeader!);
} }
public void DecodeTile(Span<byte> tileData, int tileNum) public void DecodeTile(Span<byte> tileData, int tileNum)

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

@ -29,8 +29,6 @@ internal class ObuColorConfig
public int BitDepth { get; set; } public int BitDepth { get; set; }
public bool HasSeparateUvDeltaQ { get; set; }
public Av1ColorFormat GetColorFormat() public Av1ColorFormat GetColorFormat()
{ {
Av1ColorFormat format = Av1ColorFormat.Yuv400; Av1ColorFormat format = Av1ColorFormat.Yuv400;

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

@ -9,5 +9,5 @@ internal class ObuDeltaLoopFilterParameters
public int Resolution { get; internal set; } public int Resolution { get; internal set; }
public bool Multi { get; internal set; } public bool IsMulti { get; internal set; }
} }

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

@ -6,4 +6,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopFilterParameters internal class ObuLoopFilterParameters
{ {
public int[] FilterLevel { get; internal set; } = new int[2]; public int[] FilterLevel { get; internal set; } = new int[2];
public int FilterLevelU { get; internal set; }
public int FilterLevelV { get; internal set; }
public int SharpnessLevel { get; internal set; }
public bool ReferenceDeltaModeEnabled { get; internal set; }
} }

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
@ -11,8 +12,6 @@ internal class ObuReader
public ObuFrameHeader? FrameHeader { get; set; } public ObuFrameHeader? FrameHeader { get; set; }
public ObuTileGroupHeader? TileGroupHeader { get; set; }
/// <summary> /// <summary>
/// Decode all OBU's in a frame. /// Decode all OBU's in a frame.
/// </summary> /// </summary>
@ -91,7 +90,6 @@ internal class ObuReader
throw new InvalidImageContentException("Corrupt frame"); throw new InvalidImageContentException("Corrupt frame");
} }
this.TileGroupHeader = new();
this.ReadTileGroup(ref reader, decoder, header, out frameDecodingFinished); this.ReadTileGroup(ref reader, decoder, header, out frameDecodingFinished);
if (frameDecodingFinished) if (frameDecodingFinished)
{ {
@ -202,8 +200,9 @@ internal class ObuReader
} }
} }
private static void ComputeImageSize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private void ComputeImageSize(ObuSequenceHeader sequenceHeader)
{ {
ObuFrameHeader frameInfo = this.FrameHeader!;
frameInfo.ModeInfoColumnCount = 2 * ((frameInfo.FrameSize.FrameWidth + 7) >> 3); frameInfo.ModeInfoColumnCount = 2 * ((frameInfo.FrameSize.FrameWidth + 7) >> 3);
frameInfo.ModeInfoRowCount = 2 * ((frameInfo.FrameSize.FrameHeight + 7) >> 3); frameInfo.ModeInfoRowCount = 2 * ((frameInfo.FrameSize.FrameHeight + 7) >> 3);
frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, Av1Constants.MaxSuperBlockSizeLog2) >> Av1Constants.ModeInfoSizeLog2; frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, Av1Constants.MaxSuperBlockSizeLog2) >> Av1Constants.ModeInfoSizeLog2;
@ -366,7 +365,7 @@ internal class ObuReader
} }
} }
colorConfig.HasSeparateUvDeltaQ = reader.ReadBoolean(); colorConfig.HasSeparateUvDelta = reader.ReadBoolean();
return colorConfig; return colorConfig;
} }
@ -387,8 +386,10 @@ internal class ObuReader
} }
} }
private static void ReadSuperResolutionParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private void ReadSuperResolutionParameters(ref Av1BitStreamReader reader)
{ {
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
bool useSuperResolution = false; bool useSuperResolution = false;
if (sequenceHeader.EnableSuperResolution) if (sequenceHeader.EnableSuperResolution)
{ {
@ -417,8 +418,9 @@ internal class ObuReader
} }
} }
private static void ReadRenderSize(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo) private void ReadRenderSize(ref Av1BitStreamReader reader)
{ {
ObuFrameHeader frameInfo = this.FrameHeader!;
bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean(); bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean();
if (renderSizeAndFrameSizeDifferent) if (renderSizeAndFrameSizeDifferent)
{ {
@ -432,8 +434,10 @@ internal class ObuReader
} }
} }
private static void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) private void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag)
{ {
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
bool foundReference = false; bool foundReference = false;
for (int i = 0; i < Av1Constants.ReferencesPerFrame; i++) for (int i = 0; i < Av1Constants.ReferencesPerFrame; i++)
{ {
@ -447,18 +451,20 @@ internal class ObuReader
if (!foundReference) if (!foundReference)
{ {
ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); this.ReadFrameSize(ref reader, frameSizeOverrideFlag);
ReadRenderSize(ref reader, frameInfo); this.ReadRenderSize(ref reader);
} }
else else
{ {
ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo); this.ReadSuperResolutionParameters(ref reader);
ComputeImageSize(sequenceHeader, frameInfo); this.ComputeImageSize(sequenceHeader);
} }
} }
private static void ReadFrameSize(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) private void ReadFrameSize(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag)
{ {
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
if (frameSizeOverrideFlag) if (frameSizeOverrideFlag)
{ {
frameInfo.FrameSize.FrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1; frameInfo.FrameSize.FrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1;
@ -470,8 +476,8 @@ internal class ObuReader
frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight; frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight;
} }
ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo); this.ReadSuperResolutionParameters(ref reader);
ComputeImageSize(sequenceHeader, frameInfo); this.ComputeImageSize(sequenceHeader);
} }
private static ObuTileGroupHeader ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private static ObuTileGroupHeader ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
@ -670,11 +676,8 @@ internal class ObuReader
{ {
frameInfo.ReferenceValid = new bool[Av1Constants.ReferenceFrameCount]; frameInfo.ReferenceValid = new bool[Av1Constants.ReferenceFrameCount];
frameInfo.ReferenceOrderHint = new bool[Av1Constants.ReferenceFrameCount]; frameInfo.ReferenceOrderHint = new bool[Av1Constants.ReferenceFrameCount];
for (int i = 0; i < Av1Constants.ReferenceFrameCount; i++) Array.Fill(frameInfo.ReferenceValid, false);
{ Array.Fill(frameInfo.ReferenceOrderHint, false);
frameInfo.ReferenceValid[i] = false;
frameInfo.ReferenceOrderHint[i] = false;
}
} }
frameInfo.DisableCdfUpdate = reader.ReadBoolean(); frameInfo.DisableCdfUpdate = reader.ReadBoolean();
@ -761,7 +764,14 @@ internal class ObuReader
frameSizeOverrideFlag = reader.ReadBoolean(); frameSizeOverrideFlag = reader.ReadBoolean();
} }
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); if (sequenceHeader.OrderHintInfo.OrderHintBits > 0)
{
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
}
else
{
frameInfo.OrderHint = 0;
}
if (isIntraFrame || frameInfo.ErrorResilientMode) if (isIntraFrame || frameInfo.ErrorResilientMode)
{ {
@ -807,8 +817,8 @@ internal class ObuReader
if (isIntraFrame) if (isIntraFrame)
{ {
ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); this.ReadFrameSize(ref reader, frameSizeOverrideFlag);
ReadRenderSize(ref reader, frameInfo); this.ReadRenderSize(ref reader);
if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0) if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0)
{ {
if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth) if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth)
@ -852,6 +862,9 @@ internal class ObuReader
int tilesCount = frameInfo.TilesInfo.TileColumnCount * frameInfo.TilesInfo.TileRowCount; int tilesCount = frameInfo.TilesInfo.TileColumnCount * frameInfo.TilesInfo.TileRowCount;
frameInfo.CodedLossless = true; frameInfo.CodedLossless = true;
frameInfo.SegmentationParameters.QMLevel[0] = new int[Av1Constants.MaxSegmentCount];
frameInfo.SegmentationParameters.QMLevel[1] = new int[Av1Constants.MaxSegmentCount];
frameInfo.SegmentationParameters.QMLevel[2] = new int[Av1Constants.MaxSegmentCount];
for (int segmentId = 0; segmentId < Av1Constants.MaxSegmentCount; segmentId++) for (int segmentId = 0; segmentId < Av1Constants.MaxSegmentCount; segmentId++)
{ {
int qIndex = GetQIndex(frameInfo.SegmentationParameters, segmentId, frameInfo.QuantizationParameters.BaseQIndex); int qIndex = GetQIndex(frameInfo.SegmentationParameters, segmentId, frameInfo.QuantizationParameters.BaseQIndex);
@ -871,19 +884,24 @@ internal class ObuReader
{ {
if (frameInfo.LosslessArray[segmentId]) if (frameInfo.LosslessArray[segmentId])
{ {
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = 15; frameInfo.SegmentationParameters.QMLevel[0][segmentId] = 15;
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = 15; frameInfo.SegmentationParameters.QMLevel[1][segmentId] = 15;
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = 15; frameInfo.SegmentationParameters.QMLevel[2][segmentId] = 15;
} }
else else
{ {
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y]; frameInfo.SegmentationParameters.QMLevel[0][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y];
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U]; frameInfo.SegmentationParameters.QMLevel[1][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U];
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V]; frameInfo.SegmentationParameters.QMLevel[2][segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V];
} }
} }
} }
if (frameInfo.CodedLossless)
{
DebugGuard.IsFalse(frameInfo.DeltaQParameters.IsPresent, nameof(frameInfo.DeltaQParameters.IsPresent), "No Delta Q parameters are allowed for lossless frame.");
}
frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth; frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth;
ReadLoopFilterParameters(ref reader, sequenceHeader, frameInfo, planesCount); ReadLoopFilterParameters(ref reader, sequenceHeader, frameInfo, planesCount);
ReadCdefParameters(ref reader, sequenceHeader, frameInfo, planesCount); ReadCdefParameters(ref reader, sequenceHeader, frameInfo, planesCount);
@ -896,6 +914,10 @@ internal class ObuReader
{ {
frameInfo.AllowWarpedMotion = false; frameInfo.AllowWarpedMotion = false;
} }
else
{
frameInfo.AllowWarpedMotion = reader.ReadBoolean();
}
frameInfo.ReducedTransformSet = reader.ReadBoolean(); frameInfo.ReducedTransformSet = reader.ReadBoolean();
ReadGlobalMotionParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame); ReadGlobalMotionParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame);
@ -945,8 +967,7 @@ internal class ObuReader
{ {
ObuSequenceHeader sequenceHeader = this.SequenceHeader!; ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!; ObuFrameHeader frameInfo = this.FrameHeader!;
ObuTileGroupHeader tileInfo = this.TileGroupHeader!; ObuTileGroupHeader tileInfo = this.FrameHeader!.TilesInfo;
this.TileGroupHeader = tileInfo;
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount; int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = reader.BitPosition; int startBitPosition = reader.BitPosition;
bool tileStartAndEndPresentFlag = false; bool tileStartAndEndPresentFlag = false;
@ -1013,7 +1034,7 @@ internal class ObuReader
int deltaQ = 0; int deltaQ = 0;
if (reader.ReadBoolean()) if (reader.ReadBoolean())
{ {
deltaQ = reader.ReadSignedFromUnsigned(6); deltaQ = reader.ReadSignedFromUnsigned(7);
} }
return deltaQ; return deltaQ;
@ -1038,7 +1059,7 @@ internal class ObuReader
{ {
frameInfo.DeltaLoopFilterParameters.IsPresent = false; frameInfo.DeltaLoopFilterParameters.IsPresent = false;
frameInfo.DeltaLoopFilterParameters.Resolution = 0; frameInfo.DeltaLoopFilterParameters.Resolution = 0;
frameInfo.DeltaLoopFilterParameters.Multi = false; frameInfo.DeltaLoopFilterParameters.IsMulti = false;
if (frameInfo.DeltaQParameters.IsPresent) if (frameInfo.DeltaQParameters.IsPresent)
{ {
if (!frameInfo.AllowIntraBlockCopy) if (!frameInfo.AllowIntraBlockCopy)
@ -1049,7 +1070,7 @@ internal class ObuReader
if (frameInfo.DeltaLoopFilterParameters.IsPresent) if (frameInfo.DeltaLoopFilterParameters.IsPresent)
{ {
frameInfo.DeltaLoopFilterParameters.Resolution = (int)reader.ReadLiteral(2); frameInfo.DeltaLoopFilterParameters.Resolution = (int)reader.ReadLiteral(2);
frameInfo.DeltaLoopFilterParameters.Multi = reader.ReadBoolean(); frameInfo.DeltaLoopFilterParameters.IsMulti = reader.ReadBoolean();
} }
} }
} }
@ -1062,6 +1083,11 @@ internal class ObuReader
if (planesCount > 1) if (planesCount > 1)
{ {
bool areUvDeltaDifferent = false; bool areUvDeltaDifferent = false;
if (colorInfo.HasSeparateUvDelta)
{
areUvDeltaDifferent = reader.ReadBoolean();
}
quantParams.DeltaQDc[(int)Av1Plane.U] = ReadDeltaQ(ref reader); quantParams.DeltaQDc[(int)Av1Plane.U] = ReadDeltaQ(ref reader);
quantParams.DeltaQAc[(int)Av1Plane.U] = ReadDeltaQ(ref reader); quantParams.DeltaQAc[(int)Av1Plane.U] = ReadDeltaQ(ref reader);
if (areUvDeltaDifferent) if (areUvDeltaDifferent)
@ -1088,7 +1114,7 @@ internal class ObuReader
{ {
quantParams.QMatrix[(int)Av1Plane.Y] = (int)reader.ReadLiteral(4); quantParams.QMatrix[(int)Av1Plane.Y] = (int)reader.ReadLiteral(4);
quantParams.QMatrix[(int)Av1Plane.U] = (int)reader.ReadLiteral(4); quantParams.QMatrix[(int)Av1Plane.U] = (int)reader.ReadLiteral(4);
if (!colorInfo.HasSeparateUvDeltaQ) if (!colorInfo.HasSeparateUvDelta)
{ {
quantParams.QMatrix[(int)Av1Plane.V] = quantParams.QMatrix[(int)Av1Plane.U]; quantParams.QMatrix[(int)Av1Plane.V] = quantParams.QMatrix[(int)Av1Plane.U];
} }
@ -1115,14 +1141,31 @@ internal class ObuReader
private static void ReadLoopFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadLoopFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
frameInfo.LoopFilterParameters.FilterLevel = new int[2];
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy)
{ {
frameInfo.LoopFilterParameters.FilterLevel[0] = 0;
frameInfo.LoopFilterParameters.FilterLevel[1] = 0;
return; return;
} }
// TODO: Parse more stuff. frameInfo.LoopFilterParameters.FilterLevel[0] = (int)reader.ReadLiteral(6);
frameInfo.LoopFilterParameters.FilterLevel[1] = (int)reader.ReadLiteral(6);
if (planesCount > 1)
{
if (frameInfo.LoopFilterParameters.FilterLevel[0] > 0 || frameInfo.LoopFilterParameters.FilterLevel[1] > 0)
{
frameInfo.LoopFilterParameters.FilterLevelU = (int)reader.ReadLiteral(6);
frameInfo.LoopFilterParameters.FilterLevelV = (int)reader.ReadLiteral(6);
}
}
frameInfo.LoopFilterParameters.SharpnessLevel = (int)reader.ReadLiteral(3);
frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled = reader.ReadBoolean();
if (frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled)
{
// TODO: Implement.
throw new NotImplementedException();
}
} }
private static void ReadTransformMode(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo) private static void ReadTransformMode(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo)
@ -1238,6 +1281,7 @@ internal class ObuReader
} }
// Not applicable for INTRA frames. // Not applicable for INTRA frames.
throw new NotImplementedException();
} }
private static ObuReferenceMode ReadFrameReferenceMode(ref Av1BitStreamReader reader, bool isIntraFrame) private static ObuReferenceMode ReadFrameReferenceMode(ref Av1BitStreamReader reader, bool isIntraFrame)
@ -1286,7 +1330,7 @@ internal class ObuReader
} }
// TODO: Implement parsing. // TODO: Implement parsing.
return grainParams; throw new NotImplementedException();
} }
private static bool IsValidSequenceLevel(int sequenceLevelIndex) private static bool IsValidSequenceLevel(int sequenceLevelIndex)

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

@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuSegmentationParameters internal class ObuSegmentationParameters
{ {
public int[,] QMLevel { get; internal set; } = new int[3, Av1Constants.MaxSegmentCount]; public int[][] QMLevel { get; internal set; } = new int[3][];
public bool[,] FeatureEnabled { get; internal set; } = new bool[Av1Constants.MaxSegmentCount, Av1Constants.SegmentationLevelMax]; public bool[,] FeatureEnabled { get; internal set; } = new bool[Av1Constants.MaxSegmentCount, Av1Constants.SegmentationLevelMax];

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

@ -10,7 +10,7 @@ internal class ObuWriter
/// <summary> /// <summary>
/// Encode a single frame into OBU's. /// Encode a single frame into OBU's.
/// </summary> /// </summary>
public static void Write(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo) public static void Write(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{ {
MemoryStream bufferStream = new(100); MemoryStream bufferStream = new(100);
Av1BitStreamWriter writer = new(bufferStream); Av1BitStreamWriter writer = new(bufferStream);
@ -26,7 +26,7 @@ internal class ObuWriter
WriteObuHeaderAndSize(stream, ObuType.FrameHeader, bufferStream.GetBuffer(), (int)bufferStream.Position); WriteObuHeaderAndSize(stream, ObuType.FrameHeader, bufferStream.GetBuffer(), (int)bufferStream.Position);
bufferStream.Position = 0; bufferStream.Position = 0;
WriteTileGroup(ref writer, tileInfo); WriteTileGroup(ref writer, frameInfo.TilesInfo);
writer.Flush(); writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.TileGroup, bufferStream.GetBuffer(), (int)bufferStream.Position); WriteObuHeaderAndSize(stream, ObuType.TileGroup, bufferStream.GetBuffer(), (int)bufferStream.Position);
} }
@ -145,7 +145,7 @@ internal class ObuWriter
} }
} }
writer.WriteBoolean(colorConfig.HasSeparateUvDeltaQ); writer.WriteBoolean(colorConfig.HasSeparateUvDelta);
} }
private static void WriteBitDepth(ref Av1BitStreamWriter writer, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader) private static void WriteBitDepth(ref Av1BitStreamWriter writer, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader)
@ -520,7 +520,7 @@ internal class ObuWriter
if (frameInfo.DeltaLoopFilterParameters.IsPresent) if (frameInfo.DeltaLoopFilterParameters.IsPresent)
{ {
writer.WriteLiteral((uint)frameInfo.DeltaLoopFilterParameters.Resolution, 2); writer.WriteLiteral((uint)frameInfo.DeltaLoopFilterParameters.Resolution, 2);
writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.Multi); writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.IsMulti);
} }
} }
} }
@ -535,6 +535,11 @@ internal class ObuWriter
if (planesCount > 1) if (planesCount > 1)
{ {
bool areUvDeltaDifferent = false; bool areUvDeltaDifferent = false;
if (colorInfo.HasSeparateUvDelta)
{
writer.WriteBoolean(colorInfo.HasSeparateUvDelta);
}
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.U]); WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.U]);
WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.U]); WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.U]);
if (areUvDeltaDifferent) if (areUvDeltaDifferent)
@ -549,7 +554,7 @@ internal class ObuWriter
{ {
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.Y], 4); writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.Y], 4);
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.U], 4); writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.U], 4);
if (colorInfo.HasSeparateUvDeltaQ) if (colorInfo.HasSeparateUvDelta)
{ {
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.V], 4); writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.V], 4);
} }

37
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs

@ -30,11 +30,10 @@ internal class Av1TileDecoder : IAv1TileDecoder
private int deltaLoopFilterResolution = -1; private int deltaLoopFilterResolution = -1;
private int deltaQuantizerResolution = -1; private int deltaQuantizerResolution = -1;
public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo) public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{ {
this.FrameInfo = frameInfo; this.FrameInfo = frameInfo;
this.SequenceHeader = sequenceHeader; this.SequenceHeader = sequenceHeader;
this.TileInfo = tileInfo;
// init_main_frame_ctxt // init_main_frame_ctxt
this.FrameBuffer = new(this.SequenceHeader); this.FrameBuffer = new(this.SequenceHeader);
@ -50,17 +49,15 @@ internal class Av1TileDecoder : IAv1TileDecoder
public ObuSequenceHeader SequenceHeader { get; } public ObuSequenceHeader SequenceHeader { get; }
public ObuTileGroupHeader TileInfo { get; }
public Av1FrameBuffer FrameBuffer { get; } public Av1FrameBuffer FrameBuffer { get; }
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 tileColumnIndex = tileNum % this.TileInfo.TileColumnCount; int tileColumnIndex = tileNum % this.FrameInfo.TilesInfo.TileColumnCount;
int tileRowIndex = tileNum / this.TileInfo.TileColumnCount; int tileRowIndex = tileNum / this.FrameInfo.TilesInfo.TileColumnCount;
this.aboveContext.Clear(this.TileInfo.TileColumnStartModeInfo[tileColumnIndex], this.TileInfo.TileColumnStartModeInfo[tileColumnIndex - 1]); this.aboveContext.Clear(this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex], this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex - 1]);
this.ClearLoopFilterDelta(); this.ClearLoopFilterDelta();
int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3; int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
this.referenceSgrXqd = new int[planesCount][]; this.referenceSgrXqd = new int[planesCount][];
@ -79,11 +76,11 @@ internal class Av1TileDecoder : IAv1TileDecoder
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
int superBlock4x4Size = superBlockSize.Get4x4WideCount(); int superBlock4x4Size = superBlockSize.Get4x4WideCount();
for (int row = this.TileInfo.TileRowStartModeInfo[tileRowIndex]; row < this.TileInfo.TileRowStartModeInfo[tileRowIndex + 1]; row += this.SequenceHeader.ModeInfoSize) for (int row = this.FrameInfo.TilesInfo.TileRowStartModeInfo[tileRowIndex]; row < this.FrameInfo.TilesInfo.TileRowStartModeInfo[tileRowIndex + 1]; row += this.SequenceHeader.ModeInfoSize)
{ {
int superBlockRow = row << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2; int superBlockRow = row << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2;
this.leftContext.Clear(); this.leftContext.Clear();
for (int column = this.TileInfo.TileColumnStartModeInfo[tileColumnIndex]; column < this.TileInfo.TileColumnStartModeInfo[tileColumnIndex + 1]; column += this.SequenceHeader.ModeInfoSize) for (int column = this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex]; column < this.FrameInfo.TilesInfo.TileColumnStartModeInfo[tileColumnIndex + 1]; column += this.SequenceHeader.ModeInfoSize)
{ {
int superBlockColumn = column << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2; int superBlockColumn = column << Av1Constants.ModeInfoSizeLog2 >> this.SequenceHeader.SuperBlockSizeLog2;
bool subSamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; bool subSamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;
@ -165,8 +162,8 @@ internal class Av1TileDecoder : IAv1TileDecoder
private void ParsePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo) private void ParsePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo)
{ {
if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] || if (rowIndex >= this.FrameInfo.TilesInfo.TileRowStartModeInfo[rowIndex] ||
columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex]) columnIndex >= this.FrameInfo.TilesInfo.TileColumnStartModeInfo[columnIndex])
{ {
return; return;
} }
@ -339,8 +336,8 @@ internal class Av1TileDecoder : IAv1TileDecoder
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
int txsWide = planeBlockSize.GetWidth() >> 2; int txsWide = planeBlockSize.GetWidth() >> 2;
int txsHigh = planeBlockSize.GetHeight() >> 2; int txsHigh = planeBlockSize.GetHeight() >> 2;
int aboveOffset = (partitionInfo.ColumnIndex - this.TileInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0); int aboveOffset = (partitionInfo.ColumnIndex - this.FrameInfo.TilesInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0);
int leftOffset = (partitionInfo.RowIndex - this.TileInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0); int leftOffset = (partitionInfo.RowIndex - this.FrameInfo.TilesInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0);
int[] aboveContext = this.aboveContext.AboveContext[i + aboveOffset]; int[] aboveContext = this.aboveContext.AboveContext[i + aboveOffset];
int[] leftContext = this.leftContext.LeftContext[i + leftOffset]; int[] leftContext = this.leftContext.LeftContext[i + leftOffset];
Array.Fill(aboveContext, 0); Array.Fill(aboveContext, 0);
@ -870,7 +867,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent) if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent)
{ {
int frameLoopFilterCount = 1; int frameLoopFilterCount = 1;
if (this.FrameInfo.DeltaLoopFilterParameters.Multi) if (this.FrameInfo.DeltaLoopFilterParameters.IsMulti)
{ {
frameLoopFilterCount = this.SequenceHeader.ColorConfig.ChannelCount > 1 ? Av1Constants.FrameLoopFilterCount : Av1Constants.FrameLoopFilterCount - 2; frameLoopFilterCount = this.SequenceHeader.ColorConfig.ChannelCount > 1 ? Av1Constants.FrameLoopFilterCount : Av1Constants.FrameLoopFilterCount - 2;
} }
@ -942,10 +939,10 @@ internal class Av1TileDecoder : IAv1TileDecoder
} }
private bool IsInside(int rowIndex, int columnIndex) => private bool IsInside(int rowIndex, int columnIndex) =>
columnIndex >= this.TileInfo.TileColumnCount && columnIndex >= this.FrameInfo.TilesInfo.TileColumnCount &&
columnIndex < this.TileInfo.TileColumnCount && columnIndex < this.FrameInfo.TilesInfo.TileColumnCount &&
rowIndex >= this.TileInfo.TileRowCount && rowIndex >= this.FrameInfo.TilesInfo.TileRowCount &&
rowIndex < this.TileInfo.TileRowCount; rowIndex < this.FrameInfo.TilesInfo.TileRowCount;
/* /*
private static bool IsChroma(int rowIndex, int columnIndex, Av1BlockModeInfo blockMode, bool subSamplingX, bool subSamplingY) private static bool IsChroma(int rowIndex, int columnIndex, Av1BlockModeInfo blockMode, bool subSamplingX, bool subSamplingY)
@ -961,8 +958,8 @@ internal class Av1TileDecoder : IAv1TileDecoder
{ {
// Maximum partition point is 8x8. Offset the log value occordingly. // Maximum partition point is 8x8. Offset the log value occordingly.
int blockSizeLog = blockSize.Get4x4WidthLog2() - Av1BlockSize.Block8x8.Get4x4WidthLog2(); int blockSizeLog = blockSize.Get4x4WidthLog2() - Av1BlockSize.Block8x8.Get4x4WidthLog2();
int aboveCtx = this.aboveContext.PartitionWidth + columnIndex - this.TileInfo.TileColumnStartModeInfo[columnIndex]; int aboveCtx = this.aboveContext.PartitionWidth + columnIndex - this.FrameInfo.TilesInfo.TileColumnStartModeInfo[columnIndex];
int leftCtx = this.leftContext.PartitionHeight + rowIndex - this.TileInfo.TileRowStartModeInfo[rowIndex]; int leftCtx = this.leftContext.PartitionHeight + rowIndex - this.FrameInfo.TilesInfo.TileRowStartModeInfo[rowIndex];
int above = (aboveCtx >> blockSizeLog) & 0x1; int above = (aboveCtx >> blockSizeLog) & 0x1;
int left = (leftCtx >> blockSizeLog) & 0x1; int left = (leftCtx >> blockSizeLog) & 0x1;
return (left * 2) + above + (blockSizeLog * PartitionProbabilitySet); return (left * 2) + above + (blockSizeLog * PartitionProbabilitySet);

6
tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

@ -31,7 +31,7 @@ public class ObuFrameHeaderTests
// Assert // Assert
Assert.NotNull(obuReader.SequenceHeader); Assert.NotNull(obuReader.SequenceHeader);
Assert.NotNull(obuReader.FrameHeader); Assert.NotNull(obuReader.FrameHeader);
Assert.NotNull(obuReader.TileGroupHeader); Assert.NotNull(obuReader.FrameHeader.TilesInfo);
} }
/* [Theory] /* [Theory]
@ -78,7 +78,7 @@ public class ObuFrameHeaderTests
MemoryStream encoded = new(); MemoryStream encoded = new();
// Act 2 // Act 2
ObuWriter.Write(encoded, obuReader1.SequenceHeader, obuReader1.FrameHeader, obuReader1.TileGroupHeader); ObuWriter.Write(encoded, obuReader1.SequenceHeader, obuReader1.FrameHeader);
// Assign 2 // Assign 2
Span<byte> encodedBuffer = encoded.ToArray(); Span<byte> encodedBuffer = encoded.ToArray();
@ -93,7 +93,7 @@ public class ObuFrameHeaderTests
Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig)); Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig));
Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader), PrettyPrintProperties(obuReader2.SequenceHeader)); Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader), PrettyPrintProperties(obuReader2.SequenceHeader));
Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader), PrettyPrintProperties(obuReader2.FrameHeader)); Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader), PrettyPrintProperties(obuReader2.FrameHeader));
Assert.Equal(PrettyPrintProperties(obuReader1.TileGroupHeader), PrettyPrintProperties(obuReader2.TileGroupHeader)); Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader.TilesInfo), PrettyPrintProperties(obuReader2.FrameHeader.TilesInfo));
} }
private static string PrettyPrintProperties(object obj) private static string PrettyPrintProperties(object obj)

Loading…
Cancel
Save