Browse Source

Merge branch 'master' into js/buffered-read

pull/1269/head
James Jackson-South 6 years ago
parent
commit
94a3ea5bc6
  1. 11
      src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
  2. 54
      src/ImageSharp/Formats/Png/PngChunkType.cs
  3. 7
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 5
      src/ImageSharp/Formats/Png/PngThrowHelper.cs
  5. 2
      src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
  6. 41
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  7. 42
      src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs
  8. 51
      src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
  9. 45
      src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
  10. 16
      tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
  11. 69
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  12. 27
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
  13. 15
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
  14. 239
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  15. 36
      tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs
  16. 7
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
  17. 23
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
  18. 34
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs
  19. 31
      tests/ImageSharp.Tests/TestImages.cs
  20. BIN
      tests/Images/Input/Png/basn3p01.png
  21. BIN
      tests/Images/Input/Png/basn3p02.png
  22. BIN
      tests/Images/Input/Png/basn3p04.png
  23. BIN
      tests/Images/Input/Png/basn3p08.png
  24. BIN
      tests/Images/Input/Png/issues/Issue_410.png
  25. BIN
      tests/Images/Input/Png/xc1n0g08.png
  26. BIN
      tests/Images/Input/Png/xc9n2c08.png
  27. BIN
      tests/Images/Input/Png/xd0n2c08.png
  28. BIN
      tests/Images/Input/Png/xd3n2c08.png
  29. BIN
      tests/Images/Input/Png/xdtn0g01.png

11
src/ImageSharp/Common/Exceptions/ImageProcessingException.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -10,6 +10,13 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
public sealed class ImageProcessingException : Exception public sealed class ImageProcessingException : Exception
{ {
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
public ImageProcessingException()
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class with the name of the /// Initializes a new instance of the <see cref="ImageProcessingException"/> class with the name of the
/// parameter that causes this exception. /// parameter that causes this exception.
@ -32,4 +39,4 @@ namespace SixLabors.ImageSharp
{ {
} }
} }
} }

54
src/ImageSharp/Formats/Png/PngChunkType.cs

@ -73,6 +73,58 @@ namespace SixLabors.ImageSharp.Formats.Png
/// either alpha values associated with palette entries (for indexed-color images) /// either alpha values associated with palette entries (for indexed-color images)
/// or a single transparent color (for grayscale and true color images). /// or a single transparent color (for grayscale and true color images).
/// </summary> /// </summary>
Transparency = 0x74524E53U Transparency = 0x74524E53U,
/// <summary>
/// The tIME chunk gives the time of the last image modification (not the time of initial image creation).
/// </summary>
Time = 0x74494d45,
/// <summary>
/// The bKGD chunk specifies a default background colour to present the image against.
/// If there is any other preferred background, either user-specified or part of a larger page (as in a browser),
/// the bKGD chunk should be ignored.
/// </summary>
Background = 0x624b4744,
/// <summary>
/// The iCCP chunk contains a embedded color profile. If the iCCP chunk is present,
/// the image samples conform to the colour space represented by the embedded ICC profile as defined by the International Color Consortium.
/// </summary>
EmbeddedColorProfile = 0x69434350,
/// <summary>
/// The sBIT chunk defines the original number of significant bits (which can be less than or equal to the sample depth).
/// This allows PNG decoders to recover the original data losslessly even if the data had a sample depth not directly supported by PNG.
/// </summary>
SignificantBits = 0x73424954,
/// <summary>
/// If the sRGB chunk is present, the image samples conform to the sRGB colour space [IEC 61966-2-1] and should be displayed
/// using the specified rendering intent defined by the International Color Consortium.
/// </summary>
StandardRgbColourSpace = 0x73524742,
/// <summary>
/// The hIST chunk gives the approximate usage frequency of each colour in the palette.
/// </summary>
Histogram = 0x68495354,
/// <summary>
/// The sPLT chunk contains the suggested palette.
/// </summary>
SuggestedPalette = 0x73504c54,
/// <summary>
/// The cHRM chunk may be used to specify the 1931 CIE x,y chromaticities of the red,
/// green, and blue display primaries used in the image, and the referenced white point.
/// </summary>
Chroma = 0x6348524d,
/// <summary>
/// Malformed chunk named CgBI produced by apple, which is not conform to the specification.
/// Related issue is here https://github.com/SixLabors/ImageSharp/issues/410
/// </summary>
ProprietaryApple = 0x43674249
} }
} }

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

