From a940a55f5374e0baacb4da23dc85e7dd91807109 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 23 Feb 2024 23:07:34 +1000 Subject: [PATCH 01/13] Make DrawImage processor more robust to bad input. --- .../Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 0d81270b16..d5499d5865 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -98,9 +98,10 @@ protected override void OnFrameApply(ImageFrame source) top = 0; } - // clamp the height/width to the availible space left to prevent overflowing + // Clamp the height/width to the available space left to prevent overflowing foregroundRectangle.Width = Math.Min(source.Width - left, foregroundRectangle.Width); foregroundRectangle.Height = Math.Min(source.Height - top, foregroundRectangle.Height); + foregroundRectangle = Rectangle.Intersect(foregroundRectangle, this.ForegroundImage.Bounds); int width = foregroundRectangle.Width; int height = foregroundRectangle.Height; @@ -111,7 +112,6 @@ protected override void OnFrameApply(ImageFrame source) } // Sanitize the dimensions so that we don't try and sample outside the image. - foregroundRectangle = Rectangle.Intersect(foregroundRectangle, this.ForegroundImage.Bounds); Rectangle backgroundRectangle = Rectangle.Intersect(new(left, top, width, height), this.SourceRectangle); Configuration configuration = this.Configuration; From b4049ce517ddd94fdbac398633f895957defe157 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 24 Feb 2024 00:12:15 +1000 Subject: [PATCH 02/13] Don't skip data read on animation and frame control chunks --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 9 +++++++-- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 8 ++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/issues/Issue_2666.png | 3 +++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Png/issues/Issue_2666.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 95154d68d3..179bf38ba8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1871,8 +1871,13 @@ private bool TryReadChunk(Span buffer, out PngChunk chunk) PngChunkType type = this.ReadChunkType(buffer); // If we're reading color metadata only we're only interested in the IHDR and tRNS chunks. - // We can skip all other chunk data in the stream for better performance. - if (this.colorMetadataOnly && type != PngChunkType.Header && type != PngChunkType.Transparency && type != PngChunkType.Palette) + // We can skip most other chunk data in the stream for better performance. + if (this.colorMetadataOnly && + type != PngChunkType.Header && + type != PngChunkType.Transparency && + type != PngChunkType.Palette && + type != PngChunkType.AnimationControl && + type != PngChunkType.FrameControl) { chunk = new PngChunk(length, type); return true; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index bc277bf485..743d1f797c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.IO; using System.Runtime.Intrinsics.X86; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; @@ -665,4 +666,11 @@ public void Binary_PrematureEof() Assert.True(eofHitCounter.EofHitCount <= 3); Assert.Equal(new Size(200, 120), eofHitCounter.Image.Size); } + + [Fact] + public void Decode_Issue2666() + { + string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Png.Issue2666)); + using Image image = Image.Load(path); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8aa95d3496..82db313585 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -73,6 +73,7 @@ public static class Png public const string DisposeBackgroundRegion = "Png/animated/15-dispose-background-region.png"; public const string DisposePreviousFirst = "Png/animated/12-dispose-prev-first.png"; public const string BlendOverMultiple = "Png/animated/21-blend-over-multiple.png"; + public const string Issue2666 = "Png/Issues/Issue_2666.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; diff --git a/tests/Images/Input/Png/issues/Issue_2666.png b/tests/Images/Input/Png/issues/Issue_2666.png new file mode 100644 index 0000000000..b918fd4744 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2666.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed7665cdfd5fad00c5995040350a254b96af6c0c95ab13975f2291e9d3fce0f3 +size 8244837 From 83ecbdb19fb180266a154896ccb9949f3bdd8c5d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 10:27:03 +1000 Subject: [PATCH 03/13] Fix path --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 1 - tests/ImageSharp.Tests/TestImages.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 743d1f797c..b9f6075fb8 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using System.Runtime.Intrinsics.X86; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 82db313585..4e33bbd8ad 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -73,7 +73,7 @@ public static class Png public const string DisposeBackgroundRegion = "Png/animated/15-dispose-background-region.png"; public const string DisposePreviousFirst = "Png/animated/12-dispose-prev-first.png"; public const string BlendOverMultiple = "Png/animated/21-blend-over-multiple.png"; - public const string Issue2666 = "Png/Issues/Issue_2666.png"; + public const string Issue2666 = "Png/issues/Issue_2666.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; From 3c65383dec1c0291824596a485c6840b8bf38e1a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 11:56:46 +1000 Subject: [PATCH 04/13] Prevent underflow --- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 10 ++++------ .../ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs | 11 +++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Webp/issues/Issue2670.webp | 3 +++ 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/Images/Input/Webp/issues/Issue2670.webp diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 63e6541354..125ed0acd7 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossless; @@ -311,18 +312,15 @@ private static void ColorIndexInverseTransformAlpha( private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { - if (Sse2.IsSupported) + // TODO: Investigate AdvSim support for this method. + if (Sse2.IsSupported && width >= 9) { dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0])); - if (width <= 1) - { - return; - } - nuint i; Vector128 last = Vector128.Zero.WithElement(0, dst[0]); ref byte srcRef = ref MemoryMarshal.GetReference(input); ref byte dstRef = ref MemoryMarshal.GetReference(dst); + for (i = 1; i <= (uint)width - 8; i += 8) { Vector128 a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index c38b13075a..0dda304b64 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -439,6 +439,17 @@ public void WebpDecoder_CanDecode_Issue2257(TestImageProvider pr image.CompareToOriginal(provider, ReferenceDecoder); } + // https://github.com/SixLabors/ImageSharp/issues/2670 + [Theory] + [WithFile(Lossy.Issue2670, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2670(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(WebpDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); + } + [Theory] [WithFile(Lossless.LossLessCorruptImage3, PixelTypes.Rgba32)] public void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 4e33bbd8ad..b2085c6836 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -804,6 +804,7 @@ public static class Lossy public const string Issue1594 = "Webp/issues/Issue1594.webp"; public const string Issue2243 = "Webp/issues/Issue2243.webp"; public const string Issue2257 = "Webp/issues/Issue2257.webp"; + public const string Issue2670 = "Webp/issues/Issue2670.webp"; } } diff --git a/tests/Images/Input/Webp/issues/Issue2670.webp b/tests/Images/Input/Webp/issues/Issue2670.webp new file mode 100644 index 0000000000..4dd1248986 --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2670.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23ad5eb449f693af68e51dd108a6b9847a8eb48b82ca5b848395a54c2e0be08f +size 152 From 15619ecd88bc67b6bf4a002094ba241836f8f10a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 12:01:11 +1000 Subject: [PATCH 05/13] Fix file name --- tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 2 +- .../Images/Input/Webp/issues/{Issue2670.webp => Issue2676.webp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/Images/Input/Webp/issues/{Issue2670.webp => Issue2676.webp} (100%) diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 0dda304b64..b0b7c294ab 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -441,7 +441,7 @@ public void WebpDecoder_CanDecode_Issue2257(TestImageProvider pr // https://github.com/SixLabors/ImageSharp/issues/2670 [Theory] - [WithFile(Lossy.Issue2670, PixelTypes.Rgba32)] + [WithFile(Lossy.Issue2676, PixelTypes.Rgba32)] public void WebpDecoder_CanDecode_Issue2670(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index b2085c6836..1eba38e4a3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -804,7 +804,7 @@ public static class Lossy public const string Issue1594 = "Webp/issues/Issue1594.webp"; public const string Issue2243 = "Webp/issues/Issue2243.webp"; public const string Issue2257 = "Webp/issues/Issue2257.webp"; - public const string Issue2670 = "Webp/issues/Issue2670.webp"; + public const string Issue2676 = "Webp/issues/Issue2676.webp"; } } diff --git a/tests/Images/Input/Webp/issues/Issue2670.webp b/tests/Images/Input/Webp/issues/Issue2676.webp similarity index 100% rename from tests/Images/Input/Webp/issues/Issue2670.webp rename to tests/Images/Input/Webp/issues/Issue2676.webp From 12d05b77cd3cbf1ceab13f8c60c62f8600060fbc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 12:04:03 +1000 Subject: [PATCH 06/13] Revert "Fix file name" This reverts commit 15619ecd88bc67b6bf4a002094ba241836f8f10a. --- tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 2 +- .../Images/Input/Webp/issues/{Issue2676.webp => Issue2670.webp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/Images/Input/Webp/issues/{Issue2676.webp => Issue2670.webp} (100%) diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index b0b7c294ab..0dda304b64 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -441,7 +441,7 @@ public void WebpDecoder_CanDecode_Issue2257(TestImageProvider pr // https://github.com/SixLabors/ImageSharp/issues/2670 [Theory] - [WithFile(Lossy.Issue2676, PixelTypes.Rgba32)] + [WithFile(Lossy.Issue2670, PixelTypes.Rgba32)] public void WebpDecoder_CanDecode_Issue2670(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1eba38e4a3..b2085c6836 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -804,7 +804,7 @@ public static class Lossy public const string Issue1594 = "Webp/issues/Issue1594.webp"; public const string Issue2243 = "Webp/issues/Issue2243.webp"; public const string Issue2257 = "Webp/issues/Issue2257.webp"; - public const string Issue2676 = "Webp/issues/Issue2676.webp"; + public const string Issue2670 = "Webp/issues/Issue2670.webp"; } } diff --git a/tests/Images/Input/Webp/issues/Issue2676.webp b/tests/Images/Input/Webp/issues/Issue2670.webp similarity index 100% rename from tests/Images/Input/Webp/issues/Issue2676.webp rename to tests/Images/Input/Webp/issues/Issue2670.webp From 223f6474c2035171de7fa4e43a8cd088f172e4a1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 12:04:37 +1000 Subject: [PATCH 07/13] Update AlphaDecoder.cs --- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 125ed0acd7..7033bf167c 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -6,7 +6,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossless; From 2a548818ac3c7a5e328c72757f5464007579d851 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 16:35:28 +1000 Subject: [PATCH 08/13] Use a smarter approach to determine the transparent index --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 20 ++++++++++++++++++- .../Formats/Png/PngEncoderTests.cs | 17 ++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 +++ ...antized_Encode_Alpha_Rgba32_issue_2668.png | 3 +++ tests/Images/Input/Png/issues/Issue_2668.png | 3 +++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png create mode 100644 tests/Images/Input/Png/issues/Issue_2668.png diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index ddef1c9cd9..ea94270f2f 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Binary; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; @@ -1527,7 +1528,24 @@ private void SanitizeAndSetEncoderOptions( { // We can use the color data from the decoded metadata here. // We avoid dithering by default to preserve the original colors. - this.derivedTransparencyIndex = metadata.ColorTable.Value.Span.IndexOf(Color.Transparent); + ReadOnlySpan palette = metadata.ColorTable.Value.Span; + + // Certain operations perform alpha premultiplication, which can cause the color to change so we + // must search for the transparency index in the palette. + // Transparent pixels are much more likely to be found at the end of a palette. + int index = -1; + for (int i = palette.Length - 1; i >= 0; i--) + { + Vector4 instance = palette[i].ToScaledVector4(); + if (instance.W == 0f) + { + index = i; + break; + } + } + + this.derivedTransparencyIndex = index; + this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null }, this.derivedTransparencyIndex); } else diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index e70854b082..950f1d2e3a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -678,6 +679,22 @@ public void Issue2469_Quantized_Encode_Artifacts(TestImageProvider(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + image.Mutate(x => x.Resize(100, 100)); + + PngEncoder encoder = new() { BitDepth = PngBitDepth.Bit8, ColorType = PngColorType.Palette }; + + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder); + using Image encoded = Image.Load(actualOutputFile); + encoded.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + private static void TestPngEncoderCore( TestImageProvider provider, PngColorType pngColorType, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 4e33bbd8ad..a6532a0905 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -151,6 +151,9 @@ public static class Png // Issue 2447: https://github.com/SixLabors/ImageSharp/issues/2447 public const string Issue2447 = "Png/issues/issue_2447.png"; + // Issue 2668: https://github.com/SixLabors/ImageSharp/issues/2668 + public const string Issue2668 = "Png/issues/issue_2668.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png new file mode 100644 index 0000000000..7af5391f70 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f934af128b85b9e8f557d71ac8b1f1473a0922d0754fc0c4ece0d0e3d8d94c39 +size 7702 diff --git a/tests/Images/Input/Png/issues/Issue_2668.png b/tests/Images/Input/Png/issues/Issue_2668.png new file mode 100644 index 0000000000..2ca8c46171 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2668.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8e5b2b933fd8fefd161f1d22970cb60247fd2d93b6c07b8b9ee1fdbc2241a3c +size 390225 From ed2cd3184ff9478f265fbb4ec40482d44e6d971d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 20:38:29 +1000 Subject: [PATCH 09/13] Fix casing --- tests/ImageSharp.Tests/TestImages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a6532a0905..3d9da1072a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -152,7 +152,7 @@ public static class Png public const string Issue2447 = "Png/issues/issue_2447.png"; // Issue 2668: https://github.com/SixLabors/ImageSharp/issues/2668 - public const string Issue2668 = "Png/issues/issue_2668.png"; + public const string Issue2668 = "Png/issues/Issue_2668.png"; public static class Bad { From f6d24ed15c94a5a7ca20bdcbb3b4a52c15564544 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 20:49:13 +1000 Subject: [PATCH 10/13] Update src/ImageSharp/Formats/Webp/AlphaDecoder.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 7033bf167c..63571617fb 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -311,7 +311,7 @@ private static void ColorIndexInverseTransformAlpha( private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { - // TODO: Investigate AdvSim support for this method. + // TODO: Investigate AdvSimd support for this method. if (Sse2.IsSupported && width >= 9) { dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0])); From 624277ba8803fc8b8062139c44cefe88118473a5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2024 21:01:40 +1000 Subject: [PATCH 11/13] Update Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png --- ...png => Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Images/External/ReferenceOutput/PngEncoderTests/{Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png => Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png} (100%) diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png similarity index 100% rename from tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_issue_2668.png rename to tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2668_Quantized_Encode_Alpha_Rgba32_Issue_2668.png From edc87ad90d4fcb0783c0c38be01a91a0dd26cd28 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 1 Mar 2024 20:18:35 +1000 Subject: [PATCH 12/13] Limit ancillary chunk size. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 38 +++++++++---------- .../Formats/Png/PngDecoderOptions.cs | 6 +++ .../Formats/Png/PngDecoderTests.cs | 19 ++++++++++ tests/ImageSharp.Tests/TestImages.cs | 4 +- tests/Images/Input/Png/issues/bad-ztxt.png | 3 ++ tests/Images/Input/Png/issues/bad-ztxt2.png | 3 ++ 6 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 tests/Images/Input/Png/issues/bad-ztxt.png create mode 100644 tests/Images/Input/Png/issues/bad-ztxt2.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 179bf38ba8..a96c53c104 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -120,6 +120,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// private readonly PngCrcChunkHandling pngCrcChunkHandling; + /// + /// The maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed. + /// + private readonly int maxUncompressedLength; + /// /// Initializes a new instance of the class. /// @@ -132,6 +137,7 @@ public PngDecoderCore(PngDecoderOptions options) this.skipMetadata = options.GeneralOptions.SkipMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; this.pngCrcChunkHandling = options.PngCrcChunkHandling; + this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) @@ -143,6 +149,7 @@ internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; this.pngCrcChunkHandling = options.PngCrcChunkHandling; + this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes; } /// @@ -596,23 +603,7 @@ private static void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan d private void InitializeImage(ImageMetadata metadata, FrameControl frameControl, out Image image) where TPixel : unmanaged, IPixel { - // When ignoring data CRCs, we can't use the image constructor that leaves the buffer uncleared. - if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll) - { - image = new Image( - this.configuration, - this.header.Width, - this.header.Height, - metadata); - } - else - { - image = Image.CreateUninitialized( - this.configuration, - this.header.Width, - this.header.Height, - metadata); - } + image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngMetadata(); frameMetadata.FromChunk(in frameControl); @@ -1572,7 +1563,7 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan da ReadOnlySpan compressedData = data[(zeroIndex + 2)..]; - if (this.TryDecompressZlibData(compressedData, out byte[] iccpProfileBytes)) + if (this.TryDecompressZlibData(compressedData, this.maxUncompressedLength, out byte[] iccpProfileBytes)) { metadata.IccProfile = new IccProfile(iccpProfileBytes); } @@ -1582,9 +1573,10 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan da /// Tries to decompress zlib compressed data. /// /// The compressed data. + /// The maximum uncompressed length. /// The uncompressed bytes array. /// True, if de-compressing was successful. - private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, out byte[] uncompressedBytesArray) + private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, int maxLength, out byte[] uncompressedBytesArray) { fixed (byte* compressedDataBase = compressedData) { @@ -1604,6 +1596,12 @@ private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, out int bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length); while (bytesRead != 0) { + if (memoryStreamOutput.Length > maxLength) + { + uncompressedBytesArray = Array.Empty(); + return false; + } + memoryStreamOutput.Write(destUncompressedData[..bytesRead]); bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length); } @@ -1746,7 +1744,7 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpanThe . private bool TryDecompressTextData(ReadOnlySpan compressedData, Encoding encoding, [NotNullWhen(true)] out string? value) { - if (this.TryDecompressZlibData(compressedData, out byte[] uncompressedData)) + if (this.TryDecompressZlibData(compressedData, this.maxUncompressedLength, out byte[] uncompressedData)) { value = encoding.GetString(uncompressedData); return true; diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs index ab6ba4770e..abfa4b1da8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs @@ -15,4 +15,10 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions /// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG. /// public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical; + + /// + /// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed. + /// Defaults to 8MB + /// + public int MaxUncompressedAncillaryChunkSizeBytes { get; init; } = 8 * 1024 * 1024; // 8MB } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index b9f6075fb8..9b165526eb 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -672,4 +672,23 @@ public void Decode_Issue2666() string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Png.Issue2666)); using Image image = Image.Load(path); } + + [Theory] + + [InlineData(TestImages.Png.Bad.BadZTXT)] + [InlineData(TestImages.Png.Bad.BadZTXT2)] + public void Decode_BadZTXT(string file) + { + string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file)); + using Image image = Image.Load(path); + } + + [Theory] + [InlineData(TestImages.Png.Bad.BadZTXT)] + [InlineData(TestImages.Png.Bad.BadZTXT2)] + public void Info_BadZTXT(string file) + { + string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file)); + _ = Image.Identify(path); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e72f6fad8d..00f7370f31 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -186,8 +186,10 @@ public static class Bad // Invalid color type. public const string ColorTypeOne = "Png/xc1n0g08.png"; public const string ColorTypeNine = "Png/xc9n2c08.png"; - public const string FlagOfGermany0000016446 = "Png/issues/flag_of_germany-0000016446.png"; + + public const string BadZTXT = "Png/issues/bad-ztxt.png"; + public const string BadZTXT2 = "Png/issues/bad-ztxt2.png"; } } diff --git a/tests/Images/Input/Png/issues/bad-ztxt.png b/tests/Images/Input/Png/issues/bad-ztxt.png new file mode 100644 index 0000000000..710f888d0b --- /dev/null +++ b/tests/Images/Input/Png/issues/bad-ztxt.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:132a70cf0ac458a55cf4a44f4c6c025587491d304595835959955de6682fa472 +size 3913750 diff --git a/tests/Images/Input/Png/issues/bad-ztxt2.png b/tests/Images/Input/Png/issues/bad-ztxt2.png new file mode 100644 index 0000000000..958c00e3f0 --- /dev/null +++ b/tests/Images/Input/Png/issues/bad-ztxt2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:778a5fc8e915d79e9f55e58c6e4f646ae55dd7e866e65960754cb67a2b445987 +size 93 From 3f22857a80c854791837333e3e5caca38d2d1363 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 1 Mar 2024 23:49:55 +1000 Subject: [PATCH 13/13] Allow nightlies from previous releases --- .github/workflows/build-and-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b375574018..5a2e11266d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - release/* tags: - "v*" pull_request: