diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs index d8bc059dfa..64210e9d42 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.Prediction.cs @@ -17,8 +17,9 @@ internal sealed partial class HevcPictureDecoder /// The base-two logarithm of the square prediction-block side. /// The current independent-slice and tile prediction region. /// The selected separate-color plane, or zero for combined coding. + /// Whether the governing coding unit bypasses inverse quantization and transform. /// The packed predicted samples. - private Span PredictComponentBlock(HevcPlane plane, int x, int y, int log2Size, int regionId, int colorPlaneIndex) + private Span PredictComponentBlock(HevcPlane plane, int x, int y, int log2Size, int regionId, int colorPlaneIndex, bool transquantBypass) { int size = 1 << log2Size; int sampleCount = size * size; @@ -69,6 +70,14 @@ internal sealed partial class HevcPictureDecoder mode = HevcIntraPredictionMode.RemapChroma422(mode); } + // H.265 8.4.4.2.3 and 8.4.4.2.6 restrict prediction-edge filtering to luma blocks no larger than 16 samples. + // Implicit RDPCM bypasses that filtering for the lossless horizontal and vertical prediction modes. + bool filterPredictionEdges = useLumaSyntax + && size <= 16 + && !(transquantBypass + && this.sequenceParameterSet.ImplicitResidualDpcmEnabled + && (mode == HevcIntraPredictionMode.Horizontal || mode == HevcIntraPredictionMode.Vertical)); + bool filterReferences = HevcIntraPredictor.ShouldFilterReferenceSamples( useLumaSyntax ? HevcPlane.Y : plane, mode, @@ -101,7 +110,7 @@ internal sealed partial class HevcPictureDecoder log2Size, mode, this.Picture.GetBitDepth(plane), - useLumaSyntax, + filterPredictionEdges, operationScratch); return prediction; diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs index eefc75e19e..ffe8e34572 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcPictureDecoder.TransformTree.cs @@ -369,7 +369,7 @@ internal sealed partial class HevcPictureDecoder Span dequantized = integerScratch.Slice(MaximumTransformSampleCount, MaximumTransformSampleCount); Span residual = integerScratch.Slice(MaximumTransformSampleCount * 2, MaximumTransformSampleCount); Span transformScratch = integerScratch.Slice(MaximumTransformSampleCount * 4, MaximumTransformSampleCount * 2); - Span prediction = this.PredictComponentBlock(plane, x, y, log2Size, regionId, colorPlaneIndex); + Span prediction = this.PredictComponentBlock(plane, x, y, log2Size, regionId, colorPlaneIndex, transquantBypass); residual[..sampleCount].Clear(); bool useLumaSyntax = this.sequenceParameterSet.SeparateColorPlaneFlag; HevcPlane codingPlane = useLumaSyntax ? HevcPlane.Y : plane; diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs index 0a03804972..27a81568af 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs @@ -21,6 +21,7 @@ public class HevcPictureDecoderTests [InlineData(TestImages.Heif.Image1TileHvcConfiguration, TestImages.Heif.Image1Tile1Payload, TestImages.Heif.Image1Tile1ReferenceYuv)] [InlineData(TestImages.Heif.Image1TileHvcConfiguration, TestImages.Heif.Image1Tile2Payload, TestImages.Heif.Image1Tile2ReferenceYuv)] [InlineData(TestImages.Heif.Image2TileHvcConfiguration, TestImages.Heif.Image2Tile1Payload, TestImages.Heif.Image2Tile1ReferenceYuv)] + [InlineData(TestImages.Heif.Image2TileHvcConfiguration, TestImages.Heif.Image2Tile7Payload, TestImages.Heif.Image2Tile7ReferenceYuv)] [InlineData(TestImages.Heif.DwsampleTileHvcConfiguration, TestImages.Heif.DwsampleTilePayload, TestImages.Heif.DwsampleTileReferenceYuv)] public void DecodeRealHeicTileMatchesHmReference(string configurationPath, string itemPath, string referencePath) { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ac98e8576c..78e4029f1b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1289,6 +1289,8 @@ public static class TestImages public const string Image2TileHvcConfiguration = "Heif/Hevc/image2-tile.hvcc"; public const string Image2Tile1Payload = "Heif/Hevc/image2-tile1.hvc1"; public const string Image2Tile1ReferenceYuv = "Heif/Hevc/image2-tile1-hm.yuv"; + public const string Image2Tile7Payload = "Heif/Hevc/image2-tile7.hvc1"; + public const string Image2Tile7ReferenceYuv = "Heif/Hevc/image2-tile7-hm.yuv"; public const string DwsampleTileHvcConfiguration = "Heif/Hevc/dwsample-tile.hvcc"; public const string DwsampleTilePayload = "Heif/Hevc/dwsample-tile.hvc1"; public const string DwsampleTileReferenceYuv = "Heif/Hevc/dwsample-tile-hm.yuv"; diff --git a/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv b/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv new file mode 100644 index 0000000000..15e68c75a8 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/image2-tile7-hm.yuv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d85526927c19fd642b7173215767d2e822e4d0eda58eaae3bb5e3a498659ea4f +size 393216 diff --git a/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 b/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 new file mode 100644 index 0000000000..0ca9b368e3 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/image2-tile7.hvc1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bd5ebc70cea2c8359a3afdd81eed0cdb9e86616516a7b974a7f8017139bb939 +size 7945