@ -9,7 +9,7 @@ using System.IO.Compression;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Png.Zlib;
@ -215,6 +215,9 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngChunkType.End: case PngChunkType.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
break; break;
case PngChunkType.ProprietaryApple:
PngThrowHelper.ThrowInvalidChunkType("Proprietary Apple PNG detected! This PNG file is not conform to the specification and cannot be decoded.");
break;
} }
} }
finally finally
@ -1039,7 +1042,7 @@ namespace SixLabors.ImageSharp.Formats.Png
var uncompressedBytes = new List<byte>(); var uncompressedBytes = new List<byte>();
// Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here. // Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length); int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
while (bytesRead != 0) while (bytesRead != 0)
{ {

5
src/ImageSharp/Formats/Png/PngThrowHelper.cs

@ -20,11 +20,14 @@ namespace SixLabors.ImageSharp.Formats.Png
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data."); public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType(string message) => throw new InvalidImageContentException(message);
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotSupportedColor() => new NotSupportedException("Unsupported PNG color type"); public static void ThrowNotSupportedColor() => throw new NotSupportedException("Unsupported PNG color type");
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowUnknownFilter() => throw new InvalidImageContentException("Unknown filter type."); public static void ThrowUnknownFilter() => throw new InvalidImageContentException("Unknown filter type.");

2
src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs

@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
public IptcProfile DeepClone() => new IptcProfile(this); public IptcProfile DeepClone() => new IptcProfile(this);
/// <summary> /// <summary>
/// Returns all value with the specified tag. /// Returns all values with the specified tag.
/// </summary> /// </summary>
/// <param name="tag">The tag of the iptc value.</param> /// <param name="tag">The tag of the iptc value.</param>
/// <returns>The values found with the specified tag.</returns> /// <returns>The values found with the specified tag.</returns>

41
src/ImageSharp/Processing/AffineTransformBuilder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -247,15 +247,33 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a raw matrix. /// Prepends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to prepend.</param> /// <param name="matrix">The matrix to prepend.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) => this.Prepend(_ => matrix); public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
{
CheckDegenerate(matrix);
return this.Prepend(_ => matrix);
}
/// <summary> /// <summary>
/// Appends a raw matrix. /// Appends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to append.</param> /// <param name="matrix">The matrix to append.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) => this.Append(_ => matrix); public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
{
CheckDegenerate(matrix);
return this.Append(_ => matrix);
}
/// <summary> /// <summary>
/// Returns the combined matrix for a given source size. /// Returns the combined matrix for a given source size.
@ -268,6 +286,11 @@ namespace SixLabors.ImageSharp.Processing
/// Returns the combined matrix for a given source rectangle. /// Returns the combined matrix for a given source rectangle.
/// </summary> /// </summary>
/// <param name="sourceRectangle">The rectangle in the source image.</param> /// <param name="sourceRectangle">The rectangle in the source image.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="Matrix3x2"/>.</returns> /// <returns>The <see cref="Matrix3x2"/>.</returns>
public Matrix3x2 BuildMatrix(Rectangle sourceRectangle) public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
{ {
@ -284,9 +307,19 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size); matrix *= factory(size);
} }
CheckDegenerate(matrix);
return matrix; return matrix;
} }
private static void CheckDegenerate(Matrix3x2 matrix)
{
if (TransformUtilities.IsDegenerate(matrix))
{
throw new DegenerateTransformException("Matrix is degenerate. Check input values.");
}
}
private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> factory) private AffineTransformBuilder Prepend(Func<Size, Matrix3x2> factory)
{ {
this.matrixFactories.Insert(0, factory); this.matrixFactories.Insert(0, factory);
@ -299,4 +332,4 @@ namespace SixLabors.ImageSharp.Processing
return this; return this;
} }
} }
} }

42
src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs

@ -0,0 +1,42 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Represents an error that occurs during a transform operation.
/// </summary>
public sealed class DegenerateTransformException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException"/> class.
/// </summary>
public DegenerateTransformException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException" /> class
/// with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public DegenerateTransformException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DegenerateTransformException" /> class
/// with a specified error message and a reference to the inner exception that is
/// the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public DegenerateTransformException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

51
src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs

@ -12,6 +12,57 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
internal static class TransformUtilities internal static class TransformUtilities
{ {
/// <summary>
/// Returns a value that indicates whether the specified matrix is degenerate
/// containing one or more values equivalent to <see cref="float.NaN"/> or a
/// zero determinant and therefore cannot be used for linear transforms.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
public static bool IsDegenerate(Matrix3x2 matrix)
=> IsNaN(matrix) || IsZero(matrix.GetDeterminant());
/// <summary>
/// Returns a value that indicates whether the specified matrix is degenerate
/// containing one or more values equivalent to <see cref="float.NaN"/> or a
/// zero determinant and therefore cannot be used for linear transforms.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
public static bool IsDegenerate(Matrix4x4 matrix)
=> IsNaN(matrix) || IsZero(matrix.GetDeterminant());
[MethodImpl(InliningOptions.ShortMethod)]
private static bool IsZero(float a)
=> a > -Constants.EpsilonSquared && a < Constants.EpsilonSquared;
/// <summary>
/// Returns a value that indicates whether the specified matrix contains any values
/// that are not a number <see cref="float.NaN"/>.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
/// <returns>The <see cref="bool"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsNaN(Matrix3x2 matrix)
{
return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12)
|| float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22)
|| float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32);
}
/// <summary>
/// Returns a value that indicates whether the specified matrix contains any values
/// that are not a number <see cref="float.NaN"/>.
/// </summary>
/// <param name="matrix">The transform matrix.</param>
/// <returns>The <see cref="bool"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsNaN(Matrix4x4 matrix)
{
return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14)
|| float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24)
|| float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34)
|| float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44);
}
/// <summary> /// <summary>
/// Applies the projective transform against the given coordinates flattened into the 2D space. /// Applies the projective transform against the given coordinates flattened into the 2D space.
/// </summary> /// </summary>

