Browse Source

Merge branch 'main' into js/jpeg-icc-normalize

pull/2922/head
James Jackson-South 12 months ago
committed by GitHub
parent
commit
818412bd13
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  2. 17
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 3
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  4. 13
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  5. 5
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  6. 3
      src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
  7. 12
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  8. 10
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  9. 4
      tests/ImageSharp.Tests/TestImages.cs
  10. 23
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  11. 3
      tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png
  12. 3
      tests/Images/Input/Png/issues/Issue_2924.png
  13. 3
      tests/Images/Input/Webp/issues/Issue2925.webp

2
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -58,7 +58,7 @@ internal sealed unsafe partial class JpegEncoderCore
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
if (image.Width >= JpegConstants.MaxLength || image.Height >= JpegConstants.MaxLength)
if (image.Width > JpegConstants.MaxLength || image.Height > JpegConstants.MaxLength)
{
JpegThrowHelper.ThrowDimensionsTooLarge(image.Width, image.Height);
}

17
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -131,6 +131,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore
/// </summary>
private readonly int maxUncompressedLength;
/// <summary>
/// A value indicating whether the image data has been read.
/// </summary>
private bool hasImageData;
/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
@ -749,7 +754,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore
where TPixel : unmanaged, IPixel<TPixel>
{
using ZlibInflateStream inflateStream = new(this.currentStream, getData);
inflateStream.AllocateNewBytes(chunkLength, true);
if (!inflateStream.AllocateNewBytes(chunkLength, !this.hasImageData))
{
return;
}
DeflateStream dataStream = inflateStream.CompressedStream!;
if (this.header.InterlaceMethod is PngInterlaceMode.Adam7)
@ -803,7 +812,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
int bytesRead = compressedStream.Read(scanSpan, currentRowBytesRead, bytesPerFrameScanline - currentRowBytesRead);
if (bytesRead <= 0)
{
return;
goto EXIT;
}
currentRowBytesRead += bytesRead;
@ -848,6 +857,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
}
EXIT:
this.hasImageData = true;
blendMemory?.Dispose();
}
@ -906,7 +916,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
int bytesRead = compressedStream.Read(this.scanline.GetSpan(), currentRowBytesRead, bytesPerInterlaceScanline - currentRowBytesRead);
if (bytesRead <= 0)
{
return;
goto EXIT;
}
currentRowBytesRead += bytesRead;
@ -979,6 +989,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
}
EXIT:
this.hasImageData = true;
blendMemory?.Dispose();
}

3
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -371,9 +371,6 @@ internal class Vp8LEncoder : IDisposable
/// <param name="inputImgHeight">The input image height.</param>
private void WriteImageSize(int inputImgWidth, int inputImgHeight)
{
Guard.MustBeLessThan(inputImgWidth, WebpConstants.MaxDimension, nameof(inputImgWidth));
Guard.MustBeLessThan(inputImgHeight, WebpConstants.MaxDimension, nameof(inputImgHeight));
uint width = (uint)inputImgWidth - 1;
uint height = (uint)inputImgHeight - 1;

13
src/ImageSharp/Formats/Webp/WebpDecoderCore.cs

@ -220,7 +220,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
else
{
// Ignore unknown chunks.
uint chunkSize = ReadChunkSize(stream, buffer);
uint chunkSize = ReadChunkSize(stream, buffer, false);
stream.Skip((int)chunkSize);
}
}
@ -498,9 +498,10 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
/// </summary>
/// <param name="stream">The stream to decode from.</param>
/// <param name="buffer">Temporary buffer.</param>
/// <param name="required">If true, the chunk size is required to be read, otherwise it can be skipped.</param>
/// <returns>The chunk size in bytes.</returns>
/// <exception cref="ImageFormatException">Invalid data.</exception>
private static uint ReadChunkSize(BufferedReadStream stream, Span<byte> buffer)
private static uint ReadChunkSize(BufferedReadStream stream, Span<byte> buffer, bool required = true)
{
if (stream.Read(buffer, 0, 4) == 4)
{
@ -508,7 +509,13 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
}
throw new ImageFormatException("Invalid Webp data.");
if (required)
{
throw new ImageFormatException("Invalid Webp data.");
}
// Return the size of the remaining data in the stream.
return (uint)(stream.Length - stream.Position);
}
/// <inheritdoc/>

5
src/ImageSharp/Formats/Webp/WebpEncoderCore.cs

