From b8fcdda44a3ca0ab32b70cc59fc1d72c2038ff20 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 21 Oct 2024 20:19:10 +1000 Subject: [PATCH] Backport - WEBP : Use Correct Width With AlphaDecoder --- src/ImageSharp/Formats/Webp/AlphaDecoder.cs | 7 ++++--- .../Formats/Webp/Lossless/LosslessUtils.cs | 11 +++++++---- .../Formats/Webp/Lossless/WebpLosslessDecoder.cs | 7 ++++++- .../Formats/WebP/WebpEncoderTests.cs | 15 +++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Webp/issues/Issue2801.webp | 3 +++ 6 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 tests/Images/Input/Webp/issues/Issue2801.webp diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index 63571617fb..ca419e619e 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -181,7 +181,7 @@ public void Decode() else { this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span); - this.ExtractAlphaRows(this.Vp8LDec); + this.ExtractAlphaRows(this.Vp8LDec, this.Width); } } @@ -255,14 +255,15 @@ public void ExtractPalettedAlphaRows(int lastRow) /// Once the image-stream is decoded into ARGB color values, the transparency information will be extracted from the green channel of the ARGB quadruplet. /// /// The VP8L decoder. - private void ExtractAlphaRows(Vp8LDecoder dec) + /// The image width. + private void ExtractAlphaRows(Vp8LDecoder dec, int width) { int numRowsToProcess = dec.Height; - int width = dec.Width; Span input = dec.Pixels.Memory.Span; Span output = this.Alpha.Memory.Span; // Extract alpha (which is stored in the green plane). + // the final width (!= dec->width_) int pixelCount = width * numRowsToProcess; WebpLosslessDecoder.ApplyInverseTransforms(dec, input, this.memoryAllocator); ExtractGreen(input, output, pixelCount); diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index 024adb7c23..5287f0b753 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -269,7 +269,11 @@ private static void SubtractGreenFromBlueAndRedScalar(Span pixelData) /// /// The transform data contains color table size and the entries in the color table. /// The pixel data to apply the reverse transform on. - public static void ColorIndexInverseTransform(Vp8LTransform transform, Span pixelData) + /// The resulting pixel data with the reversed transformation data. + public static void ColorIndexInverseTransform( + Vp8LTransform transform, + Span pixelData, + Span outputSpan) { int bitsPerPixel = 8 >> transform.Bits; int width = transform.XSize; @@ -282,7 +286,6 @@ public static void ColorIndexInverseTransform(Vp8LTransform transform, Span>= bitsPerPixel; } } - decodedPixelData.AsSpan().CopyTo(pixelData); + outputSpan.CopyTo(pixelData); } else { diff --git a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs index e4c2a7ddf6..0f366cb1e7 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs @@ -684,6 +684,7 @@ public static void ApplyInverseTransforms(Vp8LDecoder decoder, Span pixelD List transforms = decoder.Transforms; for (int i = transforms.Count - 1; i >= 0; i--) { + // TODO: Review these 1D allocations. They could conceivably exceed limits. Vp8LTransform transform = transforms[i]; switch (transform.TransformType) { @@ -701,7 +702,11 @@ public static void ApplyInverseTransforms(Vp8LDecoder decoder, Span pixelD LosslessUtils.ColorSpaceInverseTransform(transform, pixelData); break; case Vp8LTransformType.ColorIndexingTransform: - LosslessUtils.ColorIndexInverseTransform(transform, pixelData); + using (IMemoryOwner output = memoryAllocator.Allocate(pixelData.Length, AllocationOptions.Clean)) + { + LosslessUtils.ColorIndexInverseTransform(transform, pixelData, output.GetSpan()); + } + break; } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index d1d83ffb9a..031a9ba059 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -514,6 +514,21 @@ public void WebpDecoder_CanDecode_Issue2763(TestImageProvider pr image.VerifyEncoder(provider, "webp", string.Empty, encoder); } + // https://github.com/SixLabors/ImageSharp/issues/2801 + [Theory] + [WithFile(Lossy.Issue2801, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2801(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + WebpEncoder encoder = new() + { + Quality = 100 + }; + + using Image image = provider.GetImage(); + image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.TolerantPercentage(0.0994F)); + } + public static void RunEncodeLossy_WithPeakImage() { TestImageProvider provider = TestImageProvider.File(TestImageLossyFullPath); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 847aac3478..a0e951e70d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -824,6 +824,7 @@ public static class Lossy public const string Issue2257 = "Webp/issues/Issue2257.webp"; public const string Issue2670 = "Webp/issues/Issue2670.webp"; public const string Issue2763 = "Webp/issues/Issue2763.png"; + public const string Issue2801 = "Webp/issues/Issue2801.webp"; } } diff --git a/tests/Images/Input/Webp/issues/Issue2801.webp b/tests/Images/Input/Webp/issues/Issue2801.webp new file mode 100644 index 0000000000..a3b5fee6e0 --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2801.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e90a0d853ddf70d823d8da44eb6c57081e955b1fb7f436a1fd88ca5e5c75a003 +size 261212