45
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -1,9 +1,10 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
@ -263,27 +264,51 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a raw matrix. /// Prepends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to prepend.</param> /// <param name="matrix">The matrix to prepend.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) => this.Prepend(_ => matrix); public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix)
{
CheckDegenerate(matrix);
return this.Prepend(_ => matrix);
}
/// <summary> /// <summary>
/// Appends a raw matrix. /// Appends a raw matrix.
/// </summary> /// </summary>
/// <param name="matrix">The matrix to append.</param> /// <param name="matrix">The matrix to append.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) => this.Append(_ => matrix); public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix)
{
CheckDegenerate(matrix);
return this.Append(_ => matrix);
}
/// <summary> /// <summary>
/// Returns the combined matrix for a given source size. /// Returns the combined matrix for a given source size.
/// </summary> /// </summary>
/// <param name="sourceSize">The source image size.</param> /// <param name="sourceSize">The source image size.</param>
/// <returns>The <see cref="Matrix4x4"/>.</returns> /// <returns>The <see cref="Matrix4x4"/>.</returns>
public Matrix4x4 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); public Matrix4x4 BuildMatrix(Size sourceSize)
=> this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
/// <summary> /// <summary>
/// Returns the combined matrix for a given source rectangle. /// Returns the combined matrix for a given source rectangle.
/// </summary> /// </summary>
/// <param name="sourceRectangle">The rectangle in the source image.</param> /// <param name="sourceRectangle">The rectangle in the source image.</param>
/// <exception cref="DegenerateTransformException">
/// The resultant matrix is degenerate containing one or more values equivalent
/// to <see cref="float.NaN"/> or a zero determinant and therefore cannot be used
/// for linear transforms.
/// </exception>
/// <returns>The <see cref="Matrix4x4"/>.</returns> /// <returns>The <see cref="Matrix4x4"/>.</returns>
public Matrix4x4 BuildMatrix(Rectangle sourceRectangle) public Matrix4x4 BuildMatrix(Rectangle sourceRectangle)
{ {
@ -300,9 +325,19 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size); matrix *= factory(size);
} }
CheckDegenerate(matrix);
return matrix; return matrix;
} }
private static void CheckDegenerate(Matrix4x4 matrix)
{
if (TransformUtilities.IsDegenerate(matrix))
{
throw new DegenerateTransformException("Matrix is degenerate. Check input values.");
}
}
private ProjectiveTransformBuilder Prepend(Func<Size, Matrix4x4> factory) private ProjectiveTransformBuilder Prepend(Func<Size, Matrix4x4> factory)
{ {
this.matrixFactories.Insert(0, factory); this.matrixFactories.Insert(0, factory);
@ -315,4 +350,4 @@ namespace SixLabors.ImageSharp.Processing
return this; return this;
} }
} }
} }

16
tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs

@ -1,17 +1,10 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using Moq;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit; using Xunit;
@ -19,7 +12,6 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
{ {
[Fact] [Fact]
public void DrawImage_OpacityOnly_VerifyGraphicOptionsTakenFromContext() public void DrawImage_OpacityOnly_VerifyGraphicOptionsTakenFromContext()
{ {
@ -28,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, 0.5f); this.operations.DrawImage(null, 0.5f);
var dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode); Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
@ -43,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, PixelColorBlendingMode.Multiply, 0.5f); this.operations.DrawImage(null, PixelColorBlendingMode.Multiply, 0.5f);
var dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode); Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
@ -58,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, Point.Empty, 0.5f); this.operations.DrawImage(null, Point.Empty, 0.5f);
var dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode); Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
@ -73,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f); this.operations.DrawImage(null, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f);
var dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode); Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);

69
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -1,10 +1,14 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -215,5 +219,70 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
} }
} }
[Fact]
public void Encode_PreservesIptcProfile()
{
// arrange
using var input = new Image<Rgba32>(1, 1);
input.Metadata.IptcProfile = new IptcProfile();
input.Metadata.IptcProfile.SetValue(IptcTag.Byline, "unit_test");
var encoder = new JpegEncoder();
// act
using var memStream = new MemoryStream();
input.Save(memStream, encoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
IptcProfile actual = output.Metadata.IptcProfile;
Assert.NotNull(actual);
IEnumerable<IptcValue> values = input.Metadata.IptcProfile.Values;
Assert.Equal(values, actual.Values);
}
[Fact]
public void Encode_PreservesExifProfile()
{
// arrange
using var input = new Image<Rgba32>(1, 1);
input.Metadata.ExifProfile = new ExifProfile();
input.Metadata.ExifProfile.SetValue(ExifTag.Software, "unit_test");
var encoder = new JpegEncoder();
// act
using var memStream = new MemoryStream();
input.Save(memStream, encoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
ExifProfile actual = output.Metadata.ExifProfile;
Assert.NotNull(actual);
IReadOnlyList<IExifValue> values = input.Metadata.ExifProfile.Values;
Assert.Equal(values, actual.Values);
}
[Fact]
public void Encode_PreservesIccProfile()
{
// arrange
using var input = new Image<Rgba32>(1, 1);
input.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.Profile_Random_Array);
var encoder = new JpegEncoder();
// act
using var memStream = new MemoryStream();
input.Save(memStream, encoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
IccProfile actual = output.Metadata.IccProfile;
Assert.NotNull(actual);
IccProfile values = input.Metadata.IccProfile;
Assert.Equal(values.Entries, actual.Entries);
}
} }
} }

