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.