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