27
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -13,15 +13,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact] [Fact]
public void ChunkTypeIdsAreCorrect() public void ChunkTypeIdsAreCorrect()
{ {
Assert.Equal(PngChunkType.Header, GetType("IHDR")); Assert.Equal(PngChunkType.Header, GetType("IHDR"));
Assert.Equal(PngChunkType.Palette, GetType("PLTE")); Assert.Equal(PngChunkType.Palette, GetType("PLTE"));
Assert.Equal(PngChunkType.Data, GetType("IDAT")); Assert.Equal(PngChunkType.Data, GetType("IDAT"));
Assert.Equal(PngChunkType.End, GetType("IEND")); Assert.Equal(PngChunkType.End, GetType("IEND"));
Assert.Equal(PngChunkType.Transparency, GetType("tRNS")); Assert.Equal(PngChunkType.Transparency, GetType("tRNS"));
Assert.Equal(PngChunkType.Text, GetType("tEXt")); Assert.Equal(PngChunkType.Text, GetType("tEXt"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA")); Assert.Equal(PngChunkType.InternationalText, GetType("iTXt"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs")); Assert.Equal(PngChunkType.CompressedText, GetType("zTXt"));
Assert.Equal(PngChunkType.Exif, GetType("eXIf")); Assert.Equal(PngChunkType.Chroma, GetType("cHRM"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
Assert.Equal(PngChunkType.Exif, GetType("eXIf"));
Assert.Equal(PngChunkType.Time, GetType("tIME"));
Assert.Equal(PngChunkType.Background, GetType("bKGD"));
Assert.Equal(PngChunkType.EmbeddedColorProfile, GetType("iCCP"));
Assert.Equal(PngChunkType.StandardRgbColourSpace, GetType("sRGB"));
Assert.Equal(PngChunkType.SignificantBits, GetType("sBIT"));
Assert.Equal(PngChunkType.Histogram, GetType("hIST"));
Assert.Equal(PngChunkType.SuggestedPalette, GetType("sPLT"));
Assert.Equal(PngChunkType.ProprietaryApple, GetType("CgBI"));
} }
private static PngChunkType GetType(string text) private static PngChunkType GetType(string text)

15
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs

@ -6,6 +6,7 @@ using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Xunit; using Xunit;
@ -78,6 +79,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
} }
[Fact]
public void CalculateCrc_Works()
{
// arrange
var data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
var crc = new Crc32();
// act
crc.Update(data);
// assert
Assert.Equal(0x88AA689F, crc.Value);
}
private static string GetChunkTypeName(uint value) private static string GetChunkTypeName(uint value)
{ {
var data = new byte[4]; var data = new byte[4];

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

@ -4,7 +4,6 @@
using System.IO; using System.IO;
using Microsoft.DotNet.RemoteExecutor; using Microsoft.DotNet.RemoteExecutor;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -26,62 +25,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
public static readonly string[] CommonTestImages = public static readonly string[] CommonTestImages =
{ {
TestImages.Png.Splash, TestImages.Png.Splash,
TestImages.Png.Indexed,
TestImages.Png.FilterVar, TestImages.Png.FilterVar,
TestImages.Png.Bad.ChunkLength1,
TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.VimImage1, TestImages.Png.VimImage1,
TestImages.Png.VimImage2,
TestImages.Png.VersioningImage1, TestImages.Png.VersioningImage1,
TestImages.Png.VersioningImage2, TestImages.Png.VersioningImage2,
TestImages.Png.SnakeGame, TestImages.Png.SnakeGame,
TestImages.Png.Banner7Adam7InterlaceMode,
TestImages.Png.Banner8Index,
TestImages.Png.Bad.ChunkLength2,
TestImages.Png.VimImage2,
TestImages.Png.Rgb24BppTrans, TestImages.Png.Rgb24BppTrans,
TestImages.Png.GrayA8Bit,
TestImages.Png.Gray1BitTrans,
TestImages.Png.Bad.ZlibOverflow,
TestImages.Png.Bad.ZlibOverflow2,
TestImages.Png.Bad.ZlibZtxtBadHeader,
TestImages.Png.Bad.Issue1047_BadEndChunk
};
public static readonly string[] TestImages48Bpp =
{
TestImages.Png.Rgb48Bpp,
TestImages.Png.Rgb48BppInterlaced
};
public static readonly string[] TestImages64Bpp =
{
TestImages.Png.Rgba64Bpp,
TestImages.Png.Rgb48BppTrans
};
public static readonly string[] TestImagesL16Bit =
{
TestImages.Png.L16Bit,
};
public static readonly string[] TestImagesGrayAlpha16Bit = TestImages.Png.Bad.ChunkLength1,
{ TestImages.Png.Bad.ChunkLength2,
TestImages.Png.GrayAlpha16Bit,
TestImages.Png.GrayTrns16BitInterlaced
}; };
public static readonly string[] TestImagesL8BitInterlaced =
{
TestImages.Png.GrayAlpha1BitInterlaced,
TestImages.Png.GrayAlpha2BitInterlaced,
TestImages.Png.Gray4BitInterlaced,
TestImages.Png.GrayA8BitInterlaced
};
public static readonly string[] TestImagesIssue1014 = public static readonly string[] TestImagesIssue1014 =
{ {
TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2, TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2,
@ -95,6 +53,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
TestImages.Png.Issue1177_2 TestImages.Png.Issue1177_2
}; };
public static readonly string[] CorruptedTestImages =
{
TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.Bad.ZlibOverflow,
TestImages.Png.Bad.ZlibOverflow2,
TestImages.Png.Bad.ZlibZtxtBadHeader,
};
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)]
public void Decode<TPixel>(TestImageProvider<TPixel> provider) public void Decode<TPixel>(TestImageProvider<TPixel> provider)
@ -103,25 +69,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
using (Image<TPixel> image = provider.GetImage(PngDecoder)) using (Image<TPixel> image = provider.GetImage(PngDecoder))
{ {
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
}
// We don't have another x-plat reference decoder that can be compared for this image. [Theory]
if (provider.Utility.SourceFileOrDescription == TestImages.Png.Bad.Issue1047_BadEndChunk) [WithFile(TestImages.Png.GrayA8Bit, PixelTypes.Rgba32)]
{ [WithFile(TestImages.Png.Gray1BitTrans, PixelTypes.Rgba32)]
if (TestEnvironment.IsWindows) public void Decode_GrayWithAlpha<TPixel>(TestImageProvider<TPixel> provider)
{ where TPixel : unmanaged, IPixel<TPixel>
image.CompareToOriginal(provider, ImageComparer.Exact, (IImageDecoder)SystemDrawingReferenceDecoder.Instance); {
} using (Image<TPixel> image = provider.GetImage(PngDecoder))
} {
else image.DebugSave(provider);
{ image.CompareToOriginal(provider, ImageComparer.Exact);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
} }
} }
[Theory] [Theory]
[WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)]
public void Decode_Interlaced_ImageIsCorrect<TPixel>(TestImageProvider<TPixel> provider) [WithFile(TestImages.Png.Banner7Adam7InterlaceMode, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Banner8Index, PixelTypes.Rgba32)]
public void Decode_Interlaced<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(PngDecoder)) using (Image<TPixel> image = provider.GetImage(PngDecoder))
@ -132,7 +101,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgb48)] [WithFile(TestImages.Png.Indexed, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Banner8Index, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.PalettedTwoColor, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.PalettedFourColor, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.PalettedSixteenColor, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Paletted256Colors, PixelTypes.Rgba32)]
public void Decode_Indexed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
}
[Theory]
[WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgb48)]
[WithFile(TestImages.Png.Rgb48BppInterlaced, PixelTypes.Rgb48)]
public void Decode_48Bpp<TPixel>(TestImageProvider<TPixel> provider) public void Decode_48Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -144,7 +131,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestImages64Bpp), PixelTypes.Rgba64)] [WithFile(TestImages.Png.Rgba64Bpp, PixelTypes.Rgba64)]
[WithFile(TestImages.Png.Rgb48BppTrans, PixelTypes.Rgba64)]
public void Decode_64Bpp<TPixel>(TestImageProvider<TPixel> provider) public void Decode_64Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -156,7 +144,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesL8BitInterlaced), PixelTypes.Rgba32)] [WithFile(TestImages.Png.GrayAlpha1BitInterlaced, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.GrayAlpha2BitInterlaced, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Gray4BitInterlaced, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.GrayA8BitInterlaced, PixelTypes.Rgba32)]
public void Decoder_L8bitInterlaced<TPixel>(TestImageProvider<TPixel> provider) public void Decoder_L8bitInterlaced<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -168,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesL16Bit), PixelTypes.Rgb48)] [WithFile(TestImages.Png.L16Bit, PixelTypes.Rgb48)]
public void Decode_L16Bit<TPixel>(TestImageProvider<TPixel> provider) public void Decode_L16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -180,7 +171,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesGrayAlpha16Bit), PixelTypes.Rgba64)] [WithFile(TestImages.Png.GrayAlpha16Bit, PixelTypes.Rgba64)]
[WithFile(TestImages.Png.GrayTrns16BitInterlaced, PixelTypes.Rgba64)]
public void Decode_GrayAlpha16Bit<TPixel>(TestImageProvider<TPixel> provider) public void Decode_GrayAlpha16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -193,7 +185,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Theory] [Theory]
[WithFile(TestImages.Png.GrayA8BitInterlaced, PixelTypes)] [WithFile(TestImages.Png.GrayA8BitInterlaced, PixelTypes)]
public void Decoder_CanDecodeGrey8bitWithAlpha<TPixel>(TestImageProvider<TPixel> provider) public void Decoder_CanDecode_Grey8bitInterlaced_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
}
[Theory]
[WithFileCollection(nameof(CorruptedTestImages), PixelTypes.Rgba32)]
public void Decoder_CanDecode_CorruptedImages<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(PngDecoder)) using (Image<TPixel> image = provider.GetImage(PngDecoder))
@ -232,9 +236,63 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
} }
} }
[Theory]
[WithFile(TestImages.Png.Bad.MissingDataChunk, PixelTypes.Rgba32)]
public void Decode_MissingDataChunk_ThrowsException<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
System.Exception ex = Record.Exception(
() =>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
}
});
Assert.NotNull(ex);
Assert.Contains("PNG Image does not contain a data chunk", ex.Message);
}
[Theory]
[WithFile(TestImages.Png.Bad.BitDepthZero, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Bad.BitDepthThree, PixelTypes.Rgba32)]
public void Decode_InvalidBitDepth_ThrowsException<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
System.Exception ex = Record.Exception(
() =>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
}
});
Assert.NotNull(ex);
Assert.Contains("Invalid or unsupported bit depth", ex.Message);
}
[Theory]
[WithFile(TestImages.Png.Bad.ColorTypeOne, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Bad.ColorTypeNine, PixelTypes.Rgba32)]
public void Decode_InvalidColorType_ThrowsException<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
System.Exception ex = Record.Exception(
() =>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
}
});
Assert.NotNull(ex);
Assert.Contains("Invalid or unsupported color type", ex.Message);
}
// https://github.com/SixLabors/ImageSharp/issues/1014
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)]
public void Issue1014<TPixel>(TestImageProvider<TPixel> provider) public void Issue1014_DataSplitOverMultipleIDatChunks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
System.Exception ex = Record.Exception( System.Exception ex = Record.Exception(
@ -249,9 +307,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Assert.Null(ex); Assert.Null(ex);
} }
// https://github.com/SixLabors/ImageSharp/issues/1177
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesIssue1177), PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImagesIssue1177), PixelTypes.Rgba32)]
public void Issue1177<TPixel>(TestImageProvider<TPixel> provider) public void Issue1177_CRC_Omitted<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
System.Exception ex = Record.Exception( System.Exception ex = Record.Exception(
@ -266,6 +325,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Assert.Null(ex); Assert.Null(ex);
} }
// https://github.com/SixLabors/ImageSharp/issues/1127
[Theory] [Theory]
[WithFile(TestImages.Png.Issue1127, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Issue1127, PixelTypes.Rgba32)]
public void Issue1127<TPixel>(TestImageProvider<TPixel> provider) public void Issue1127<TPixel>(TestImageProvider<TPixel> provider)
@ -283,6 +343,53 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Assert.Null(ex); Assert.Null(ex);
} }
// https://github.com/SixLabors/ImageSharp/issues/1047
[Theory]
[WithFile(TestImages.Png.Bad.Issue1047_BadEndChunk, PixelTypes.Rgba32)]
public void Issue1047<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
System.Exception ex = Record.Exception(
() =>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
// We don't have another x-plat reference decoder that can be compared for this image.
if (TestEnvironment.IsWindows)
{
image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Instance);
}
}
});
Assert.Null(ex);
}
// https://github.com/SixLabors/ImageSharp/issues/410
[Theory]
[WithFile(TestImages.Png.Bad.Issue410_MalformedApplePng, PixelTypes.Rgba32)]
public void Issue410_MalformedApplePng<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
System.Exception ex = Record.Exception(
() =>
{
using (Image<TPixel> image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
// We don't have another x-plat reference decoder that can be compared for this image.
if (TestEnvironment.IsWindows)
{
image.CompareToOriginal(provider, ImageComparer.Exact, SystemDrawingReferenceDecoder.Instance);
}
}
});
Assert.NotNull(ex);
Assert.Contains("Proprietary Apple PNG detected!", ex.Message);
}
[Theory] [Theory]
[WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Bike, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Bike, PixelTypes.Rgba32)]

