diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs index 9e294324c..10e242e8d 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Traversal.cs @@ -41,16 +41,54 @@ internal sealed partial class HevcPictureDecoder int contextOffset = colorPlaneIndex * HevcCabacContexts.ContextCount; int riceOffset = colorPlaneIndex * 4; - if (slice.DependentSliceSegment && this.hasSliceSegmentContexts[colorPlaneIndex]) + int startRasterAddress = tileLayout.GetRasterAddress(startAddressInTileScan); + tileLayout.GetTilePosition( + startRasterAddress, + out int startTileIndex, + out int startColumnInTile, + out int startRowInTile, + out int startTileWidth, + out _); + + bool startsAtTileOrigin = startColumnInTile == 0 && startRowInTile == 0; + bool canInheritSliceSegmentContexts = !startsAtTileOrigin + && (startTileWidth >= 2 || !this.pictureParameterSet.EntropyCodingSynchronizationEnabled); + + // A dependent segment normally resumes the preceding CABAC state. Tile origins and one-CTB-wide WPP + // rows are initialization boundaries instead, matching the availability rules used by the reference decoder. + if (slice.DependentSliceSegment + && canInheritSliceSegmentContexts + && this.hasSliceSegmentContexts[colorPlaneIndex]) { reader.CopyContextsFrom(this.sliceSegmentContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); this.coefficientDecoder.CopyRiceAdaptationFrom(this.sliceSegmentRiceAdaptation.AsSpan(riceOffset, 4)); } + if (!slice.DependentSliceSegment) + { + // An independent slice starts a new prediction region, so an upper-right CTB from the preceding + // independent slice cannot supply wavefront contexts to its first row. + this.hasWavefrontContexts[colorPlaneIndex] = false; + } + + bool startsAtWavefrontRow = this.pictureParameterSet.EntropyCodingSynchronizationEnabled + && startColumnInTile == 0 + && startRowInTile > 0; + + if (startsAtWavefrontRow + && startTileWidth > 1 + && this.hasWavefrontContexts[colorPlaneIndex] + && this.wavefrontContextTileIndices[colorPlaneIndex] == startTileIndex) + { + // A dependent segment can begin exactly at a wavefront row boundary. Its first substream still uses + // the upper-right state captured from the preceding row; no substream transition occurs inside this call. + reader.CopyContextsFrom(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); + this.coefficientDecoder.CopyRiceAdaptationFrom(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); + } + int codingTreeBlockSize = 1 << this.sequenceParameterSet.CodingTreeBlockLog2; int tileScanAddress = startAddressInTileScan; bool firstCodingTreeBlock = true; - bool wavefrontStateAvailable = false; while (tileScanAddress < tileLayout.Width * tileLayout.Height) { int rasterAddress = tileLayout.GetRasterAddress(tileScanAddress); @@ -81,10 +119,13 @@ internal sealed partial class HevcPictureDecoder reader = new HevcCabacSyntaxReader(slice.GetEntropySubstream(substreamIndex).Span, sliceQuantizationParameter); this.coefficientDecoder.ResetRiceAdaptation(); this.lastCodedQuantizationParameter = sliceQuantizationParameter; - if (startsWavefrontRow && tileWidth > 1 && wavefrontStateAvailable) + if (startsWavefrontRow + && tileWidth > 1 + && this.hasWavefrontContexts[colorPlaneIndex] + && this.wavefrontContextTileIndices[colorPlaneIndex] == tileIndex) { - reader.CopyContextsFrom(this.wavefrontContexts); - this.coefficientDecoder.CopyRiceAdaptationFrom(this.wavefrontRiceAdaptation[..4]); + reader.CopyContextsFrom(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); + this.coefficientDecoder.CopyRiceAdaptationFrom(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); } } @@ -121,9 +162,10 @@ internal sealed partial class HevcPictureDecoder // row. The next row starts with those contexts but a newly initialized arithmetic register. if (this.pictureParameterSet.EntropyCodingSynchronizationEnabled && columnInTile == 1) { - reader.CopyContextsTo(this.wavefrontContexts); - this.coefficientDecoder.CopyRiceAdaptationTo(this.wavefrontRiceAdaptation[..4]); - wavefrontStateAvailable = true; + reader.CopyContextsTo(this.wavefrontContexts.AsSpan(contextOffset, HevcCabacContexts.ContextCount)); + this.coefficientDecoder.CopyRiceAdaptationTo(this.wavefrontRiceAdaptation.AsSpan(riceOffset, 4)); + this.hasWavefrontContexts[colorPlaneIndex] = true; + this.wavefrontContextTileIndices[colorPlaneIndex] = tileIndex; } tileScanAddress++; diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs index 2625687d2..8b4fd3859 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.cs @@ -82,14 +82,24 @@ internal sealed partial class HevcPictureDecoder : IDisposable private readonly IMemoryOwner availabilityScratch; /// - /// The adaptive contexts captured after the second coding-tree block of a wavefront row. + /// The per-color-plane adaptive contexts captured after the second coding-tree block of a wavefront row. /// - private readonly HevcCabacContext[] wavefrontContexts = new HevcCabacContext[HevcCabacContexts.ContextCount]; + private readonly HevcCabacContext[] wavefrontContexts = new HevcCabacContext[HevcCabacContexts.ContextCount * 3]; /// - /// The persistent Rice statistics captured with the wavefront probability contexts. + /// The per-color-plane persistent Rice statistics captured with the wavefront probability contexts. /// - private InlineArray4 wavefrontRiceAdaptation; + private readonly int[] wavefrontRiceAdaptation = new int[12]; + + /// + /// Whether retained wavefront contexts are available for each color plane. + /// + private InlineArray4 hasWavefrontContexts; + + /// + /// The tile that owns each color plane's retained wavefront contexts. + /// + private InlineArray4 wavefrontContextTileIndices; /// /// The adaptive contexts retained at the end of a dependent-slice prediction region. diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs index 013c64be3..d8c0032ba 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcSliceSegmentHeader.cs @@ -450,7 +450,7 @@ internal sealed class HevcSliceSegmentHeader /// The decoded raw-byte-sequence payload offset. /// The removed encoded-payload byte positions. /// The encoded byte-sequence payload offset at the same syntax boundary. - private static int GetEncodedPayloadOffset( + internal static int GetEncodedPayloadOffset( int rbspOffset, ReadOnlySpan emulationPreventionBytePositions) { @@ -474,7 +474,7 @@ internal sealed class HevcSliceSegmentHeader /// The encoded byte-sequence payload offset. /// The removed encoded-payload byte positions. /// The decoded raw-byte-sequence payload offset at the same syntax boundary. - private static int GetDecodedPayloadOffset( + internal static int GetDecodedPayloadOffset( int encodedOffset, ReadOnlySpan emulationPreventionBytePositions) { diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs index 61efebccb..68bf44ced 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs @@ -64,6 +64,43 @@ public class HevcPictureDecoderTests ChromaQuantizationAdjustment = 32, } + /// + /// Identifies parallelization syntax that an official independently decoded picture must exercise. + /// + [Flags] + public enum ParallelizationTools + { + /// + /// No parallelization syntax is signaled. + /// + None = 0, + + /// + /// Dependent slice segments are signaled. + /// + DependentSliceSegments = 1, + + /// + /// Tile boundaries are signaled. + /// + Tiles = 2, + + /// + /// Wavefront row entry points are signaled. + /// + Wavefront = 4, + + /// + /// Slice-header extension bytes are signaled. + /// + SliceHeaderExtensions = 8, + + /// + /// One or more entropy entry points are signaled. + /// + EntryPoints = 16, + } + /// /// Verifies the first independently coded picture from official ITU RExt conformance streams against its /// published decoded-picture hashes. @@ -466,6 +503,152 @@ public class HevcPictureDecoderTests Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); } + /// + /// Verifies the complete sequential decoder path for HEVC tiles, wavefront entry points, dependent slice + /// segments, and slice-header extensions against native-plane digests from published or pinned-HM output. + /// + /// The official Annex B conformance stream. + /// The parallelization syntax that the retained picture signals. + /// The expected coding-tree-block size logarithm. + /// The expected picture width in coding-tree blocks. + /// The exact tile-column count, or zero when only a multi-tile assertion applies. + /// The exact tile-row count, or zero when only a multi-tile assertion applies. + /// The pinned-HM or published luma-plane MD5 digest. + /// The pinned-HM or published blue-difference-plane MD5 digest. + /// The pinned-HM or published red-difference-plane MD5 digest. + [Theory] + [InlineData(TestImages.Heif.DependentSlicesA, ParallelizationTools.DependentSliceSegments, 6, 30, 1, 1, "00dc01343ab9dc53c078344d7f77dab1", "899538536f2b327d84894c947f87bbc2", "a1bc8421c5a72ce2792b40847e95d438")] + [InlineData(TestImages.Heif.DependentSlicesB, ParallelizationTools.DependentSliceSegments | ParallelizationTools.Wavefront, 6, 30, 1, 1, "d048cfe1b7f0e6a6e3689733914caa19", "d7400a314011173564516b81407c3f42", "7bddfaa6d440f8490ec88b94fdbac706")] + [InlineData(TestImages.Heif.DependentSlicesC, ParallelizationTools.DependentSliceSegments | ParallelizationTools.Tiles, 6, 30, 0, 0, "a8c96c581d9de294a4fe798cede17817", "afb4cdebbbb31edfeab504d64c779895", "e593a80b1f17724f930728068993c06c")] + [InlineData(TestImages.Heif.TilesA, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 5, 5, "6828e4b27ab4fda31fe3b8bcdb3bccef", "1f899aff0a453d133de048232d3ee1c0", "87ca938e4a19cd289bdaa85023c704c5")] + [InlineData(TestImages.Heif.TilesB, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 5, 5, "aa44a1bf0f77f5a78e514eab3621aab2", "53a29305bb7b60dcd3a0082ce5aed9f4", "0e7ad4ea85eedf8fa06ac9b366323175")] + [InlineData(TestImages.Heif.WavefrontA, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 7, 1, 1, "69bd520cd6b017b49144275f1c3b498c", "33bb1c6216561f30fbefb89ce87c0956", "1f88fd804c87d8f5c8c72cee4043d140")] + [InlineData(TestImages.Heif.WavefrontB, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 5, 13, 1, 1, "ff78fcf56cf449c195708626a975e870", "b230844124f07aad4102aa21e2fc0f15", "e10c05f8c4007b14ddc6a7cf858f374e")] + [InlineData(TestImages.Heif.WavefrontC, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 4, 26, 1, 1, "d55877b038bbe2af6a4b35eeff27b26f", "4e1145cc891c295543407b6c62c7ad55", "8fa9c17216a9b02a65582c322206216b")] + [InlineData(TestImages.Heif.WavefrontD, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 1, 1, 1, "ab7c74b80340e5bde0858276f11a37ed", "4fe6b63cfe5656bf88912ccf9caeb85a", "3dbb149d5b90a49cf72d4e2d5fbb0511")] + [InlineData(TestImages.Heif.WavefrontE, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 2, 1, 1, "d2b8cd7d9e7baf4dd3e383ba8fef3c22", "315f19843d4e637dc41f964c4adff626", "07f619c6aedd51a04ba99d2bfc0ecb2c")] + [InlineData(TestImages.Heif.WavefrontF, ParallelizationTools.Wavefront | ParallelizationTools.SliceHeaderExtensions | ParallelizationTools.EntryPoints, 6, 3, 1, 1, "797335a8e293c6ce6dd87fde6f797f07", "77f74454394860093d4b18ae9ae793fe", "e99ff04282a3457a44685378008ff86a")] + [InlineData(TestImages.Heif.EntryPointsA, ParallelizationTools.Tiles | ParallelizationTools.EntryPoints, 6, 30, 2, 2, "ea26d532556e6b71b369b41def35af93", "295a5552a8152035a1cae2405a690251", "1d81ff340ac8a37d75f7782ca140cae3")] + [InlineData(TestImages.Heif.EntryPointsC, ParallelizationTools.Wavefront | ParallelizationTools.EntryPoints, 6, 30, 1, 1, "81b087fcf7df2626c7592ec5d39ec1fc", "93bd06e1a216a388568c069ead14f98e", "f0446d71e2061a8c3a6281fede767d81")] + public void DecodeOfficialParallelizationPictureMatchesPublishedReference( + string path, + ParallelizationTools expectedTools, + int expectedCodingTreeBlockLog2, + int expectedCodingTreeBlockWidth, + int expectedTileColumns, + int expectedTileRows, + string lumaDigest, + string chromaBlueDigest, + string chromaRedDigest) + { + byte[] annexB = TestFile.Create(path).Bytes; + ConvertAnnexBStillPicture(annexB, 8, 1, out byte[] configurationData, out byte[] itemData); + HevcCodecConfiguration configuration = new(configurationData); + HevcImageItemBitstream bitstream = new(itemData, configuration); + HevcPictureParameterSet pictureParameterSet = bitstream.SliceSegments[0].PictureParameterSet; + HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; + using HevcPictureDecoder decoder = new(Configuration.Default, pictureParameterSet); + + decoder.Decode(bitstream); + + bool hasDependentSliceSegments = false; + bool hasEntryPoints = false; + foreach (HevcSliceSegmentHeader sliceSegment in bitstream.SliceSegments) + { + hasDependentSliceSegments |= sliceSegment.DependentSliceSegment; + hasEntryPoints |= sliceSegment.EntryPointOffsets.Count != 0; + } + + bool expectsTiles = (expectedTools & ParallelizationTools.Tiles) != 0; + bool expectsWavefront = (expectedTools & ParallelizationTools.Wavefront) != 0; + int codingTreeBlockSize = 1 << sequenceParameterSet.CodingTreeBlockLog2; + int codingTreeBlockWidth = (sequenceParameterSet.Width + codingTreeBlockSize - 1) / codingTreeBlockSize; + + Assert.Equal((expectedTools & ParallelizationTools.DependentSliceSegments) != 0, hasDependentSliceSegments); + Assert.Equal(expectsTiles, pictureParameterSet.TilesEnabled); + Assert.Equal(expectsWavefront, pictureParameterSet.EntropyCodingSynchronizationEnabled); + Assert.Equal((expectedTools & ParallelizationTools.SliceHeaderExtensions) != 0, pictureParameterSet.SliceSegmentHeaderExtensionPresent); + Assert.Equal((expectedTools & ParallelizationTools.EntryPoints) != 0, hasEntryPoints); + Assert.Equal(expectedCodingTreeBlockLog2, sequenceParameterSet.CodingTreeBlockLog2); + Assert.Equal(expectedCodingTreeBlockWidth, codingTreeBlockWidth); + + if (expectedTileColumns == 0) + { + Assert.True(pictureParameterSet.TileColumnWidths.Count > 1); + Assert.True(pictureParameterSet.TileRowHeights.Count > 1); + } + else + { + Assert.Equal(expectedTileColumns, pictureParameterSet.TileColumnWidths.Count); + Assert.Equal(expectedTileRows, pictureParameterSet.TileRowHeights.Count); + } + + Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); + Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); + Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); + } + + /// + /// Verifies the dependent-slice tile and wavefront combinations through split allocator groups with balanced + /// final disposal. + /// + /// The official Annex B conformance stream. + /// The pinned-HM luma-plane MD5 digest. + /// The pinned-HM blue-difference-plane MD5 digest. + /// The pinned-HM red-difference-plane MD5 digest. + [Theory] + [InlineData(TestImages.Heif.DependentSlicesB, "d048cfe1b7f0e6a6e3689733914caa19", "d7400a314011173564516b81407c3f42", "7bddfaa6d440f8490ec88b94fdbac706")] + [InlineData(TestImages.Heif.DependentSlicesC, "a8c96c581d9de294a4fe798cede17817", "afb4cdebbbb31edfeab504d64c779895", "e593a80b1f17724f930728068993c06c")] + public void DecodeOfficialParallelizationPicturesWithConstrainedAllocatorMatchPinnedHmDigest( + string path, + string lumaDigest, + string chromaBlueDigest, + string chromaRedDigest) + { + byte[] annexB = TestFile.Create(path).Bytes; + ConvertAnnexBStillPicture(annexB, 8, 1, out byte[] configurationData, out byte[] itemData); + HevcCodecConfiguration codecConfiguration = new(configurationData); + HevcImageItemBitstream bitstream = new(itemData, codecConfiguration); + TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 4_096 }; + allocator.EnableNonThreadSafeLogging(); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = allocator; + using (HevcPictureDecoder decoder = new(configuration, bitstream.SliceSegments[0].PictureParameterSet)) + { + decoder.Decode(bitstream); + + Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); + Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); + Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); + } + + Assert.NotEmpty(allocator.AllocationLog); + AssertBalancedAllocations(allocator); + } + + /// + /// Verifies that entry-point byte lengths exclude emulation-prevention bytes from the decoded substream. + /// + [Fact] + public void EntryPointOffsetsExcludeEmulationPreventionBytes() + { + ReadOnlySpan preventionBytePositions = [3, 8, 14]; + + const int DecodedHeaderLength = 4; + int encodedHeaderLength = HevcSliceSegmentHeader.GetEncodedPayloadOffset( + DecodedHeaderLength, + preventionBytePositions); + + const int EncodedSubstreamLength = 11; + int decodedBoundary = HevcSliceSegmentHeader.GetDecodedPayloadOffset( + encodedHeaderLength + EncodedSubstreamLength, + preventionBytePositions); + + Assert.Equal(5, encodedHeaderLength); + Assert.Equal(13, decodedBoundary); + Assert.Equal(9, decodedBoundary - DecodedHeaderLength); + } + /// /// Verifies all reconstructed samples from a real HEIC grid tile against the HM reference decoder. /// diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ef68cd737..d08f60c39 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1324,6 +1324,19 @@ public static class TestImages public const string StructA = "Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit"; public const string StructB = "Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit"; public const string TuSizeA = "Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit"; + public const string DependentSlicesA = "Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit"; + public const string DependentSlicesB = "Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit"; + public const string DependentSlicesC = "Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit"; + public const string TilesA = "Heif/Hevc/Conformance/TILES_A_Cisco_2.bit"; + public const string TilesB = "Heif/Hevc/Conformance/TILES_B_Cisco_1.bit"; + public const string WavefrontA = "Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit"; + public const string WavefrontB = "Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit"; + public const string WavefrontC = "Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit"; + public const string WavefrontD = "Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit"; + public const string WavefrontE = "Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit"; + public const string WavefrontF = "Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit"; + public const string EntryPointsA = "Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit"; + public const string EntryPointsC = "Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit"; public const string Image1 = "Heif/image1.heic"; public const string Image2 = "Heif/image2.heic"; public const string Image3 = "Heif/image3.heic"; diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit new file mode 100644 index 000000000..a33e54c7b --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_A_HHI_5.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8398fb23c814a197bba497ad2c6103f81ca8003434fa40ec347d4d0a07c9468a +size 373328 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit new file mode 100644 index 000000000..977d1d42e --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_B_HHI_5.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db582f1181f3b8ed3beeb32246a1a99123700dbdc3d3be4e1797032fcd46c486 +size 379617 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit new file mode 100644 index 000000000..386f2d8d8 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/DSLICE_C_HHI_5.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f162007f604aa0e48229c4153f940fd5f24db955ad158d11e2bd46c89c49ca76 +size 373275 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit new file mode 100644 index 000000000..8179733ae --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_A_Qualcomm_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc1dfb16f676f59cebde3a4732209ce5ce0d0f49ef697970987c0c3e0e087b1 +size 2623324 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit new file mode 100644 index 000000000..e94ebf87f --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/ENTP_C_Qualcomm_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:993cd68f0e749cd1d9ce7cc3a368b4df26dccf07ded91048100217b9a0c56d28 +size 218579 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/README.md b/tests/Images/Input/Heif/Hevc/Conformance/README.md index 0a64ce08f..259d6f61d 100644 --- a/tests/Images/Input/Heif/Hevc/Conformance/README.md +++ b/tests/Images/Input/Heif/Hevc/Conformance/README.md @@ -37,3 +37,23 @@ The loop-filter fixtures are the unchanged Annex B payloads from the official HE - `DBLK_A_MAIN10_VIXS_4.bit` is the Main 10 negative-QP deblocking stream. The archive SHA-256 is `D86EC87F98FCCBCEC3758954A7CF430E8AF8CF021D347DFBBED2F1D5DBB00F25`, and the bitstream SHA-256 is `3A9089207BEDDDB0C8DB28B410441E37D4CFF5DD8301CB8BA7D2F75036E2D50C`. Pinned HM reproduces the published complete-output MD5 `c4594956bb9e8303f1662f9eb1bcdf50`; the retained 10-bit 4:2:0 first picture has plane MD5 values `184a72aab144cb474df3c1a289e8692d`, `d30e750dd70cae163d00f441a75896fd`, and `d253c41f06228df215f4616febbad34f`. - `SAO_A_MediaTek_4.bit` is the 8-bit 4:2:0 sample-adaptive-offset stream. The archive SHA-256 is `E0B5C8F4AB4FB592971F06469DB630D9E7EF72B4D37D729604E2FDAA9DF706C9`, and the bitstream SHA-256 is `88F693AC4AEC4DEA03CB4BC0CB9D8C98A4023A015905369BB62688064FB58944`. Pinned HM reproduces the published complete-output MD5 `272e694a2262a2b34f6248f787a4431d`; the retained first picture has plane MD5 values `08723eb3fb41af96c87becc4f6973234`, `230778eb7df0ebc009ca92e9697ec4d6`, and `e8e21ed380d2272dc38384ecd6515e53`. - `SAO_A_RExt_MediaTek_1.bit` is the 12-bit 4:4:4 Range Extensions stream for PPS bit-shift scaling of SAO offsets. The archive SHA-256 is `D0B5646150B35FB8E4C1D51F3D451AEDDF964581132C9C49B253E34556BEEA4D`, and the bitstream SHA-256 is `6B0D45B5CA4D4919EAEC5A98DAF0EB4CEBCC6D8B0A12B30B748386CC9358DF13`. Pinned HM reproduces the published complete GBR-output MD5 `126cd42a185b327d86640a9269044bc8`; the retained first picture has G, B, and R plane MD5 values `fb342158a61b6cb3174b99d2e1167d7d`, `9ff5400aac0380474882acb903f51f89`, and `bb320be3c7905a5a220e0066a5edb991`. + +The parallelization fixtures are unchanged Annex B payloads from the official HEVC v1 attachment. Archives that name their payload `.bin` are retained byte-for-byte with the repository's common `.bit` extension. `HevcPictureDecoderTests.DecodeOfficialParallelizationPictureMatchesPublishedReference` adapts every complete first IDR picture to one bounded `hvc1` item, asserts its tile, wavefront, dependent-slice, entry-point, extension, and CTB geometry, and compares all three native planes exactly. + +Pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces the published complete-output MD5 for every dependent-slice, tile, and entry-point archive below. The WPP first-picture plane hashes come directly from the published YUV files included in their archives. The two combined dependent-slice cases also pass through 4 KiB allocator groups with balanced exactly-once disposal. + +- `DSLICE_A_HHI_5.bit`: archive SHA-256 `C41C1A1E1E700AF201EAF4CB0D11D4361B41BA0828E80C78D1835D1DE187F80C`; bitstream SHA-256 `8398FB23C814A197BBA497AD2C6103F81CA8003434FA40EC347D4D0A07C9468A`; published output MD5 `c7caf3164b0a316549ac7244f66f1294`. +- `DSLICE_B_HHI_5.bit`: archive SHA-256 `993B933AC6801899B6F04AE5DD2C20FBC2498FD9710A924611E66DE2F690716B`; bitstream SHA-256 `DB582F1181F3B8ED3BEEB32246A1A99123700DBDC3D3BE4E1797032FCD46C486`; published output MD5 `2e072130f116a4d0f7fc69fa76cfcdfb`. +- `DSLICE_C_HHI_5.bit`: archive SHA-256 `6836566F0A4465775B4757B84D7A89F453B635EBA2B941AE07132F7C7268C116`; bitstream SHA-256 `F162007F604AA0E48229C4153F940FD5F24DB955AD158D11E2BD46C89C49CA76`; published output MD5 `505a47c5ce4bc66ef5661aeb778b0f1f`. +- `TILES_A_Cisco_2.bit`: archive SHA-256 `BD63079966105A3BDAC764979CB82B708F586CC7C34859A1D466E18CC6A5C345`; bitstream SHA-256 `EFF78A401ECCCC21D995345988F1BE60EE76604CF10FA39D421C3E00668A94D6`; published output MD5 `946f0771fa87d49f684f96871c9c3c88`. +- `TILES_B_Cisco_1.bit`: archive SHA-256 `75B3C9B2981CDA59D497750322CDE7CA23D6CEF39FA9A8E11F36926758B7F83B`; bitstream SHA-256 `D2F65167917E42030DB804C60CA7775452445A61930CCEE3FA2AD2CD3830A57A`; published output MD5 `3382291f2b19ee2d760647d4cc756e95`. +- `WPP_A_ericsson_MAIN_2.bit`: archive SHA-256 `81C601E9301BA5F562EBF9918E73D1C7153E0A6FE8FA4509F61035A212188216`; bitstream SHA-256 `54D896D9FBDFA0AAE15629001105C6EE132C8459E152ABB06EFC62CEAD4324AE`; published output MD5 `cd7e815eb47e8138fec2185d4de84304`. +- `WPP_B_ericsson_MAIN_2.bit`: archive SHA-256 `57BFA7DC2417976825187B43829F1E107D87EBE661CFA4A02087EFAE3BD1148E`; bitstream SHA-256 `F1D18F737A9380F6CE58B6AEAE158AF458C5B557E06DD24977B7A5C6E095B9B2`; published output MD5 `e37c7e561a1226640a7bf98e81df78b1`. +- `WPP_C_ericsson_MAIN_2.bit`: archive SHA-256 `4D1F3E7FC1B05E22F2C53FABC670A3DB9A642BB511004C4D785B49582890BDF2`; bitstream SHA-256 `21CF0A7C5F6FBA5A76C7132A2714313D120D1443A0130400FB55AB1E454D5BDA`; published output MD5 `e067aa3a6a12cd5743849ded793c8d3f`. +- `WPP_D_ericsson_MAIN_2.bit`: archive SHA-256 `04C166DF6E6D68584910FCE77055F6C16428FC5C907BB1BEFD10BBF40C16B939`; bitstream SHA-256 `30EEC63F2324AA982FB91BD4C1C551C833C253BA711EE131AA9CC4D322398CAF`; published output MD5 `f710612103f386c415be3e6300693451`. +- `WPP_E_ericsson_MAIN_2.bit`: archive SHA-256 `10F4A50870F763D7CCBD975ADCE20176022A1B9712664244A94C069322534FAF`; bitstream SHA-256 `C8FE49762A13E1CC2B033308BB22AEB35DA0C96F94CA8EF7D87D95BDADAEAA2C`; published output MD5 `485798dbf95ad61232075df2f294aa3f`. +- `WPP_F_ericsson_MAIN_2.bit`: archive SHA-256 `39F5FFAA4C273C800B4E05715E2D4002E31B3BBE39505EA59B08C5FDABE6AF21`; bitstream SHA-256 `E8566E0E48509592DFAF7D314B7E292F51CEDE045559E3C32B447A9822A8B949`; published output MD5 `2aaf16274fe8e799d72fa08a4963850d`. +- `ENTP_A_Qualcomm_1.bit`: archive SHA-256 `66ABDF60455ECE468ACF160274316C51DCA3858D93A8FFCE93646CBB5568FE43`; bitstream SHA-256 `1DC1DFB16F676F59CEBDE3A4732209CE5CE0D0F49EF697970987C0C3E0E087B1`; published output MD5 `25f3c7facf9e7b75e81247206b4fc60d`. +- `ENTP_C_Qualcomm_1.bit`: archive SHA-256 `6CFDFE2F6BA8E1A860909550E5A486E7BF712FED00674869F45C1D82D05833F5`; bitstream SHA-256 `993CD68F0E749CD1D9CE7CC3A368B4DF26DCCF07DED91048100217B9A0C56D28`; published output MD5 `0b607e6e7946e6e8d0f92a1a294d135d`. + +The retained `ENTP_A_Qualcomm_1` first picture verifies its uniform 2x2 tile entry points. The archive description places its special entry-point emulation-prevention case at POC 4, which is outside the retained IDR picture. `HevcPictureDecoderTests.EntryPointOffsetsExcludeEmulationPreventionBytes` therefore verifies the same production boundary-conversion methods directly with prevention bytes in both the encoded header and the bounded non-final entropy substream; it does not claim that the later non-IDR picture itself is decoded. diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit new file mode 100644 index 000000000..23b755d90 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/TILES_A_Cisco_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eff78a401ecccc21d995345988f1be60ee76604cf10fa39d421c3e00668a94d6 +size 484767 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit new file mode 100644 index 000000000..024a8c1a4 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/TILES_B_Cisco_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2f65167917e42030db804c60ca7775452445a61930ccee3fa2ad2cd3830a57a +size 533127 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit new file mode 100644 index 000000000..c22e03f93 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_A_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54d896d9fbdfa0aae15629001105c6ee132c8459e152abb06efc62cead4324ae +size 67554 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit new file mode 100644 index 000000000..790f5d5b7 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_B_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1d18f737a9380f6ce58b6aeae158af458c5b557e06dd24977b7a5c6e095b9b2 +size 68895 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit new file mode 100644 index 000000000..77b1994d3 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_C_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21cf0a7c5f6fba5a76c7132a2714313d120d1443a0130400fb55ab1e454d5bda +size 71856 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit new file mode 100644 index 000000000..8aa7b24e6 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_D_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30eec63f2324aa982fb91bd4c1c551c833c253ba711ee131aa9cc4d322398caf +size 22474 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit new file mode 100644 index 000000000..522863c1b --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_E_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8fe49762a13e1cc2b033308bb22aeb35da0c96f94ca8ef7d87d95bdadaeaa2c +size 29642 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit new file mode 100644 index 000000000..350f57cfc --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/WPP_F_ericsson_MAIN_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8566e0e48509592dfaf7d314b7e292f51cede045559e3c32b447a9822a8b949 +size 31461