Skip to content

Commit

Permalink
Merge pull request #2690 from SixLabors/js/v3-updates
Browse files Browse the repository at this point in the history
Merge 2681 to v4 Main
  • Loading branch information
JimBobSquarePants authored Mar 7, 2024
2 parents f44b761 + 8bf8698 commit 7e7c795
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 31 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- release/*
tags:
- "v*"
pull_request:
Expand Down
38 changes: 18 additions & 20 deletions src/ImageSharp/Formats/Png/PngDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// </summary>
private readonly Crc32 crc32 = new();

/// <summary>
/// The maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
/// </summary>
private readonly int maxUncompressedLength;

/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
Expand All @@ -138,6 +143,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)
Expand All @@ -149,6 +155,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;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -602,23 +609,7 @@ private static void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan<byte> d
private void InitializeImage<TPixel>(ImageMetadata metadata, FrameControl frameControl, out Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
// 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<TPixel>(
this.configuration,
this.header.Width,
this.header.Height,
metadata);
}
else
{
image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.header.Width,
this.header.Height,
metadata);
}
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);

PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngMetadata();
frameMetadata.FromChunk(in frameControl);
Expand Down Expand Up @@ -1575,7 +1566,7 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan<byte> da

ReadOnlySpan<byte> 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);
}
Expand All @@ -1585,9 +1576,10 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan<byte> da
/// Tries to decompress zlib compressed data.
/// </summary>
/// <param name="compressedData">The compressed data.</param>
/// <param name="maxLength">The maximum uncompressed length.</param>
/// <param name="uncompressedBytesArray">The uncompressed bytes array.</param>
/// <returns>True, if de-compressing was successful.</returns>
private unsafe bool TryDecompressZlibData(ReadOnlySpan<byte> compressedData, out byte[] uncompressedBytesArray)
private unsafe bool TryDecompressZlibData(ReadOnlySpan<byte> compressedData, int maxLength, out byte[] uncompressedBytesArray)
{
fixed (byte* compressedDataBase = compressedData)
{
Expand All @@ -1607,6 +1599,12 @@ private unsafe bool TryDecompressZlibData(ReadOnlySpan<byte> compressedData, out
int bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
while (bytesRead != 0)
{
if (memoryStreamOutput.Length > maxLength)
{
uncompressedBytesArray = Array.Empty<byte>();
return false;
}

memoryStreamOutput.Write(destUncompressedData[..bytesRead]);
bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
}
Expand Down Expand Up @@ -1749,7 +1747,7 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byt
/// <returns>The <see cref="bool"/>.</returns>
private bool TryDecompressTextData(ReadOnlySpan<byte> 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;
Expand Down
6 changes: 6 additions & 0 deletions src/ImageSharp/Formats/Png/PngDecoderOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical;

/// <summary>
/// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
/// Defaults to 8MB
/// </summary>
public int MaxUncompressedAncillaryChunkSizeBytes { get; init; } = 8 * 1024 * 1024; // 8MB
}
21 changes: 11 additions & 10 deletions src/ImageSharp/Formats/Webp/AlphaDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
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.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Memory;
Expand Down Expand Up @@ -311,8 +313,7 @@ private static void ColorIndexInverseTransformAlpha(

private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<byte> dst, int width)
{
// TODO: Investigate AdvSimd support for this method.
if (Sse2.IsSupported && width >= 9)
if ((Sse2.IsSupported || AdvSimd.IsSupported) && width >= 9)
{
dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0]));
nuint i;
Expand All @@ -323,17 +324,17 @@ private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<b
for (i = 1; i <= (uint)width - 8; i += 8)
{
Vector128<long> a0 = Vector128.Create(Unsafe.As<byte, long>(ref Unsafe.Add(ref srcRef, i)), 0);
Vector128<byte> a1 = Sse2.Add(a0.AsByte(), last.AsByte());
Vector128<byte> a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1);
Vector128<byte> a3 = Sse2.Add(a1, a2);
Vector128<byte> a4 = Sse2.ShiftLeftLogical128BitLane(a3, 2);
Vector128<byte> a5 = Sse2.Add(a3, a4);
Vector128<byte> a6 = Sse2.ShiftLeftLogical128BitLane(a5, 4);
Vector128<byte> a7 = Sse2.Add(a5, a6);
Vector128<byte> a1 = a0.AsByte() + last.AsByte();
Vector128<byte> a2 = Vector128Utilities.ShiftLeftBytesInVector(a1, 1);
Vector128<byte> a3 = a1 + a2;
Vector128<byte> a4 = Vector128Utilities.ShiftLeftBytesInVector(a3, 2);
Vector128<byte> a5 = a3 + a4;
Vector128<byte> a6 = Vector128Utilities.ShiftLeftBytesInVector(a5, 4);
Vector128<byte> a7 = a5 + a6;

ref byte outputRef = ref Unsafe.Add(ref dstRef, i);
Unsafe.As<byte, Vector64<byte>>(ref outputRef) = a7.GetLower();
last = Sse2.ShiftRightLogical(a7.AsInt64(), 56).AsInt32();
last = Vector128.ShiftRightLogical(a7.AsInt64(), 56).AsInt32();
}

for (; i < (uint)width; ++i)
Expand Down
19 changes: 19 additions & 0 deletions tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
4 changes: 3 additions & 1 deletion tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
}

Expand Down
3 changes: 3 additions & 0 deletions tests/Images/Input/Png/issues/bad-ztxt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/Images/Input/Png/issues/bad-ztxt2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7e7c795

Please sign in to comment.