36
tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

@ -25,8 +25,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
} }
[Theory] [Theory]
[MemberData("AllIptcTags")] [MemberData(nameof(AllIptcTags))]
public void IptcProfile_SetValue_WithStrictOption_Works(IptcTag tag) public void IptcProfile_SetValue_WithStrictEnabled_Works(IptcTag tag)
{ {
// arrange // arrange
var profile = new IptcProfile(); var profile = new IptcProfile();
@ -41,6 +41,23 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
Assert.Equal(expectedLength, actual.Value.Length); Assert.Equal(expectedLength, actual.Value.Length);
} }
[Theory]
[MemberData(nameof(AllIptcTags))]
public void IptcProfile_SetValue_WithStrictDisabled_Works(IptcTag tag)
{
// arrange
var profile = new IptcProfile();
var value = new string('s', tag.MaxLength() + 1);
var expectedLength = value.Length;
// act
profile.SetValue(tag, value, false);
// assert
IptcValue actual = profile.GetValues(tag).First();
Assert.Equal(expectedLength, actual.Value.Length);
}
[Theory] [Theory]
[InlineData(IptcTag.DigitalCreationDate)] [InlineData(IptcTag.DigitalCreationDate)]
[InlineData(IptcTag.ExpirationDate)] [InlineData(IptcTag.ExpirationDate)]
@ -199,13 +216,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption); ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption);
} }
[Fact]
public void IptcProfile_SetNewValue_RespectsMaxLength()
{
// arrange
var profile = new IptcProfile();
}
[Theory] [Theory]
[InlineData(IptcTag.ObjectAttribute)] [InlineData(IptcTag.ObjectAttribute)]
[InlineData(IptcTag.SubjectReference)] [InlineData(IptcTag.SubjectReference)]
@ -292,7 +302,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
[Fact] [Fact]
public void IptcProfile_RemoveByTag_RemovesAllEntrys() public void IptcProfile_RemoveByTag_RemovesAllEntrys()
{ {
// arange // arrange
var profile = new IptcProfile(); var profile = new IptcProfile();
profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test");
profile.SetValue(IptcTag.Byline, "test2"); profile.SetValue(IptcTag.Byline, "test2");
@ -308,7 +318,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
[Fact] [Fact]
public void IptcProfile_RemoveByTagAndValue_Works() public void IptcProfile_RemoveByTagAndValue_Works()
{ {
// arange // arrange
var profile = new IptcProfile(); var profile = new IptcProfile();
profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test");
profile.SetValue(IptcTag.Byline, "test2"); profile.SetValue(IptcTag.Byline, "test2");
@ -322,9 +332,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
} }
[Fact] [Fact]
public void IptcProfile_GetValue_RetrievesAllEntrys() public void IptcProfile_GetValue_RetrievesAllEntries()
{ {
// arange // arrange
var profile = new IptcProfile(); var profile = new IptcProfile();
profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test");
profile.SetValue(IptcTag.Byline, "test2"); profile.SetValue(IptcTag.Byline, "test2");

7
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics; using System.Numerics;
@ -8,7 +8,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder> public class AffineTransformBuilderTests : TransformBuilderTestBase<AffineTransformBuilder>
{ {
protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder(); protected override AffineTransformBuilder CreateBuilder()
=> new AffineTransformBuilder();
protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees) protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees)
=> builder.AppendRotationDegrees(degrees); => builder.AppendRotationDegrees(degrees);
@ -67,4 +68,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
return Vector2.Transform(sourcePoint, matrix); return Vector2.Transform(sourcePoint, matrix);
} }
} }
} }

