diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs index b83c1cf6e..e71e80db7 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictionState.cs @@ -102,6 +102,13 @@ internal sealed class HevcIntraPredictionState : IDisposable this.WidthInMinPredictionBlocks, this.HeightInMinPredictionBlocks); + // PCM coding units skip intra-mode syntax but remain available as most-probable-mode neighbors. HM + // initializes every luma direction to DC so those units provide the required default until syntax replaces it. + for (int row = 0; row < this.HeightInMinPredictionBlocks; row++) + { + lumaModes.DangerousGetRowSpan(row).Fill(DcMode); + } + this.lumaModes = lumaModes; this.chromaModes = chromaModes; this.effectiveChromaModes = effectiveChromaModes; diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs index 58f818dd7..5da5de2a1 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs @@ -15,6 +15,48 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; [Trait("Format", "Heif")] public class HevcPictureDecoderTests { + /// + /// Identifies residual-tool signaling that an official independently decoded picture must exercise. + /// + [Flags] + public enum ResidualTools + { + /// + /// No optional residual tool is signaled. + /// + None = 0, + + /// + /// Coding-unit luma quantization deltas are signaled. + /// + DeltaQuantization = 1, + + /// + /// Non-flat quantization scaling matrices are enabled. + /// + ScalingLists = 2, + + /// + /// Transform skip is enabled. + /// + TransformSkip = 4, + + /// + /// Range-extension transform precision is enabled. + /// + ExtendedPrecision = 8, + + /// + /// Transform and quantization bypass is enabled. + /// + TransquantizationBypass = 16, + + /// + /// A coding-unit chroma quantization-offset list is enabled. + /// + ChromaQuantizationAdjustment = 32, + } + /// /// Verifies the first independently coded picture from official ITU RExt conformance streams against its /// published decoded-picture hashes. @@ -144,6 +186,57 @@ public class HevcPictureDecoderTests Assert.Equal("baafaef47a55ae2e876862b30b3bc720", GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); } + /// + /// Verifies official independently coded residual-tool pictures against pinned-HM native-plane hashes. + /// + /// The official or provenance-preserving extracted Annex B picture. + /// The signaled component precision. + /// The signaled HEVC chroma-format identifier. + /// The residual tools that the retained picture signals. + /// The pinned-HM luma-plane digest. + /// The pinned-HM blue-difference-plane digest. + /// The pinned-HM red-difference-plane digest. + [Theory] + [InlineData(TestImages.Heif.DeltaQuantizationParameterA, 8, 1, ResidualTools.DeltaQuantization, "2b715c3517e40c00f296260fd0d591c6", "e261d9de5312cba7ac2e355a976ce062", "ca058a402db52ae33aacfcd8c73ae3c6")] + [InlineData(TestImages.Heif.QuantizationMatrixA, 8, 3, ResidualTools.DeltaQuantization | ResidualTools.ScalingLists | ResidualTools.TransformSkip, "6995cec045398044d9cb9668d01fe295", "970394f8a6df16378a39ef2e8fbad354", "1c76949b0ee61b9c1a7d99681a324419")] + [InlineData(TestImages.Heif.ExtendedPrecision12Bit444, 12, 3, ResidualTools.TransformSkip | ResidualTools.ExtendedPrecision, "c8664d56d8391b236a8347397eb97a08", "08cd345d8798ac2714b2939462ac45e2", "5424bce4562f298e983302da697db849")] + [InlineData(TestImages.Heif.ChromaQuantizationAdjustment12Bit444, 12, 3, ResidualTools.TransformSkip | ResidualTools.ChromaQuantizationAdjustment, "0279d9ab84612be260dbd3d4832369b1", "b7eec690a0e5685913ac59d8121ea3e9", "64d7b590e5666f9286204e7e43c6a330")] + [InlineData(TestImages.Heif.LosslessA, 8, 1, ResidualTools.DeltaQuantization | ResidualTools.TransformSkip | ResidualTools.TransquantizationBypass, "6d063ac9bc53ab53e142e300e668c32e", "b69a3e55ff000c0418b79471247ca73f", "1b7449f2f395578ead369f6abde7c4eb")] + public void DecodeOfficialResidualToolsPictureMatchesPinnedHmDigest( + string path, + int bitDepth, + byte chromaFormat, + ResidualTools expectedTools, + string lumaDigest, + string chromaBlueDigest, + string chromaRedDigest) + { + byte[] annexB = TestFile.Create(path).Bytes; + ConvertAnnexBStillPicture(annexB, bitDepth, chromaFormat, 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); + + Assert.Equal(bitDepth, decoder.Picture.BitDepthLuma); + Assert.Equal(chromaFormat, decoder.Picture.ChromaFormat); + Assert.Equal((expectedTools & ResidualTools.DeltaQuantization) != 0, pictureParameterSet.CodingUnitQuantizationParameterDeltaEnabled); + Assert.Equal((expectedTools & ResidualTools.ScalingLists) != 0, sequenceParameterSet.ScalingListEnabled); + Assert.Equal((expectedTools & ResidualTools.TransformSkip) != 0, pictureParameterSet.TransformSkipEnabled); + Assert.Equal((expectedTools & ResidualTools.ExtendedPrecision) != 0, sequenceParameterSet.ExtendedPrecisionProcessingEnabled); + Assert.Equal((expectedTools & ResidualTools.TransquantizationBypass) != 0, pictureParameterSet.TransquantizationBypassEnabled); + Assert.Equal( + (expectedTools & ResidualTools.ChromaQuantizationAdjustment) != 0, + pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count != 0); + + 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 coding-tree and transform-tree conformance streams against native-plane digests produced by the /// pinned HM decoder from output that matches each archive's published checksum. diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6843d4553..210159391 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1307,6 +1307,11 @@ public static class TestImages public const string IntraPredictionB = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.bit"; public const string IntraPredictionBReference = "Heif/Hevc/Conformance/IPRED_B_Nokia_3.yuv"; public const string ConstrainedIntraPredictionA = "Heif/Hevc/Conformance/CIP_A_Panasonic_3.bit"; + public const string DeltaQuantizationParameterA = "Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit"; + public const string ExtendedPrecision12Bit444 = "Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit"; + public const string ChromaQuantizationAdjustment12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit"; + public const string LosslessA = "Heif/Hevc/Conformance/LS_A_Orange_2.bit"; + public const string QuantizationMatrixA = "Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit"; public const string RqtA = "Heif/Hevc/Conformance/RQT_A_HHI_4.bit"; public const string RqtB = "Heif/Hevc/Conformance/RQT_B_HHI_4.bit"; public const string RqtC = "Heif/Hevc/Conformance/RQT_C_HHI_4.bit"; diff --git a/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit b/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit new file mode 100644 index 000000000..b26e7149c --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/DELTAQP_A_BRCM_4_frame0.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dc49a2aae1cce20d5cd190a1342faf4c3b2bd06d1a59824542b9b22f53c581b +size 230658 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit new file mode 100644 index 000000000..fc59086ae --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4107d5bb5df4f95185e7a8a5eef8f35970a4769c76901ff590f38f7bc36b268b +size 205277 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit new file mode 100644 index 000000000..db90f260c --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2_idr7.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:981d97f0adc228f2e3a739d7a68161f84cd60309745397d5cf24d2f81c009dbc +size 86390 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit b/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit new file mode 100644 index 000000000..e77bd3ef6 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/LS_A_Orange_2.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14251238f005a4576437429a327b34d216b346aaac2a7a05c132a3430174b11b +size 593001 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit new file mode 100644 index 000000000..baba05904 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/QMATRIX_A_RExt_Sony_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74847e07f3298923da0e9b68b1f861930d227bd5ad83a834a8280cf042a7e78f +size 236915 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/README.md b/tests/Images/Input/Heif/Hevc/Conformance/README.md index 53fb38abd..4b86582a3 100644 --- a/tests/Images/Input/Heif/Hevc/Conformance/README.md +++ b/tests/Images/Input/Heif/Hevc/Conformance/README.md @@ -22,3 +22,11 @@ The intra-prediction fixtures come from the same official HEVC v1 attachment: - `CIP_A_Panasonic_3` signals constrained intra prediction and contains one I picture followed by one B picture. The bounded still decoder test extracts only the independently coded I picture. The archive SHA-256 is `5C55B3594E5C951C9F5312913961750283EB2DADD15844FE35F9BEB94A9A548C`, and the published bitstream MD5 is `e1e00592fc8a158da9109b4dc05d03be`. HM at commit `9c1f298659ab0cee9dc13d23d0304221575410b9` reproduces the published `IPRED_B_Nokia_3` YUV MD5 exactly and reports decoded-picture hashes `200ef9f7d2efde44ab872a78a40ece42`, `bfe9de3bef5ce5596a71bbfdbfb823fc`, and `5b14acdd5c5bf8c6887160817ee1ad11`. The same pinned decoder reproduces `CIP_A_Panasonic_3`'s published complete two-picture YUV MD5 `4cbf601ba98d63f642defab5eaa12c8d`; its independently coded first picture reports plane hashes `69a20189e6bbb9c088e3adc967244ca1`, `26502d354bb123f54c20413f14360ddb`, and `baafaef47a55ae2e876862b30b3bc720`. + +The residual-reconstruction fixtures come from the official HEVC v1 and Range Extensions attachments. Pinned HM commit `9c1f298659ab0cee9dc13d23d0304221575410b9` decodes every retained independently coded picture and produces the plane hashes asserted by the tests. + +- `DELTAQP_A_BRCM_4_frame0.bit` is the exact first independently coded picture extracted from `DELTAQP_A_BRCM_4.bit`. The official archive has SHA-256 `63ABBBAF43D334D7952EAB7BBC5B3E34B4AD0259EDDCD63CDB164DBCF9A36CA6`, the original bitstream has SHA-256 `1274E3E308DFE684B38A88F8B3A2845B84F57492B0B02AAECBE5DC29CA8ED532`, and the retained picture has SHA-256 `4DC49A2AAE1CCE20D5CD190A1342FAF4C3B2BD06D1A59824542B9B22F53C581B`. The official description randomizes `diff_cu_qp_delta_depth` from zero through three and delta QP from -26 through 25; the retained picture contains 384 delta-QP CABAC decisions. +- `QMATRIX_A_RExt_Sony_1.bit` has SHA-256 `74847E07F3298923DA0E9B68B1F861930D227BD5AD83A834A8280CF042A7E78F`. Its official description covers scaling-list data with and without transform skip at every transform size over different QPs; the retained first picture signals scaling lists, coding-unit QP deltas, transform skip, transform-skip contexts and rotation, and implicit residual DPCM. +- `EXTPREC_MAIN_444_16_INTRA_12BIT_RExt_Sony_1.bit` is the second of the official stream's two concatenated one-picture sequences, retained with its own VPS, SPS, and PPS. The official archive has SHA-256 `66C8E24866210B94A63A29C93F8C1490046CC17B26484C5BD81F805D7BFD18D1`, the original bitstream has SHA-256 `5F0164DCCC333296FB2753068CAFE4985B758DDB7E282358DBB8FAF74986817D`, and the retained sequence has SHA-256 `4107D5BB5DF4F95185E7A8A5EEF8F35970A4769C76901FF590F38F7BC36B268B`. This 12-bit GBR sequence is the one that signals extended-precision processing and transform skip. Its reference plane hashes were produced by the pinned HM commit built with its `HIGH_BITDEPTH` option, which is required by HM for this sequence. +- `GENERAL_12b_444_RExt_Sony_2_idr7.bit` is the seventh independently coded picture extracted with its active VPS, SPS, and PPS from the already retained official GENERAL stream. It has SHA-256 `981D97F0ADC228F2E3A739D7A68161F84CD60309745397D5CF24D2F81C009DBC` and contains 39 coding-unit chroma-QP adjustment decisions across 8-, 16-, and 32-sample chroma transforms. +- `LS_A_Orange_2.bit` has SHA-256 `14251238F005A4576437429A327B34D216B346AAAC2A7A05C132A3430174B11B` and published MD5 `9118d01cf6b3671038d6f99342c0895e`. The official lossless description states that every coding unit uses transform, quantization, and filtering bypass; the retained first picture exercises the bypass-flag path and its decoded-picture hash reports OK.