Browse Source

Obu reading and writing improvements

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
23bf5dede0
  1. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
  2. 11
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationItem.cs
  3. 18
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs
  4. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuQuantizationParameters.cs
  5. 56
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  6. 246
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
  7. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
  8. 7
      tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

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

@ -37,7 +37,7 @@ internal class ObuFrameHeader
public ObuLoopFilterParameters LoopFilterParameters { get; set; } = new ObuLoopFilterParameters(); public ObuLoopFilterParameters LoopFilterParameters { get; set; } = new ObuLoopFilterParameters();
public ObuLoopRestorationParameters[] LoopRestorationParameters { get; set; } = new ObuLoopRestorationParameters[3]; public ObuLoopRestorationParameters LoopRestorationParameters { get; set; } = new ObuLoopRestorationParameters();
public ObuConstraintDirectionalEnhancementFilterParameters CdefParameters { get; set; } = new ObuConstraintDirectionalEnhancementFilterParameters(); public ObuConstraintDirectionalEnhancementFilterParameters CdefParameters { get; set; } = new ObuConstraintDirectionalEnhancementFilterParameters();

11
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationItem.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopRestorationItem
{
internal int Size { get; set; }
internal ObuRestorationType Type { get; set; } = ObuRestorationType.None;
}

18
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs

@ -5,7 +5,21 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopRestorationParameters internal class ObuLoopRestorationParameters
{ {
internal int Size { get; set; } internal ObuLoopRestorationParameters()
{
this.Items = new ObuLoopRestorationItem[3];
this.Items[0] = new();
this.Items[1] = new();
this.Items[2] = new();
}
internal ObuRestorationType Type { get; set; } = ObuRestorationType.None; internal bool UsesLoopRestoration { get; set; }
internal bool UsesChromaLoopRestoration { get; set; }
internal ObuLoopRestorationItem[] Items { get; }
internal int UnitShift { get; set; }
internal int UVShift { get; set; }
} }

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

@ -16,4 +16,6 @@ internal class ObuQuantizationParameters
public int[] DeltaQAc { get; internal set; } = new int[3]; public int[] DeltaQAc { get; internal set; } = new int[3];
public int[] QMatrix { get; internal set; } = new int[3]; public int[] QMatrix { get; internal set; } = new int[3];
public bool HasSeparateUvDelta { get; internal set; }
} }

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