23
tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
@ -8,20 +9,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder> public class ProjectiveTransformBuilderTests : TransformBuilderTestBase<ProjectiveTransformBuilder>
{ {
protected override ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder(); protected override ProjectiveTransformBuilder CreateBuilder()
=> new ProjectiveTransformBuilder();
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees) => builder.AppendRotationDegrees(degrees); protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees)
=> builder.AppendRotationDegrees(degrees);
protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees, Vector2 origin) => builder.AppendRotationDegrees(degrees, origin); protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees, Vector2 origin)
=> builder.AppendRotationDegrees(degrees, origin);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians); protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians)
=> builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) => builder.AppendRotationRadians(radians, origin); protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin)
=> builder.AppendRotationRadians(radians, origin);
protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY) protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY)
=> builder.AppendSkewDegrees(degreesX, degreesY); => builder.AppendSkewDegrees(degreesX, degreesY);
protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY, Vector2 origin) protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY, Vector2 origin)
=> builder.AppendSkewDegrees(degreesX, degreesY, origin); => builder.AppendSkewDegrees(degreesX, degreesY, origin);
@ -44,7 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected override void PrependSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY, Vector2 origin) protected override void PrependSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY, Vector2 origin)
=> builder.PrependSkewRadians(radiansX, radiansY, origin); => builder.PrependSkewRadians(radiansX, radiansY, origin);
protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate)
=> builder.PrependTranslation(translate);
protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) => protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) =>
builder.PrependRotationRadians(radians, origin); builder.PrependRotationRadians(radians, origin);

