diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
index affa7f9b57..687b973331 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
@@ -37,6 +37,13 @@ internal ref struct Av1BitStreamWriter
span[1] = (byte)((value >> 7) & 0xff);
return 2;
}
+ else if (value < 0x800000U)
+ {
+ span[0] = (byte)((value & 0x7fU) | 0x80U);
+ span[1] = (byte)((value >> 7) & 0xff);
+ span[2] = (byte)((value >> 14) & 0xff);
+ return 3;
+ }
else
{
throw new NotImplementedException("No such large values yet.");
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
index 1042effdfe..c0d5c63f1e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
@@ -247,14 +247,6 @@ internal class ObuReader
frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, Av1Constants.MaxSuperBlockSizeLog2) >> Av1Constants.ModeInfoSizeLog2;
}
- private static bool IsValidObuType(ObuType type) => type switch
- {
- ObuType.SequenceHeader or ObuType.TemporalDelimiter or ObuType.FrameHeader or
- ObuType.TileGroup or ObuType.Metadata or ObuType.Frame or ObuType.RedundantFrameHeader or
- ObuType.TileList or ObuType.Padding => true,
- _ => false,
- };
-
///
/// 5.5.1. General sequence header OBU syntax.
///
@@ -292,7 +284,7 @@ internal class ObuReader
sequenceHeader.TimingInfoPresentFlag = reader.ReadBoolean();
if (sequenceHeader.TimingInfoPresentFlag)
{
- ReadTimingInfo(ref reader, sequenceHeader);
+ ReadTimingInfo(ref reader, sequenceHeader);
sequenceHeader.DecoderModelInfoPresentFlag = reader.ReadBoolean();
if (sequenceHeader.DecoderModelInfoPresentFlag)
{
@@ -309,9 +301,11 @@ internal class ObuReader
sequenceHeader.OperatingPoint = new ObuOperatingPoint[operatingPointsCnt];
for (int i = 0; i < operatingPointsCnt; i++)
{
- sequenceHeader.OperatingPoint[i] = new ObuOperatingPoint();
- sequenceHeader.OperatingPoint[i].Idc = reader.ReadLiteral(12);
- sequenceHeader.OperatingPoint[i].SequenceLevelIndex = (int)reader.ReadLiteral(5);
+ sequenceHeader.OperatingPoint[i] = new ObuOperatingPoint
+ {
+ Idc = reader.ReadLiteral(12),
+ SequenceLevelIndex = (int)reader.ReadLiteral(5)
+ };
if (sequenceHeader.OperatingPoint[i].SequenceLevelIndex > 7)
{
sequenceHeader.OperatingPoint[i].SequenceTier = (int)reader.ReadLiteral(1);
@@ -638,36 +632,6 @@ internal class ObuReader
}
}
- ///
- /// 5.9.7. Frame size with refs syntax.
- ///
- private void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag)
- {
- ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
- ObuFrameHeader frameInfo = this.FrameHeader!;
- bool foundReference = false;
- for (int i = 0; i < Av1Constants.ReferencesPerFrame; i++)
- {
- foundReference = reader.ReadBoolean();
- if (foundReference)
- {
- // Take values over from reference frame
- break;
- }
- }
-
- if (!foundReference)
- {
- this.ReadFrameSize(ref reader, frameSizeOverrideFlag);
- this.ReadRenderSize(ref reader);
- }
- else
- {
- this.ReadSuperResolutionParameters(ref reader);
- this.ComputeImageSize(sequenceHeader);
- }
- }
-
///
/// 5.9.5. Frame size syntax.
///
@@ -1087,7 +1051,7 @@ internal class ObuReader
if (frameHeader.PrimaryReferenceFrame == Av1Constants.PrimaryReferenceFrameNone)
{
// InitConCoefficientCdfs();
- SetupPastIndependence(frameHeader);
+ // SetupPastIndependence(frameHeader);
}
else
{
@@ -1184,14 +1148,6 @@ internal class ObuReader
frameHeader.FilmGrainParameters = ReadFilmGrainFilterParameters(ref reader, sequenceHeader, frameHeader);
}
- private static void SetupPastIndependence(ObuFrameHeader frameInfo)
- {
- _ = frameInfo;
-
- // TODO: Implement.
- // The current frame can be decoded without dependencies on previous coded frame.
- }
-
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature)
=> segmentationParameters.Enabled && segmentationParameters.IsFeatureActive(segmentId, feature);
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
index d1db3f25f6..1772daf722 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
@@ -32,7 +32,7 @@ internal class ObuWriter
if (frameInfo != null && sequenceHeader != null)
{
- this.WriteFrameHeader(ref writer, sequenceHeader, frameInfo, true);
+ this.WriteFrameHeader(ref writer, sequenceHeader, frameInfo, false);
if (frameInfo.TilesInfo != null)
{
WriteTileGroup(ref writer, frameInfo.TilesInfo, tileWriter);
@@ -68,7 +68,7 @@ internal class ObuWriter
private static void WriteObuHeaderAndSize(Stream stream, ObuType type, Span payload)
{
stream.WriteByte(WriteObuHeader(type));
- Span lengthBytes = stackalloc byte[2];
+ Span lengthBytes = stackalloc byte[3];
int lengthLength = Av1BitStreamWriter.GetLittleEndianBytes128((uint)payload.Length, lengthBytes);
stream.Write(lengthBytes, 0, lengthLength);
stream.Write(payload);
@@ -416,6 +416,7 @@ internal class ObuWriter
if (frameHeader.DeltaQParameters.IsPresent)
{
writer.WriteLiteral((uint)frameHeader.DeltaQParameters.Resolution - 1, 2);
+ this.previousQIndex = new int[tileCount];
for (int tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
this.previousQIndex[tileIndex] = frameHeader.QuantizationParameters.BaseQIndex;
@@ -438,6 +439,7 @@ internal class ObuWriter
writer.WriteLiteral((uint)(1 + Av1Math.MostSignificantBit((uint)frameHeader.DeltaLoopFilterParameters.Resolution) - 1), 2);
writer.WriteBoolean(frameHeader.DeltaLoopFilterParameters.IsMulti);
int frameLoopFilterCount = sequenceHeader.ColorConfig.IsMonochrome ? Av1Constants.FrameLoopFilterCount - 2 : Av1Constants.FrameLoopFilterCount;
+ this.previousDeltaLoopFilter = new int[frameLoopFilterCount];
for (int loopFilterId = 0; loopFilterId < frameLoopFilterCount; loopFilterId++)
{
this.previousDeltaLoopFilter[loopFilterId] = 0;
@@ -498,11 +500,14 @@ internal class ObuWriter
return headerBytes;
}
+ ///
+ /// 5.11.1. General tile group OBU syntax.
+ ///
private static int WriteTileGroup(ref Av1BitStreamWriter writer, ObuTileGroupHeader tileInfo, IAv1TileWriter tileWriter)
{
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = writer.BitPosition;
- bool tileStartAndEndPresentFlag = tileCount != 0;
+ bool tileStartAndEndPresentFlag = tileCount > 1;
writer.WriteBoolean(tileStartAndEndPresentFlag);
uint tileGroupStart = 0U;
@@ -623,6 +628,9 @@ internal class ObuWriter
writer.WriteBoolean(false);
}
+ ///
+ /// 5.9.11. Loop filter params syntax
+ ///
private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{
if (frameHeader.CodedLossless || frameHeader.AllowIntraBlockCopy)
@@ -750,7 +758,7 @@ internal class ObuWriter
private static void WriteFilmGrainFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{
ObuFilmGrainParameters grainParams = frameHeader.FilmGrainParameters;
- if (!sequenceHeader.AreFilmGrainingParametersPresent && (!frameHeader.ShowFrame && !frameHeader.ShowableFrame))
+ if (!sequenceHeader.AreFilmGrainingParametersPresent || (!frameHeader.ShowFrame && !frameHeader.ShowableFrame))
{
return;
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs
index 0c523442b6..0a13e08945 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs
@@ -12,9 +12,7 @@ public class ObuFrameHeaderTests
private static readonly byte[] DefaultSequenceHeaderBitStream =
[0x0a, 0x06, 0b001_1_1_000, 0b00_1000_01, 0b11_110101, 0b001_11101, 0b111_1_1_1_0_1, 0b1_0_0_1_1_1_10];
- // TODO: Check with libgav1 test code.
- private static readonly byte[] KeyFrameHeaderBitStream =
- [0x32, 0x07, 0x10, 0x00];
+ private static readonly byte[] KeyFrameHeaderBitStream = [0x32, 0x06, 0x10, 0x00];
// Bits Syntax element Value
// 1 obu_forbidden_bit 0
@@ -26,7 +24,7 @@ public class ObuFrameHeaderTests
private static readonly byte[] DefaultTemporalDelimiterBitStream = [0x12, 0x00];
[Theory]
- // [InlineData(TestImages.Heif.IrvineAvif, 0x0102, 0x000d)]
+
// [InlineData(TestImages.Heif.IrvineAvif, 0x0198, 0x6bd1)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc)]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
@@ -51,12 +49,10 @@ public class ObuFrameHeaderTests
Assert.Equal(reader.Length, blockSize);
}
- /*
[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)
+ [InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
+ [InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc)]
+ public void BinaryIdenticalRoundTripFrameHeader(string filename, int fileOffset, int blockSize)
{
// Assign
string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, filename);
@@ -74,13 +70,12 @@ public class ObuFrameHeaderTests
// Act 2
ObuWriter obuWriter = new();
- obuWriter.WriteAll(encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub);
+ obuWriter.WriteAll(Configuration.Default, encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub);
// Assert
byte[] encodedArray = encoded.ToArray();
Assert.Equal(span, encodedArray);
}
- */
[Theory]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
@@ -323,5 +318,4 @@ public class ObuFrameHeaderTests
TileRowCount = 1,
}
};
-
}