@ -1272,9 +1272,9 @@ internal class ObuReader
frameInfo.CdefParameters.YStrength[0] != 0 || frameInfo.CdefParameters.YStrength[0] != 0 ||
frameInfo.CdefParameters.UvStrength[0] != 0)); frameInfo.CdefParameters.UvStrength[0] != 0));
bool doLoopRestoration = noIbc && bool doLoopRestoration = noIbc &&
(frameInfo.LoopRestorationParameters[(int)Av1Plane.Y].Type != ObuRestorationType.None || (frameInfo.LoopRestorationParameters.Items[(int)Av1Plane.Y].Type != ObuRestorationType.None ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.U].Type != ObuRestorationType.None || frameInfo.LoopRestorationParameters.Items[(int)Av1Plane.U].Type != ObuRestorationType.None ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.V].Type != ObuRestorationType.None); frameInfo.LoopRestorationParameters.Items[(int)Av1Plane.V].Type != ObuRestorationType.None);
for (int tileNum = tileGroupStart; tileNum <= tileGroupEnd; tileNum++) for (int tileNum = tileGroupStart; tileNum <= tileGroupEnd; tileNum++)
{ {
@ -1363,15 +1363,15 @@ internal class ObuReader
quantParams.DeltaQAc[(int)Av1Plane.Y] = 0; quantParams.DeltaQAc[(int)Av1Plane.Y] = 0;
if (planesCount > 1) if (planesCount > 1)
{ {
bool areUvDeltaDifferent = false; quantParams.HasSeparateUvDelta = false;
if (colorInfo.HasSeparateUvDelta) if (colorInfo.HasSeparateUvDelta)
{ {
areUvDeltaDifferent = reader.ReadBoolean(); quantParams.HasSeparateUvDelta = 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 (quantParams.HasSeparateUvDelta)
{ {
quantParams.DeltaQDc[(int)Av1Plane.V] = ReadDeltaQ(ref reader); quantParams.DeltaQDc[(int)Av1Plane.V] = ReadDeltaQ(ref reader);
quantParams.DeltaQAc[(int)Av1Plane.V] = ReadDeltaQ(ref reader); quantParams.DeltaQAc[(int)Av1Plane.V] = ReadDeltaQ(ref reader);
@ -1504,7 +1504,6 @@ internal class ObuReader
private void ReadLoopFilterParameters(ref Av1BitStreamReader reader, int planesCount) private void ReadLoopFilterParameters(ref Av1BitStreamReader reader, int planesCount)
{ {
ObuFrameHeader frameInfo = this.FrameHeader!; ObuFrameHeader frameInfo = this.FrameHeader!;
frameInfo.LoopFilterParameters.FilterLevel = new int[2];
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy)
{ {
return; return;
@ -1575,58 +1574,51 @@ internal class ObuReader
/// </summary> /// </summary>
private static void ReadLoopRestorationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadLoopRestorationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
_ = planesCount;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration)
{ {
frameInfo.LoopRestorationParameters[0] = new ObuLoopRestorationParameters();
frameInfo.LoopRestorationParameters[1] = new ObuLoopRestorationParameters();
frameInfo.LoopRestorationParameters[2] = new ObuLoopRestorationParameters();
return; return;
} }
bool usesLoopRestoration = false; frameInfo.LoopRestorationParameters.UsesLoopRestoration = false;
bool usesChromaLoopRestoration = false; frameInfo.LoopRestorationParameters.UsesChromaLoopRestoration = false;
for (int i = 0; i < planesCount; i++) for (int i = 0; i < planesCount; i++)
{ {
frameInfo.LoopRestorationParameters[i] = new ObuLoopRestorationParameters frameInfo.LoopRestorationParameters.Items[i].Type = (ObuRestorationType)reader.ReadLiteral(2);
{
Type = (ObuRestorationType)reader.ReadLiteral(2)
};
if (frameInfo.LoopRestorationParameters[i].Type != ObuRestorationType.None) if (frameInfo.LoopRestorationParameters.Items[i].Type != ObuRestorationType.None)
{ {
usesLoopRestoration = true; frameInfo.LoopRestorationParameters.UsesLoopRestoration = true;
if (i > 0) if (i > 0)
{ {
usesChromaLoopRestoration = true; frameInfo.LoopRestorationParameters.UsesChromaLoopRestoration = true;
} }
} }
} }
if (usesLoopRestoration) if (frameInfo.LoopRestorationParameters.UsesLoopRestoration)
{ {
uint loopRestorationShift = reader.ReadLiteral(1); frameInfo.LoopRestorationParameters.UnitShift = (int)reader.ReadLiteral(1);
if (sequenceHeader.Use128x128Superblock) if (sequenceHeader.Use128x128Superblock)
{ {
loopRestorationShift++; frameInfo.LoopRestorationParameters.UnitShift++;
} }
else else
{ {
if (reader.ReadBoolean()) if (reader.ReadBoolean())
{ {
loopRestorationShift += reader.ReadLiteral(1); frameInfo.LoopRestorationParameters.UnitShift += (int)reader.ReadLiteral(1);
} }
} }
frameInfo.LoopRestorationParameters[0].Size = Av1Constants.RestorationMaxTileSize >> (int)(2 - loopRestorationShift); frameInfo.LoopRestorationParameters.Items[0].Size = Av1Constants.RestorationMaxTileSize >> (2 - frameInfo.LoopRestorationParameters.UnitShift);
int uvShift = 0; frameInfo.LoopRestorationParameters.UVShift = 0;
if (sequenceHeader.ColorConfig.SubSamplingX && sequenceHeader.ColorConfig.SubSamplingY && usesChromaLoopRestoration) if (sequenceHeader.ColorConfig.SubSamplingX && sequenceHeader.ColorConfig.SubSamplingY && frameInfo.LoopRestorationParameters.UsesChromaLoopRestoration)
{ {
uvShift = (int)reader.ReadLiteral(1); frameInfo.LoopRestorationParameters.UVShift = (int)reader.ReadLiteral(1);
} }
frameInfo.LoopRestorationParameters[1].Size = frameInfo.LoopRestorationParameters[0].Size >> uvShift; frameInfo.LoopRestorationParameters.Items[1].Size = frameInfo.LoopRestorationParameters.Items[0].Size >> frameInfo.LoopRestorationParameters.UVShift;
frameInfo.LoopRestorationParameters[2].Size = frameInfo.LoopRestorationParameters[0].Size >> uvShift; frameInfo.LoopRestorationParameters.Items[2].Size = frameInfo.LoopRestorationParameters.Items[0].Size >> frameInfo.LoopRestorationParameters.UVShift;
} }
} }
@ -1839,14 +1831,14 @@ internal class ObuReader
{ {
grainParams.CbMult = reader.ReadLiteral(8); grainParams.CbMult = reader.ReadLiteral(8);
grainParams.CbLumaMult = reader.ReadLiteral(8); grainParams.CbLumaMult = reader.ReadLiteral(8);
grainParams.CbOffset = reader.ReadLiteral(8); grainParams.CbOffset = reader.ReadLiteral(9);
} }
if (grainParams.NumCrPoints != 0) if (grainParams.NumCrPoints != 0)
{ {
grainParams.CrMult = reader.ReadLiteral(8); grainParams.CrMult = reader.ReadLiteral(8);
grainParams.CrLumaMult = reader.ReadLiteral(8); grainParams.CrLumaMult = reader.ReadLiteral(8);
grainParams.CrOffset = reader.ReadLiteral(8); grainParams.CrOffset = reader.ReadLiteral(9);
} }
grainParams.OverlapFlag = reader.ReadBoolean(); grainParams.OverlapFlag = reader.ReadBoolean();

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

@ -1,7 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System; using System.Buffers;
using System.Reflection.PortableExecutable;
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;
@ -16,7 +17,7 @@ internal class ObuWriter
/// </summary> /// </summary>
public void WriteAll(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, IAv1TileWriter tileWriter) public void WriteAll(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, IAv1TileWriter tileWriter)
{ {
MemoryStream bufferStream = new(100); MemoryStream bufferStream = new(2000);
Av1BitStreamWriter writer = new(bufferStream); Av1BitStreamWriter writer = new(bufferStream);
WriteObuHeaderAndSize(stream, ObuType.TemporalDelimiter, [], 0); WriteObuHeaderAndSize(stream, ObuType.TemporalDelimiter, [], 0);
@ -37,7 +38,7 @@ internal class ObuWriter
WriteTileGroup(ref writer, frameInfo.TilesInfo, tileWriter); WriteTileGroup(ref writer, frameInfo.TilesInfo, tileWriter);
} }
int bytesWritten = 5; // (writer.BitPosition + 7) >> 3; int bytesWritten = (writer.BitPosition + 7) >> 3;
writer.Flush(); writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.Frame, bufferStream.GetBuffer(), bytesWritten); WriteObuHeaderAndSize(stream, ObuType.Frame, bufferStream.GetBuffer(), bytesWritten);
} }
@ -128,7 +129,14 @@ internal class ObuWriter
writer.WriteBoolean(colorConfig.IsMonochrome); writer.WriteBoolean(colorConfig.IsMonochrome);
} }
writer.WriteBoolean(false); // colorConfig.IsColorDescriptionPresent writer.WriteBoolean(colorConfig.IsColorDescriptionPresent);
if (colorConfig.IsColorDescriptionPresent)
{
writer.WriteLiteral((uint)colorConfig.ColorPrimaries, 8);
writer.WriteLiteral((uint)colorConfig.TransferCharacteristics, 8);
writer.WriteLiteral((uint)colorConfig.MatrixCoefficients, 8);
}
if (colorConfig.IsMonochrome) if (colorConfig.IsMonochrome)
{ {
writer.WriteBoolean(colorConfig.ColorRange); writer.WriteBoolean(colorConfig.ColorRange);
@ -368,6 +376,7 @@ internal class ObuWriter
if (frameHeader.FrameType == ObuFrameType.KeyFrame) if (frameHeader.FrameType == ObuFrameType.KeyFrame)
{ {
WriteFrameSize(ref writer, sequenceHeader, frameHeader, false); WriteFrameSize(ref writer, sequenceHeader, frameHeader, false);
WriteRenderSize(ref writer, frameHeader);
if (frameHeader.AllowScreenContentTools) if (frameHeader.AllowScreenContentTools)
{ {
writer.WriteBoolean(frameHeader.AllowIntraBlockCopy); writer.WriteBoolean(frameHeader.AllowIntraBlockCopy);
@ -376,6 +385,7 @@ internal class ObuWriter
else if (frameHeader.FrameType == ObuFrameType.IntraOnlyFrame) else if (frameHeader.FrameType == ObuFrameType.IntraOnlyFrame)
{ {
WriteFrameSize(ref writer, sequenceHeader, frameHeader, false); WriteFrameSize(ref writer, sequenceHeader, frameHeader, false);
WriteRenderSize(ref writer, frameHeader);
if (frameHeader.AllowScreenContentTools) if (frameHeader.AllowScreenContentTools)
{ {
writer.WriteBoolean(frameHeader.AllowIntraBlockCopy); writer.WriteBoolean(frameHeader.AllowIntraBlockCopy);
@ -447,7 +457,8 @@ internal class ObuWriter
} }
} }
writer.WriteBoolean(frameHeader.TransformMode == Av1TransformMode.Select); // No Frame Reference mode selection for AVIF
WriteTransformMode(ref writer, frameHeader);
// No compound INTER-INTER for AVIF. // No compound INTER-INTER for AVIF.
if (frameHeader.SkipModeParameters.SkipModeAllowed) if (frameHeader.SkipModeParameters.SkipModeAllowed)
@ -455,52 +466,16 @@ internal class ObuWriter
writer.WriteBoolean(frameHeader.SkipModeParameters.SkipModeFlag); writer.WriteBoolean(frameHeader.SkipModeParameters.SkipModeFlag);
} }
if (FrameMightAllowWarpedMotion(sequenceHeader, frameHeader)) // No warp motion for AVIF.
{
writer.WriteBoolean(frameHeader.AllowWarpedMotion);
}
else
{
Guard.IsFalse(frameHeader.AllowWarpedMotion, nameof(frameHeader.AllowWarpedMotion), "No warped motion allowed.");
}
writer.WriteBoolean(frameHeader.UseReducedTransformSet); writer.WriteBoolean(frameHeader.UseReducedTransformSet);
// No global motion for AVIF. // No global motion for AVIF.
if (sequenceHeader.AreFilmGrainingParametersPresent && (frameHeader.ShowFrame || frameHeader.ShowableFrame)) WriteFilmGrainFilterParameters(ref writer, sequenceHeader, frameHeader);
{
WriteFilmGrainFilterParameters(ref writer, frameHeader.FilmGrainParameters);
}
}
private static bool IsSuperResolutionUnscaled(ObuFrameSize frameSize)
=> frameSize.FrameWidth == frameSize.SuperResolutionUpscaledWidth;
private static bool FrameMightAllowWarpedMotion(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
=> false; // !frameHeader.ErrorResilientMode && !FrameIsIntraOnly(sequenceHeader) && scs->enable_warped_motion;
private static void SetupPastIndependence(ObuFrameHeader frameInfo)
{
// TODO: Initialize the loop filter parameters.
} }
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.FeatureEnabled[segmentId, (int)feature];
private static int GetQIndex(ObuSegmentationParameters segmentationParameters, int segmentId, int baseQIndex)
{
if (IsSegmentationFeatureActive(segmentationParameters, segmentId, ObuSegmentationLevelFeature.AlternativeQuantizer))
{
int data = segmentationParameters.FeatureData[segmentId, (int)ObuSegmentationLevelFeature.AlternativeQuantizer];
int qIndex = baseQIndex + data;
return Av1Math.Clamp(qIndex, 0, Av1Constants.MaxQ);
}
else
{
return baseQIndex;
}
}
private int WriteFrameHeader(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool writeTrailingBits) private int WriteFrameHeader(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool writeTrailingBits)
{ {
int startBitPosition = writer.BitPosition; int startBitPosition = writer.BitPosition;
@ -557,7 +532,7 @@ internal class ObuWriter
private static int WriteDeltaQ(ref Av1BitStreamWriter writer, int deltaQ) private static int WriteDeltaQ(ref Av1BitStreamWriter writer, int deltaQ)
{ {
bool isCoded = deltaQ == 0; bool isCoded = deltaQ != 0;
writer.WriteBoolean(isCoded); writer.WriteBoolean(isCoded);
if (isCoded) if (isCoded)
{ {
@ -606,15 +581,14 @@ internal class ObuWriter
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.Y]); WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.Y]);
if (planesCount > 1) if (planesCount > 1)
{ {
bool areUvDeltaDifferent = false;
if (colorInfo.HasSeparateUvDelta) if (colorInfo.HasSeparateUvDelta)
{ {
writer.WriteBoolean(colorInfo.HasSeparateUvDelta); writer.WriteBoolean(quantParams.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 (quantParams.HasSeparateUvDelta)
{ {
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.V]); WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.V]);
WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.V]); WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.V]);
@ -641,14 +615,37 @@ internal class ObuWriter
private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
_ = writer; if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy)
_ = sequenceHeader; {
_ = frameInfo; return;
_ = planesCount; }
writer.WriteLiteral((uint)frameInfo.LoopFilterParameters.FilterLevel[0], 6);
writer.WriteLiteral((uint)frameInfo.LoopFilterParameters.FilterLevel[1], 6);
if (sequenceHeader.ColorConfig.PlaneCount > 1)
{
if (frameInfo.LoopFilterParameters.FilterLevel[0] > 0 || frameInfo.LoopFilterParameters.FilterLevel[1] > 0)
{
writer.WriteLiteral((uint)frameInfo.LoopFilterParameters.FilterLevelU, 6);
writer.WriteLiteral((uint)frameInfo.LoopFilterParameters.FilterLevelV, 6);
}
}
// TODO: Parse more stuff. writer.WriteLiteral((uint)frameInfo.LoopFilterParameters.SharpnessLevel, 3);
writer.WriteBoolean(frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled);
if (frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled)
{
writer.WriteBoolean(frameInfo.LoopFilterParameters.ReferenceDeltaModeUpdate);
if (frameInfo.LoopFilterParameters.ReferenceDeltaModeUpdate)
{
throw new NotImplementedException("Reference update of loop filter not supported yet.");
}
}
} }
/// <summary>
/// 5.9.21. TX mode syntax.
/// </summary>
private static void WriteTransformMode(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo) private static void WriteTransformMode(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo)
{ {
if (!frameInfo.CodedLossless) if (!frameInfo.CodedLossless)
@ -659,12 +656,37 @@ internal class ObuWriter
private static void WriteLoopRestorationParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void WriteLoopRestorationParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
_ = writer; if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration)
_ = sequenceHeader; {
_ = frameInfo; return;
_ = planesCount; }
for (int i = 0; i < planesCount; i++)
{
writer.WriteLiteral((uint)frameInfo.LoopRestorationParameters.Items[i].Type, 2);
}
// TODO: Parse more stuff. if (frameInfo.LoopRestorationParameters.UsesLoopRestoration)
{
uint unitShift = (uint)frameInfo.LoopRestorationParameters.UnitShift;
if (sequenceHeader.Use128x128Superblock)
{
writer.WriteLiteral(unitShift - 1, 1);
}
else
{
writer.WriteLiteral(unitShift & 0x01, 1);
if (unitShift > 0)
{
writer.WriteLiteral(unitShift - 1, 1);
}
}
if (sequenceHeader.ColorConfig.SubSamplingX && sequenceHeader.ColorConfig.SubSamplingY && frameInfo.LoopRestorationParameters.UsesChromaLoopRestoration)
{
writer.WriteLiteral((uint)frameInfo.LoopRestorationParameters.UVShift, 1);
}
}
} }
private static void WriteCdefParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void WriteCdefParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
@ -674,7 +696,10 @@ internal class ObuWriter
_ = frameInfo; _ = frameInfo;
_ = planesCount; _ = planesCount;
// TODO: Parse more stuff. if (!frameInfo.CodedLossless && !frameInfo.AllowIntraBlockCopy && sequenceHeader.EnableCdef)
{
throw new NotImplementedException("Didn't implment writing CDF yet.");
}
} }
private static void WriteGlobalMotionParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame) private static void WriteGlobalMotionParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame)
@ -703,11 +728,108 @@ internal class ObuWriter
Guard.IsTrue(isIntraFrame, nameof(isIntraFrame), "Still picture contains only INTRA frames."); Guard.IsTrue(isIntraFrame, nameof(isIntraFrame), "Still picture contains only INTRA frames.");
} }
private static void WriteFilmGrainFilterParameters(ref Av1BitStreamWriter writer, ObuFilmGrainParameters filmGrainInfo) private static void WriteFilmGrainFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{ {
_ = writer; ObuFilmGrainParameters grainParams = frameHeader.FilmGrainParameters;
_ = filmGrainInfo; if (!sequenceHeader.AreFilmGrainingParametersPresent && (!frameHeader.ShowFrame && !frameHeader.ShowableFrame))
{
return;
}
writer.WriteBoolean(grainParams.ApplyGrain);
if (!grainParams.ApplyGrain)
{
return;
}
writer.WriteLiteral(grainParams.GrainSeed, 16);
writer.WriteLiteral(grainParams.NumYPoints, 4);
Guard.NotNull(grainParams.PointYValue);
Guard.NotNull(grainParams.PointYScaling);
for (int i = 0; i < grainParams.NumYPoints; i++)
{
writer.WriteLiteral(grainParams.PointYValue[i], 8);
writer.WriteLiteral(grainParams.PointYScaling[i], 8);
}
if (!sequenceHeader.ColorConfig.IsMonochrome)
{
writer.WriteBoolean(grainParams.ChromaScalingFromLuma);
}
if (!sequenceHeader.ColorConfig.IsMonochrome &&
!grainParams.ChromaScalingFromLuma &&
(!sequenceHeader.ColorConfig.SubSamplingX || !sequenceHeader.ColorConfig.SubSamplingY || grainParams.NumYPoints != 0))
{
writer.WriteLiteral(grainParams.NumCbPoints, 4);
Guard.NotNull(grainParams.PointCbValue);
Guard.NotNull(grainParams.PointCbScaling);
for (int i = 0; i < grainParams.NumCbPoints; i++)
{
writer.WriteLiteral(grainParams.PointCbValue[i], 8);
writer.WriteLiteral(grainParams.PointCbScaling[i], 8);
}
writer.WriteLiteral(grainParams.NumCrPoints, 4);
Guard.NotNull(grainParams.PointCrValue);
Guard.NotNull(grainParams.PointCrScaling);
for (int i = 0; i < grainParams.NumCbPoints; i++)
{
writer.WriteLiteral(grainParams.PointCrValue[i], 8);
writer.WriteLiteral(grainParams.PointCrScaling[i], 8);
}
}
writer.WriteLiteral(grainParams.GrainScalingMinus8, 2);
writer.WriteLiteral(grainParams.ArCoeffLag, 2);
uint numPosLuma = 2 * grainParams.ArCoeffLag * (grainParams.ArCoeffLag + 1);
uint numPosChroma = 0;
if (grainParams.NumYPoints != 0)
{
numPosChroma = numPosLuma + 1;
Guard.NotNull(grainParams.ArCoeffsYPlus128);
for (int i = 0; i < numPosLuma; i++)
{
writer.WriteLiteral(grainParams.ArCoeffsYPlus128[i], 8);
}
}
if (grainParams.ChromaScalingFromLuma || grainParams.NumCbPoints != 0)
{
Guard.NotNull(grainParams.ArCoeffsCbPlus128);
for (int i = 0; i < numPosChroma; i++)
{
writer.WriteLiteral(grainParams.ArCoeffsCbPlus128[i], 8);
}
}
if (grainParams.ChromaScalingFromLuma || grainParams.NumCrPoints != 0)
{
Guard.NotNull(grainParams.ArCoeffsCrPlus128);
for (int i = 0; i < numPosChroma; i++)
{
writer.WriteLiteral(grainParams.ArCoeffsCrPlus128[i], 8);
}
}
writer.WriteLiteral(grainParams.ArCoeffShiftMinus6, 2);
writer.WriteLiteral(grainParams.GrainScaleShift, 2);
if (grainParams.NumCbPoints != 0)
{
writer.WriteLiteral(grainParams.CbMult, 8);
writer.WriteLiteral(grainParams.CbLumaMult, 8);
writer.WriteLiteral(grainParams.CbOffset, 9);
}
if (grainParams.NumCrPoints != 0)
{
writer.WriteLiteral(grainParams.CrMult, 8);
writer.WriteLiteral(grainParams.CrLumaMult, 8);
writer.WriteLiteral(grainParams.CrOffset, 9);
}
// Film grain filter not supported yet writer.WriteBoolean(grainParams.OverlapFlag);
writer.WriteBoolean(grainParams.ClipToRestrictedRange);
} }
} }

