diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 84245254a2..0d93429bb8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1431,6 +1431,22 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.pngColorType = this.header.ColorType; this.Dimensions = new Size(this.header.Width, this.header.Height); + + // Apple's pngcrush emits the CgBI chunk before IHDR, so the header + // compatibility check is deferred until both chunks have been seen. + if (this.isCgbi) + { + ThrowIfInvalidCgbiContent(this.header); + } + } + + private static void ThrowIfInvalidCgbiContent(in PngHeader header) + { + if (header.BitDepth != 8 || (header.ColorType is not PngColorType.Rgb and not PngColorType.RgbWithAlpha)) + { + PngThrowHelper.ThrowInvalidImageContentException( + $"CgBI is only supported for 8-bit truecolor images. Was bit depth '{header.BitDepth}', color type '{header.ColorType}'."); + } } /// diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 2e452b896d..2fbbe695e9 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -767,6 +767,28 @@ public partial class PngDecoderTests Assert.Equal(expectedHeight, imageInfo.Height); } + [Theory] + [InlineData(TestImages.Png.Cgbi.BitDepth16)] + [InlineData(TestImages.Png.Cgbi.Palette)] + public void Identify_CgBI_IncompatibleHeader_ThrowsInvalidImageContentException(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + InvalidImageContentException ex = Assert.Throws(() => Image.Identify(stream)); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + + [Theory] + [WithFile(TestImages.Png.Cgbi.BitDepth16, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Palette, PixelTypes.Rgba32)] + public void Decode_CgBI_IncompatibleHeader_ThrowsInvalidImageContentException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + InvalidImageContentException ex = Assert.Throws( + () => { using Image image = provider.GetImage(PngDecoder.Instance); }); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + [Theory] [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Bike, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1b6ae56850..7b43ab262c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -189,6 +189,10 @@ public static class TestImages // Issue 410: https://github.com/SixLabors/ImageSharp/issues/410 public const string Issue410 = "Png/issues/Issue_410.png"; + + // Synthetic fixtures derived from colors.png to exercise CgBI validation. + public const string BitDepth16 = "Png/cgbi/colors-cgbi-bitdepth16.png"; + public const string Palette = "Png/cgbi/colors-cgbi-palette.png"; } public static class Bad diff --git a/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png b/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png new file mode 100644 index 0000000000..18cfa9246d --- /dev/null +++ b/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59610bc03f6ca867e5f71c574b3a0d1942c9e3a230c8a32bf3007cb82f286866 +size 12853 diff --git a/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png b/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png new file mode 100644 index 0000000000..f6406559b1 --- /dev/null +++ b/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a2f20c69ae423523a8f41887e3f37257a338f2220c2ea44d35c87daf8c3aa3 +size 12853