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

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

@ -1,7 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
using System.Reflection.PortableExecutable;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
@ -16,7 +17,7 @@ internal class ObuWriter
/// </summary>
public void WriteAll(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, IAv1TileWriter tileWriter)
{
MemoryStream bufferStream = new(100);
MemoryStream bufferStream = new(2000);
Av1BitStreamWriter writer = new(bufferStream);
WriteObuHeaderAndSize(stream, ObuType.TemporalDelimiter, [], 0);
@ -37,7 +38,7 @@ internal class ObuWriter
WriteTileGroup(ref writer, frameInfo.TilesInfo, tileWriter);
}
int bytesWritten = 5; // (writer.BitPosition + 7) >> 3;
int bytesWritten = (writer.BitPosition + 7) >> 3;
writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.Frame, bufferStream.GetBuffer(), bytesWritten);
}
@ -128,7 +129,14 @@ internal class ObuWriter
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)
{
writer.WriteBoolean(colorConfig.ColorRange);
@ -368,6 +376,7 @@ internal class ObuWriter
if (frameHeader.FrameType == ObuFrameType.KeyFrame)
{
WriteFrameSize(ref writer, sequenceHeader, frameHeader, false);
WriteRenderSize(ref writer, frameHeader);
if (frameHeader.AllowScreenContentTools)
{
writer.WriteBoolean(frameHeader.AllowIntraBlockCopy);
@ -376,6 +385,7 @@ internal class ObuWriter
else if (frameHeader.FrameType == ObuFrameType.IntraOnlyFrame)
{
WriteFrameSize(ref writer, sequenceHeader, frameHeader, false);
WriteRenderSize(ref writer, frameHeader);
if (frameHeader.AllowScreenContentTools)
{
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.
if (frameHeader.SkipModeParameters.SkipModeAllowed)
@ -455,52 +466,16 @@ internal class ObuWriter
writer.WriteBoolean(frameHeader.SkipModeParameters.SkipModeFlag);
}
if (FrameMightAllowWarpedMotion(sequenceHeader, frameHeader))
{
writer.WriteBoolean(frameHeader.AllowWarpedMotion);
}
else
{
Guard.IsFalse(frameHeader.AllowWarpedMotion, nameof(frameHeader.AllowWarpedMotion), "No warped motion allowed.");
}
// No warp motion for AVIF.
writer.WriteBoolean(frameHeader.UseReducedTransformSet);
// No global motion for AVIF.
if (sequenceHeader.AreFilmGrainingParametersPresent && (frameHeader.ShowFrame || frameHeader.ShowableFrame))
{
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.
WriteFilmGrainFilterParameters(ref writer, sequenceHeader, frameHeader);
}
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature 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)
{
int startBitPosition = writer.BitPosition;
@ -557,7 +532,7 @@ internal class ObuWriter
private static int WriteDeltaQ(ref Av1BitStreamWriter writer, int deltaQ)
{
bool isCoded = deltaQ == 0;
bool isCoded = deltaQ != 0;
writer.WriteBoolean(isCoded);
if (isCoded)
{
@ -606,15 +581,14 @@ internal class ObuWriter
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.Y]);
if (planesCount > 1)
{
bool areUvDeltaDifferent = false;
if (colorInfo.HasSeparateUvDelta)
{
writer.WriteBoolean(colorInfo.HasSeparateUvDelta);
writer.WriteBoolean(quantParams.HasSeparateUvDelta);
}
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(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.DeltaQAc[(int)Av1Plane.V]);
@ -641,14 +615,37 @@ internal class ObuWriter
private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
_ = planesCount;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy)
{
return;
}
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)
{
if (!frameInfo.CodedLossless)
@ -659,12 +656,37 @@ internal class ObuWriter
private static void WriteLoopRestorationParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
_ = planesCount;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration)
{
return;
}
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)
@ -674,7 +696,10 @@ internal class ObuWriter
_ = frameInfo;
_ = 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)
@ -703,11 +728,108 @@ internal class ObuWriter
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;
_ = filmGrainInfo;
ObuFilmGrainParameters grainParams = frameHeader.FilmGrainParameters;
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;
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.
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.
private static readonly byte[] KeyFrameHeaderBitStream =
// libgav1 expects this: [0x32, 0x05, 0x10, 0x00];
[0x32, 0x05, 0x20, 0x04];
[0x32, 0x07, 0x10, 0x00];
// Bits Syntax element Value
// 1 obu_forbidden_bit 0
@ -55,6 +54,7 @@ public class ObuFrameHeaderTests
/*
[Theory]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d, 0x0128)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc, 0x0114)]
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);
// Assert
Assert.Equal(span, encoded.ToArray());
byte[] encodedArray = encoded.ToArray();
Assert.Equal(span, encodedArray);
}
*/

Loading…
Cancel
Save