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