@ -132,6 +132,11 @@ internal sealed class WebpEncoderCore
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
if (image.Width > WebpConstants.MaxDimension || image.Height > WebpConstants.MaxDimension)
{
WebpThrowHelper.ThrowDimensionsTooLarge(image.Width, image.Height);
}
bool lossless;
if (this.fileFormat is not null)
{

3
src/ImageSharp/Formats/Webp/WebpThrowHelper.cs

@ -18,4 +18,7 @@ internal static class WebpThrowHelper
[DoesNotReturn]
public static void ThrowInvalidImageDimensions(string errorMessage) => throw new InvalidImageContentException(errorMessage);
[DoesNotReturn]
public static void ThrowDimensionsTooLarge(int width, int height) => throw new ImageFormatException($"Image is too large to encode at {width}x{height} for WEBP format.");
}

12
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -719,10 +719,20 @@ public partial class PngDecoderTests
[Theory]
[WithFile(TestImages.Png.Issue2752, PixelTypes.Rgba32)]
public void CanDecodeJustOneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { MaxFrames = 1 };
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, options);
Assert.Equal(1, image.Frames.Count);
}
[Theory]
[WithFile(TestImages.Png.Issue2924, PixelTypes.Rgba32)]
public void CanDecode_Issue2924<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToReferenceOutput(provider);
}
}

10
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -549,4 +549,14 @@ public class WebpDecoderTests
Assert.Equal(37.8, meta.VerticalResolution);
Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, meta.ResolutionUnits);
}
[Theory]
[WithFile(Lossy.Issue2925, PixelTypes.Rgba32)]
public void WebpDecoder_CanDecode_Issue2925<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
}

4
tests/ImageSharp.Tests/TestImages.cs

@ -160,6 +160,9 @@ public static class TestImages
// Issue 2752: https://github.com/SixLabors/ImageSharp/issues/2752
public const string Issue2752 = "Png/issues/Issue_2752.png";
// Issue 2924: https://github.com/SixLabors/ImageSharp/issues/2924
public const string Issue2924 = "Png/issues/Issue_2924.png";
public static class Bad
{
public const string MissingDataChunk = "Png/xdtn0g01.png";
@ -871,6 +874,7 @@ public static class TestImages
public const string Issue2763 = "Webp/issues/Issue2763.png";
public const string Issue2801 = "Webp/issues/Issue2801.webp";
public const string Issue2866 = "Webp/issues/Issue2866.webp";
public const string Issue2925 = "Webp/issues/Issue2925.webp";
}
public const string AlphaBlend = "Webp/alpha-blend.webp";

23
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -59,11 +59,6 @@ public static class TestImageExtensions
bool appendSourceFileOrDescription = true,
IImageEncoder encoder = null)
{
if (TestEnvironment.RunsWithCodeCoverage)
{
return image;
}
provider.Utility.SaveTestOutputFile(
image,
extension,
@ -112,11 +107,6 @@ public static class TestImageExtensions
Func<int, int, bool> predicate = null)
where TPixel : unmanaged, IPixel<TPixel>
{
if (TestEnvironment.RunsWithCodeCoverage)
{
return image;
}
provider.Utility.SaveTestOutputFileMultiFrame(
image,
extension,
@ -298,24 +288,23 @@ public static class TestImageExtensions
appendPixelTypeToFileName,
predicate: predicate);
using (Image<TPixel> debugImage = GetDebugOutputImageMultiFrame<TPixel>(
using Image<TPixel> debugImage = GetDebugOutputImageMultiFrame<TPixel>(
provider,
image.Frames.Count,
testOutputDetails,
extension,
appendPixelTypeToFileName,
predicate: predicate))
predicate: predicate);
using (Image<TPixel> referenceImage = GetReferenceOutputImageMultiFrame<TPixel>(
using Image<TPixel> referenceImage = GetReferenceOutputImageMultiFrame<TPixel>(
provider,
image.Frames.Count,
testOutputDetails,
extension,
appendPixelTypeToFileName,
predicate: predicate))
{
comparer.VerifySimilarity(referenceImage, debugImage);
}
predicate: predicate);
comparer.VerifySimilarity(referenceImage, debugImage);
return image;
}

3
tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4347cd89196c09496288724afdd876b227063149bba33615c338ebb474a0cb19
size 47260

3
tests/Images/Input/Png/issues/Issue_2924.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd366a2de522041c706729b01a6030df6c82a4e87ea3509cc78a47486f097044
size 49376

3
tests/Images/Input/Webp/issues/Issue2925.webp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e12207babf122af7a62246938c2e78faa0d3f730edb3182f4f9d6adae6bfc602
size 262144
Loading…
Cancel
Save