2
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs

@ -129,7 +129,7 @@ internal class Av1TileReader : IAv1TileReader
int planesCount = this.SequenceHeader.ColorConfig.PlaneCount; int planesCount = this.SequenceHeader.ColorConfig.PlaneCount;
for (int plane = 0; plane < planesCount; plane++) for (int plane = 0; plane < planesCount; plane++)
{ {
if (this.FrameInfo.LoopRestorationParameters[plane].Type != ObuRestorationType.None) if (this.FrameInfo.LoopRestorationParameters.Items[plane].Type != ObuRestorationType.None)
{ {
// TODO: Implement. // TODO: Implement.
throw new NotImplementedException("No loop restoration filter support."); throw new NotImplementedException("No loop restoration filter support.");

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

@ -14,8 +14,7 @@ public class ObuFrameHeaderTests
// TODO: Check with libgav1 test code. // TODO: Check with libgav1 test code.
private static readonly byte[] KeyFrameHeaderBitStream = private static readonly byte[] KeyFrameHeaderBitStream =
// libgav1 expects this: [0x32, 0x05, 0x10, 0x00]; [0x32, 0x07, 0x10, 0x00];
[0x32, 0x05, 0x20, 0x04];
// Bits Syntax element Value // Bits Syntax element Value
// 1 obu_forbidden_bit 0 // 1 obu_forbidden_bit 0
@ -55,6 +54,7 @@ public class ObuFrameHeaderTests
/* /*
[Theory] [Theory]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d, 0x0128)] [InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d, 0x0128)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc, 0x0114)] [InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc, 0x0114)]
public void BinaryIdenticalRoundTripFrameHeader(string filename, int fileOffset, int blockSize, int tileOffset) public void BinaryIdenticalRoundTripFrameHeader(string filename, int fileOffset, int blockSize, int tileOffset)
{ {
@ -77,7 +77,8 @@ public class ObuFrameHeaderTests
obuWriter.WriteAll(encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub); obuWriter.WriteAll(encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub);
// Assert // Assert
Assert.Equal(span, encoded.ToArray()); byte[] encodedArray = encoded.ToArray();
Assert.Equal(span, encodedArray);
} }
*/ */

Loading…
Cancel
Save