34
tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
// These operations should be size-agnostic: // These operations should be size-agnostic:
var size = new Size(123, 321); var size = new Size(123, 321);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
this.AppendScale(builder, new SizeF(scale)); this.AppendScale(builder, new SizeF(scale));
this.AppendTranslation(builder, translate); this.AppendTranslation(builder, translate);
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
// Translate ans scale are size-agnostic: // Translate ans scale are size-agnostic:
var size = new Size(456, 432); var size = new Size(456, 432);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
this.AppendTranslation(builder, translate); this.AppendTranslation(builder, translate);
this.AppendScale(builder, new SizeF(scale)); this.AppendScale(builder, new SizeF(scale));
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
public void LocationOffsetIsPrepended(int locationX, int locationY) public void LocationOffsetIsPrepended(int locationX, int locationY)
{ {
var rectangle = new Rectangle(locationX, locationY, 10, 10); var rectangle = new Rectangle(locationX, locationY, 10, 10);
TBuilder builder = this.CreateBuilder(rectangle); TBuilder builder = this.CreateBuilder();
this.AppendScale(builder, new SizeF(2, 2)); this.AppendScale(builder, new SizeF(2, 2));
@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y) float y)
{ {
var size = new Size(width, height); var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
this.AppendRotationDegrees(builder, degrees); this.AppendRotationDegrees(builder, degrees);
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y) float y)
{ {
var size = new Size(width, height); var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
var centerPoint = new Vector2(cx, cy); var centerPoint = new Vector2(cx, cy);
this.AppendRotationDegrees(builder, degrees, centerPoint); this.AppendRotationDegrees(builder, degrees, centerPoint);
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y) float y)
{ {
var size = new Size(width, height); var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
this.AppendSkewDegrees(builder, degreesX, degreesY); this.AppendSkewDegrees(builder, degreesX, degreesY);
@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
float y) float y)
{ {
var size = new Size(width, height); var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
var centerPoint = new Vector2(cx, cy); var centerPoint = new Vector2(cx, cy);
this.AppendSkewDegrees(builder, degreesX, degreesY, centerPoint); this.AppendSkewDegrees(builder, degreesX, degreesY, centerPoint);
@ -194,8 +194,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
public void AppendPrependOpposite() public void AppendPrependOpposite()
{ {
var rectangle = new Rectangle(-1, -1, 3, 3); var rectangle = new Rectangle(-1, -1, 3, 3);
TBuilder b1 = this.CreateBuilder(rectangle); TBuilder b1 = this.CreateBuilder();
TBuilder b2 = this.CreateBuilder(rectangle); TBuilder b2 = this.CreateBuilder();
const float pi = (float)Math.PI; const float pi = (float)Math.PI;
@ -232,14 +232,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.ThrowsAny<ArgumentOutOfRangeException>( Assert.ThrowsAny<ArgumentOutOfRangeException>(
() => () =>
{ {
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder();
this.Execute(builder, new Rectangle(Point.Empty, size), Vector2.Zero); this.Execute(builder, new Rectangle(Point.Empty, size), Vector2.Zero);
}); });
} }
protected TBuilder CreateBuilder(Size size) => this.CreateBuilder(new Rectangle(Point.Empty, size)); [Fact]
public void ThrowsForInvalidMatrix()
{
Assert.ThrowsAny<DegenerateTransformException>(
() =>
{
TBuilder builder = this.CreateBuilder();
this.AppendSkewDegrees(builder, 45, 45);
this.Execute(builder, new Rectangle(0, 0, 150, 150), Vector2.Zero);
});
}
protected abstract TBuilder CreateBuilder(Rectangle rectangle); protected abstract TBuilder CreateBuilder();
protected abstract void AppendRotationDegrees(TBuilder builder, float degrees); protected abstract void AppendRotationDegrees(TBuilder builder, float degrees);

31
tests/ImageSharp.Tests/TestImages.cs

@ -65,6 +65,12 @@ namespace SixLabors.ImageSharp.Tests
public const string Filter3 = "Png/filter3.png"; public const string Filter3 = "Png/filter3.png";
public const string Filter4 = "Png/filter4.png"; public const string Filter4 = "Png/filter4.png";
// Paletted images also from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string PalettedTwoColor = "Png/basn3p01.png";
public const string PalettedFourColor = "Png/basn3p02.png";
public const string PalettedSixteenColor = "Png/basn3p04.png";
public const string Paletted256Colors = "Png/basn3p08.png";
// Filter changing per scanline // Filter changing per scanline
public const string FilterVar = "Png/filterVar.png"; public const string FilterVar = "Png/filterVar.png";
@ -93,6 +99,8 @@ namespace SixLabors.ImageSharp.Tests
public const string Issue1014_4 = "Png/issues/Issue_1014_4.png"; public const string Issue1014_4 = "Png/issues/Issue_1014_4.png";
public const string Issue1014_5 = "Png/issues/Issue_1014_5.png"; public const string Issue1014_5 = "Png/issues/Issue_1014_5.png";
public const string Issue1014_6 = "Png/issues/Issue_1014_6.png"; public const string Issue1014_6 = "Png/issues/Issue_1014_6.png";
// Issue 1127: https://github.com/SixLabors/ImageSharp/issues/1127
public const string Issue1127 = "Png/issues/Issue_1127.png"; public const string Issue1127 = "Png/issues/Issue_1127.png";
// Issue 1177: https://github.com/SixLabors/ImageSharp/issues/1177 // Issue 1177: https://github.com/SixLabors/ImageSharp/issues/1177
@ -101,14 +109,31 @@ namespace SixLabors.ImageSharp.Tests
public static class Bad public static class Bad
{ {
// Odd chunk lengths public const string MissingDataChunk = "Png/xdtn0g01.png";
public const string ChunkLength1 = "Png/chunklength1.png";
public const string ChunkLength2 = "Png/chunklength2.png";
public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string CorruptedChunk = "Png/big-corrupted-chunk.png";
// Zlib errors.
public const string ZlibOverflow = "Png/zlib-overflow.png"; public const string ZlibOverflow = "Png/zlib-overflow.png";
public const string ZlibOverflow2 = "Png/zlib-overflow2.png"; public const string ZlibOverflow2 = "Png/zlib-overflow2.png";
public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png"; public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png";
// Odd chunk lengths
public const string ChunkLength1 = "Png/chunklength1.png";
public const string ChunkLength2 = "Png/chunklength2.png";
// Issue 1047: https://github.com/SixLabors/ImageSharp/issues/1047
public const string Issue1047_BadEndChunk = "Png/issues/Issue_1047.png"; public const string Issue1047_BadEndChunk = "Png/issues/Issue_1047.png";
// Issue 410: https://github.com/SixLabors/ImageSharp/issues/410
public const string Issue410_MalformedApplePng = "Png/issues/Issue_410.png";
// Bad bit depth.
public const string BitDepthZero = "Png/xd0n2c08.png";
public const string BitDepthThree = "Png/xd3n2c08.png";
// Invalid color type.
public const string ColorTypeOne = "Png/xc1n0g08.png";
public const string ColorTypeNine = "Png/xc9n2c08.png";
} }
public static readonly string[] All = public static readonly string[] All =

BIN
tests/Images/Input/Png/basn3p01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

BIN
tests/Images/Input/Png/basn3p02.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

BIN
tests/Images/Input/Png/basn3p04.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

BIN
tests/Images/Input/Png/basn3p08.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
tests/Images/Input/Png/issues/Issue_410.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

BIN
tests/Images/Input/Png/xc1n0g08.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

BIN
tests/Images/Input/Png/xc9n2c08.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
tests/Images/Input/Png/xd0n2c08.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
tests/Images/Input/Png/xd3n2c08.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
tests/Images/Input/Png/xdtn0g01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Loading…
Cancel
Save