Browse Source

Merge branch 'master' into tocsoft/mutate-api

af/merge-core
JimBobSquarePants 9 years ago
parent
commit
78a4fa7bbb
  1. 4
      .travis.yml
  2. 20
      README.md
  3. 4
      appveyor.yml
  4. 10
      src/ImageSharp/Configuration.cs
  5. 2
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  6. 4
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  7. 2
      src/ImageSharp/Formats/Gif/GifFormat.cs
  8. 154
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  9. 2
      src/ImageSharp/Formats/Png/PngFormat.cs
  10. 2
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  11. 4
      tests/ImageSharp.Tests/FileTestBase.cs
  12. 89
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  13. 40
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  14. 2
      tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs
  15. 62
      tests/ImageSharp.Tests/Image/ImageEqualTests.cs
  16. 48
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  17. 54
      tests/ImageSharp.Tests/Image/ImageRotationTests.cs
  18. 2
      tests/ImageSharp.Tests/Image/ImageSaveTests.cs
  19. 2
      tests/ImageSharp.Tests/Image/ImageTests.cs
  20. 10
      tests/ImageSharp.Tests/TestImages.cs
  21. 3
      tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp-interlaced.png
  22. 3
      tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png
  23. 3
      tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png
  24. 3
      tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png
  25. 3
      tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png

4
.travis.yml

@ -26,7 +26,7 @@ env:
global: global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key # via the "travis encrypt" command using the project repo's public key
- secure: "aim+fUyx7kDQQcAYV1mX16cvyFEYsxiW3y26xjmeuKzsOf6DIUK328pE8KnO50bMWhfVPjuW7jWc43jI+nbUeIW5018aFcjoOrEK2F8JvJ0UKtEo+ONchypJSXA2TSdL0iIlufMBepsmlBsSLkCwHCJYohYcueZV0u9NVPc3n282KLL8ItRZeSFG/cL/a2yrkFnTFhq9OtkUtP4CcVE7BOtzjfftNcn4Rup73e5JkLT7L9AZS7eCYkIYV0KRlT2pOa/FuOHlfP9NP+NVtd83GXUY2FKBsmN3EmrQgGDTfwfwcJjN5dqIqzkIXmLV8IKQ3aiW2//02pIe5VrdqHQG+EVMRcdpCWyKUkMj0g4rGYkqKCtVJojKtOR93ycOGUDc6+cMMoyn3J2qFydkp278dGWeLuwtGfD25fHXorqK1aL9/bGPcwdinrBmcwnuy1IECtuTkEfAPsb6O4nArnDsTEzeQxwa/MAicmpux//TNKgkQGqzCPeHKbl4vOfyyI6kCsf8edWv8fOSPvJUGvL14+/TZ6lY8S+30fosOmwMCe7xlbtcVlBVtOsKx/XUufrP2Vuptlc8INaq6++XtgpCoMLL0SJfBFQKZRmBGavv1Ztyf0aL6Qp303HKGTyXOEq2k18iJmukB6JcnEGVsaAyteGlruQIbPgHWbxhZSoJZPw=" - secure: "rjMvEMN9rpvIXqXqCAAKzbHyABzr7E4wPU/dYJ/mHBqlCccFpQrEXVVM1MfRFXYuWZSaIioknhLATZjT5xvIYpTNM6D57z4OTmqeRHhYm80="
before_install: before_install:
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
@ -34,7 +34,7 @@ before_install:
addons: addons:
coverity_scan: coverity_scan:
project: project:
name: "JimBobSquarePants/ImageSharp" name: "SixLabors/ImageSharp"
description: "Build submitted via Travis CI" description: "Build submitted via Travis CI"
notification_email: james_south@hotmail.com notification_email: james_south@hotmail.com
build_command_prepend: "dotnet restore" build_command_prepend: "dotnet restore"

20
README.md

@ -1,5 +1,5 @@
# <img src="https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-256.png" alt="ImageSharp" width="52"/> ImageSharp # <img src="https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-256.png" alt="ImageSharp" width="52"/> ImageSharp
**ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API designed to allow the processing of images without the use of `System.Drawing`.
@ -9,12 +9,12 @@ Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and emb
> >
> Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp). > Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp).
[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/APACHE-2.0-LICENSE.txt) [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/SixLabors/ImageSharp/master/APACHE-2.0-LICENSE.txt)
[![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues) [![GitHub issues](https://img.shields.io/github/issues/SixLabors/ImageSharp.svg)](https://github.com/SixLabors/ImageSharp/issues)
[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers) [![GitHub stars](https://img.shields.io/github/stars/SixLabors/ImageSharp.svg)](https://github.com/SixLabors/ImageSharp/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network) [![GitHub forks](https://img.shields.io/github/forks/SixLabors/ImageSharp.svg)](https://github.com/SixLabors/ImageSharp/network)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Twitter](https://img.shields.io/twitter/url/https/github.com/JimBobSquarePants/ImageSharp.svg?style=social)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fJimBobSquarePants%2fImageSharp&via=james_m_south) [![Twitter](https://img.shields.io/twitter/url/https/github.com/SixLabors/ImageSharp.svg?style=social)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors)
[![OpenCollective](https://opencollective.com/imagesharp/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/imagesharp/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/imagesharp/sponsors/badge.svg)](#sponsors) [![OpenCollective](https://opencollective.com/imagesharp/sponsors/badge.svg)](#sponsors)
@ -22,8 +22,8 @@ Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and emb
| |Build Status|Code Coverage| | |Build Status|Code Coverage|
|-------------|:----------:|:-----------:| |-------------|:----------:|:-----------:|
|**Linux/Mac**|[![Build Status](https://travis-ci.org/JimBobSquarePants/ImageSharp.svg)](https://travis-ci.org/JimBobSquarePants/ImageSharp)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)| |**Linux/Mac**|[![Build Status](https://travis-ci.org/SixLabors/ImageSharp.svg)](https://travis-ci.org/SixLabors/ImageSharp)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)|
|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/hu6d1gdpxdw0q360/branch/master?svg=true)](https://ci.appveyor.com/project/JamesSouth/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)| |**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/m9pn907xdah3ca39/branch/master?svg=true)](https://ci.appveyor.com/project/six-labors/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)|
### Installation ### Installation
@ -64,7 +64,7 @@ Alternatively on Linux you can use:
To clone it locally click the "Clone in Windows" button above or run the following git commands. To clone it locally click the "Clone in Windows" button above or run the following git commands.
```bash ```bash
git clone https://github.com/JimBobSquarePants/ImageSharp git clone https://github.com/SixLabors/ImageSharp
``` ```
### Features ### Features
@ -121,7 +121,7 @@ For optimized access within a loop it is recommended that the following methods
1. `image.GetRowSpan(y)` 1. `image.GetRowSpan(y)`
2. `image.GetRowSpan(x, y)` 2. `image.GetRowSpan(x, y)`
For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/JimBobSquarePants/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame. For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/SixLabors/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame.
All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start. All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start.

4
appveyor.yml

@ -7,10 +7,6 @@ skip_branch_with_pr: true
init: init:
- ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps')) - ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps'))
# temp work around - https://appveyor.statuspage.io/incidents/m2vdvw39kdk8
hosts:
api.nuget.org: 93.184.221.200
build_script: build_script:
- cmd: build.cmd - cmd: build.cmd

10
src/ImageSharp/Configuration.cs

@ -35,14 +35,14 @@ namespace ImageSharp
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>(); private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s. /// The list of supported <see cref="IImageFormat"/>s.
/// </summary> /// </summary>
private readonly List<IImageFormatDetector> imageFormatDetectors = new List<IImageFormatDetector>(); private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormat"/>s. /// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary> /// </summary>
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>(); private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
@ -186,7 +186,7 @@ namespace ImageSharp
/// </summary> /// </summary>
public void ClearImageFormatDetectors() public void ClearImageFormatDetectors()
{ {
this.imageFormatDetectors.Clear(); this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/Bmp/BmpFormat.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary> /// </summary>
internal sealed class BmpFormat : IImageFormat internal sealed class BmpFormat : IImageFormat
{ {

4
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -157,6 +157,10 @@ namespace ImageSharp.Formats
} }
nextFlag = stream.ReadByte(); nextFlag = stream.ReadByte();
if (nextFlag == -1)
{
break;
}
} }
} }
finally finally

2
src/ImageSharp/Formats/Gif/GifFormat.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary> /// </summary>
internal sealed class GifFormat : IImageFormat internal sealed class GifFormat : IImageFormat
{ {

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

@ -267,45 +267,48 @@ namespace ImageSharp.Formats
/// <param name="source">The bytes to convert from. Cannot be null.</param> /// <param name="source">The bytes to convert from. Cannot be null.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bits">The number of bits per value.</param> /// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns> /// <returns>The resulting <see cref="Span{Byte}"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception> /// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception> /// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
private static byte[] ToArrayByBitsLength(byte[] source, int bytesPerScanline, int bits) private static Span<byte> ToArrayByBitsLength(Span<byte> source, int bytesPerScanline, int bits)
{ {
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThan(bits, 0, nameof(bits)); Guard.MustBeGreaterThan(bits, 0, nameof(bits));
byte[] result; if (bits >= 8)
if (bits < 8)
{ {
result = new byte[bytesPerScanline * 8 / bits]; return source;
int mask = 0xFF >> (8 - bits); }
int resultOffset = 0;
// ReSharper disable once ForCanBeConvertedToForeach byte[] result = new byte[bytesPerScanline * 8 / bits];
// First byte is the marker so skip. int mask = 0xFF >> (8 - bits);
for (int i = 1; i < bytesPerScanline; i++) int resultOffset = 0;
for (int i = 0; i < bytesPerScanline; i++)
{
byte b = source[i];
for (int shift = 0; shift < 8; shift += bits)
{ {
byte b = source[i]; int colorIndex = (b >> (8 - bits - shift)) & mask;
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex; result[resultOffset] = (byte)colorIndex;
resultOffset++; resultOffset++;
}
} }
} }
else
{
result = source;
}
return result; return result;
} }
private static bool IsCriticalChunk(PngChunk chunk)
{
return
chunk.Type == PngChunkTypes.Header ||
chunk.Type == PngChunkTypes.Palette ||
chunk.Type == PngChunkTypes.Data ||
chunk.Type == PngChunkTypes.End;
}
/// <summary> /// <summary>
/// Reads the data chunk containing physical dimension data. /// Reads the data chunk containing physical dimension data.
/// </summary> /// </summary>
@ -575,13 +578,15 @@ namespace ImageSharp.Formats
{ {
var color = default(TPixel); var color = default(TPixel);
Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1); var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.pngColorType) switch (this.pngColorType)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); Span<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
byte intensity = (byte)(newScanline1[x] * factor); byte intensity = (byte)(newScanline1[x] * factor);
@ -595,10 +600,10 @@ namespace ImageSharp.Formats
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
int offset = 1 + (x * this.bytesPerPixel); int offset = x * this.bytesPerPixel;
byte intensity = defilteredScanline[offset]; byte intensity = scanlineBuffer[offset];
byte alpha = defilteredScanline[offset + this.bytesPerSample]; byte alpha = scanlineBuffer[offset + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha)); color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color; rowSpan[x] = color;
@ -608,7 +613,7 @@ namespace ImageSharp.Formats
case PngColorType.Palette: case PngColorType.Palette:
this.ProcessScanlineFromPalette(defilteredScanline, rowSpan); this.ProcessScanlineFromPalette(scanlineBuffer, rowSpan);
break; break;
@ -673,10 +678,10 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam> /// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param> /// <param name="defilteredScanline">The scanline</param>
/// <param name="row">Thecurrent output image row</param> /// <param name="row">Thecurrent output image row</param>
private void ProcessScanlineFromPalette<TPixel>(byte[] defilteredScanline, Span<TPixel> row) private void ProcessScanlineFromPalette<TPixel>(Span<byte> defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); Span<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] pal = this.palette; byte[] pal = this.palette;
var color = default(TPixel); var color = default(TPixel);
@ -688,19 +693,11 @@ namespace ImageSharp.Formats
// channel and we should try to read it. // channel and we should try to read it.
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
int index = newScanline[x + 1]; int index = newScanline[x];
int pixelOffset = index * 3; int pixelOffset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = pal.GetRgb24(pixelOffset);
if (rgba.A > 0)
{
rgba.Rgb = pal.GetRgb24(pixelOffset);
}
else
{
rgba = default(Rgba32);
}
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
row[x] = color; row[x] = color;
@ -712,7 +709,7 @@ namespace ImageSharp.Formats
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
int index = newScanline[x + 1]; int index = newScanline[x];
int pixelOffset = index * 3; int pixelOffset = index * 3;
rgba.Rgb = pal.GetRgb24(pixelOffset); rgba.Rgb = pal.GetRgb24(pixelOffset);
@ -736,15 +733,15 @@ namespace ImageSharp.Formats
{ {
var color = default(TPixel); var color = default(TPixel);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.pngColorType) switch (this.pngColorType)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength( Span<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
defilteredScanline, for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
this.bytesPerScanline,
this.header.BitDepth);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
{ {
byte intensity = (byte)(newScanline1[o] * factor); byte intensity = (byte)(newScanline1[o] * factor);
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
@ -755,10 +752,10 @@ namespace ImageSharp.Formats
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{ {
byte intensity = defilteredScanline[o]; byte intensity = scanlineBuffer[o];
byte alpha = defilteredScanline[o + this.bytesPerSample]; byte alpha = scanlineBuffer[o + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha)); color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color; rowSpan[x] = color;
} }
@ -767,31 +764,20 @@ namespace ImageSharp.Formats
case PngColorType.Palette: case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength( Span<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
var rgba = default(Rgba32); var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{ {
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it. // channel and we should try to read it.
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{ {
int index = newScanline[o]; int index = newScanline[o];
int offset = index * 3; int offset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = this.palette.GetRgb24(offset);
if (rgba.A > 0)
{
rgba.Rgb = this.palette.GetRgb24(offset);
}
else
{
rgba = default(Rgba32);
}
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -801,7 +787,7 @@ namespace ImageSharp.Formats
{ {
rgba.A = 255; rgba.A = 255;
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{ {
int index = newScanline[o]; int index = newScanline[o];
int offset = index * 3; int offset = index * 3;
@ -825,14 +811,12 @@ namespace ImageSharp.Formats
using (var compressed = new Buffer<byte>(length)) using (var compressed = new Buffer<byte>(length))
{ {
// TODO: Should we use pack from vector here instead? // TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length); this.From16BitTo8Bit(scanlineBuffer, compressed, length);
for (int x = pixelOffset, o = 1; for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3)
x < this.header.Width;
x += increment, o += this.bytesPerPixel)
{ {
rgba.R = compressed[o]; rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample]; rgba.G = compressed[o + 1];
rgba.B = compressed[o + (2 * this.bytesPerSample)]; rgba.B = compressed[o + 2];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -841,11 +825,11 @@ namespace ImageSharp.Formats
} }
else else
{ {
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{ {
rgba.R = defilteredScanline[o]; rgba.R = scanlineBuffer[o];
rgba.G = defilteredScanline[o + this.bytesPerSample]; rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -862,13 +846,13 @@ namespace ImageSharp.Formats
using (var compressed = new Buffer<byte>(length)) using (var compressed = new Buffer<byte>(length))
{ {
// TODO: Should we use pack from vector here instead? // TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length); this.From16BitTo8Bit(scanlineBuffer, compressed, length);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4)
{ {
rgba.R = compressed[o]; rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample]; rgba.G = compressed[o + 1];
rgba.B = compressed[o + (2 * this.bytesPerSample)]; rgba.B = compressed[o + 2];
rgba.A = compressed[o + (3 * this.bytesPerSample)]; rgba.A = compressed[o + 3];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -877,12 +861,12 @@ namespace ImageSharp.Formats
} }
else else
{ {
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{ {
rgba.R = defilteredScanline[o]; rgba.R = scanlineBuffer[o];
rgba.G = defilteredScanline[o + this.bytesPerSample]; rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)]; rgba.A = scanlineBuffer[o + (3 * this.bytesPerSample)];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -1025,9 +1009,9 @@ namespace ImageSharp.Formats
this.crc.Update(this.chunkTypeBuffer); this.crc.Update(this.chunkTypeBuffer);
this.crc.Update(chunk.Data, 0, chunk.Length); this.crc.Update(chunk.Data, 0, chunk.Length);
if (this.crc.Value != chunk.Crc) if (this.crc.Value != chunk.Crc && IsCriticalChunk(chunk))
{ {
throw new ImageFormatException("CRC Error. PNG Image chunk is corrupt!"); throw new ImageFormatException($"CRC Error. PNG {chunk.Type} chunk is corrupt!");
} }
} }

2
src/ImageSharp/Formats/Png/PngFormat.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary> /// </summary>
internal sealed class PngFormat : IImageFormat internal sealed class PngFormat : IImageFormat
{ {

2
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -80,7 +80,7 @@ namespace ImageSharp.Processing.Processors
return; return;
} }
this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0)); this.processMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
if (this.Expand) if (this.Expand)
{ {
this.CreateNewCanvas(sourceRectangle, this.processMatrix); this.CreateNewCanvas(sourceRectangle, this.processMatrix);

4
tests/ImageSharp.Tests/FileTestBase.cs

@ -79,8 +79,8 @@ namespace ImageSharp.Tests
// TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only
TestFile.Create(TestImages.Png.Splash), TestFile.Create(TestImages.Png.Splash),
// TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Bad.ChunkLength1), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Bad.ChunkLength2), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only

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

@ -3,14 +3,15 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.IO;
using System.IO.Compression;
using System.Text;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
using Xunit;
namespace ImageSharp.Tests namespace ImageSharp.Tests
{ {
using System.Text;
using Xunit;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
public class PngDecoderTests public class PngDecoderTests
{ {
private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32;
@ -18,7 +19,7 @@ namespace ImageSharp.Tests
public static readonly string[] TestFiles = public static readonly string[] TestFiles =
{ {
TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.Interlaced, TestImages.Png.FilterVar, TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.Interlaced, TestImages.Png.FilterVar,
TestImages.Png.Bad.ChunkLength1, TestImages.Png.Bad.ChunkLength2, TestImages.Png.Rgb48Bpp TestImages.Png.Bad.ChunkLength1, TestImages.Png.Bad.ChunkLength2, TestImages.Png.Rgb48Bpp, TestImages.Png.Rgb48BppInterlaced
}; };
[Theory] [Theory]
@ -35,12 +36,12 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead()
{ {
PngDecoder options = new PngDecoder() var options = new PngDecoder()
{ {
IgnoreMetadata = false IgnoreMetadata = false
}; };
TestFile testFile = TestFile.Create(TestImages.Png.Blur); var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options)) using (Image<Rgba32> image = testFile.CreateImage(options))
{ {
@ -53,12 +54,12 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored()
{ {
PngDecoder options = new PngDecoder() var options = new PngDecoder()
{ {
IgnoreMetadata = true IgnoreMetadata = true
}; };
TestFile testFile = TestFile.Create(TestImages.Png.Blur); var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options)) using (Image<Rgba32> image = testFile.CreateImage(options))
{ {
@ -69,12 +70,12 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding()
{ {
PngDecoder options = new PngDecoder() var options = new PngDecoder()
{ {
TextEncoding = Encoding.Unicode TextEncoding = Encoding.Unicode
}; };
TestFile testFile = TestFile.Create(TestImages.Png.Blur); var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options)) using (Image<Rgba32> image = testFile.CreateImage(options))
{ {
@ -82,5 +83,67 @@ namespace ImageSharp.Tests
Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name);
} }
} }
[Theory]
[InlineData(PngChunkTypes.Header)]
[InlineData(PngChunkTypes.Palette)]
// [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this
[InlineData(PngChunkTypes.End)]
public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(string chunkName)
{
using (var memStream = new MemoryStream())
{
memStream.Skip(8);
WriteChunk(memStream, chunkName);
CompressStream(memStream);
var decoder = new PngDecoder();
ImageFormatException exception = Assert.Throws<ImageFormatException>(() =>
{
decoder.Decode<Rgb24>(null, memStream);
});
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
}
}
[Theory]
[InlineData(PngChunkTypes.Gamma)]
[InlineData(PngChunkTypes.PaletteAlpha)]
[InlineData(PngChunkTypes.Physical)]
//[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this
public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(string chunkName)
{
using (var memStream = new MemoryStream())
{
memStream.Skip(8);
WriteChunk(memStream, chunkName);
CompressStream(memStream);
var decoder = new PngDecoder();
decoder.Decode<Rgb24>(null, memStream);
}
}
private static void WriteChunk(MemoryStream memStream, string chunkName)
{
memStream.Write(new byte[] { 0, 0, 0, 1 }, 0, 4);
memStream.Write(Encoding.GetEncoding("ASCII").GetBytes(chunkName), 0, 4);
memStream.Write(new byte[] { 0, 0, 0, 0, 0 }, 0, 5);
}
private static void CompressStream(Stream stream)
{
stream.Position = 0;
using (var deflateStream = new DeflateStream(stream, CompressionLevel.NoCompression, true))
{
}
stream.Position = 0;
}
} }
} }

40
tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs

@ -40,25 +40,27 @@ namespace ImageSharp.Tests.Formats.Png
} }
} }
[Theory] // JJS: Disabled for now as the decoder now correctly decodes the full pixel components if the
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)] // paletted image has alpha of 0
public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider) //[Theory]
where TPixel : struct, IPixel<TPixel> //[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
{ //public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider)
// does saving a file then repoening mean both files are identical??? // where TPixel : struct, IPixel<TPixel>
using (Image<TPixel> image = provider.GetImage()) //{
using (MemoryStream ms = new MemoryStream()) // // does saving a file then repoening mean both files are identical???
{ // using (Image<TPixel> image = provider.GetImage())
// image.Save(provider.Utility.GetTestOutputFileName("bmp")); // using (MemoryStream ms = new MemoryStream())
image.Save(ms, new PngEncoder() { PaletteSize = 256 }); // {
ms.Position = 0; // // image.Save(provider.Utility.GetTestOutputFileName("bmp"));
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder())) // image.Save(ms, new PngEncoder() { PaletteSize = 256 });
{ // ms.Position = 0;
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); // using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
ImageComparer.CheckSimilarity(image, img2, 0.03f); // {
} // // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
} // ImageComparer.CheckSimilarity(image, img2, 0.03f);
} // }
// }
//}
// JJS: Commented out for now since the test does not take into lossy nature of indexing. // JJS: Commented out for now since the test does not take into lossy nature of indexing.
//[Theory] //[Theory]

2
tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs

@ -1,4 +1,4 @@
// <copyright file="PixelAccessorTests.cs" company="James Jackson-South"> // <copyright file="ImageDiscoverMimeType.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>

62
tests/ImageSharp.Tests/Image/ImageEqualTests.cs

@ -0,0 +1,62 @@
// <copyright file="ImageEqualTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using Xunit;
public class ImageEqualTests
{
[Fact]
public void TestsThatVimImagesAreEqual()
{
var image1Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VimImage1);
var image2Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VimImage2);
using (Image<Rgba32> img1 = image1Provider.GetImage())
using (Image<Rgba32> img2 = image2Provider.GetImage())
{
bool imagesEqual = AreImagesEqual(img1, img2);
Assert.True(imagesEqual);
}
}
[Fact]
public void TestsThatVersioningImagesAreEqual()
{
var image1Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VersioningImage1);
var image2Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VersioningImage2);
using (Image<Rgba32> img1 = image1Provider.GetImage())
using (Image<Rgba32> img2 = image2Provider.GetImage())
{
bool imagesEqual = AreImagesEqual(img1, img2);
Assert.True(imagesEqual);
}
}
private bool AreImagesEqual(Image<Rgba32> img1, Image<Rgba32> img2)
{
Assert.Equal(img1.Width, img2.Width);
Assert.Equal(img1.Height, img2.Height);
for (int y = 0; y < img1.Height; y++)
{
for (int x = 0; x < img1.Width; x++)
{
Rgba32 pixel1 = img1[x, y];
Rgba32 pixel2 = img2[x, y];
if (pixel1 != pixel2)
{
return false;
}
}
}
return true;
}
}
}

48
tests/ImageSharp.Tests/Image/ImageLoadTests.cs

@ -1,4 +1,4 @@
// <copyright file="PixelAccessorTests.cs" company="James Jackson-South"> // <copyright file="ImageLoadTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -10,7 +10,6 @@ namespace ImageSharp.Tests
using ImageSharp.Formats; using ImageSharp.Formats;
using ImageSharp.IO; using ImageSharp.IO;
using ImageSharp.PixelFormats;
using Moq; using Moq;
using Xunit; using Xunit;
@ -44,7 +43,8 @@ namespace ImageSharp.Tests
this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>())) this.localDecoder.Setup(x => x.Decode<Rgba32>(It.IsAny<Configuration>(), It.IsAny<Stream>()))
.Callback<Configuration, Stream>((c, s) => { .Callback<Configuration, Stream>((c, s) =>
{
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
s.CopyTo(ms); s.CopyTo(ms);
@ -76,7 +76,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromStream() public void LoadFromStream()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream); Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img); Assert.NotNull(img);
@ -87,12 +87,11 @@ namespace ImageSharp.Tests
public void LoadFromNoneSeekableStream() public void LoadFromNoneSeekableStream()
{ {
NoneSeekableStream stream = new NoneSeekableStream(this.DataStream); NoneSeekableStream stream = new NoneSeekableStream(this.DataStream);
Image<Rgba32> img = Image.Load<Rgba32>(stream); Image<Rgba32> img = Image.Load<Rgba32>(stream);
Assert.NotNull(img); Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
} }
[Fact] [Fact]
@ -104,20 +103,18 @@ namespace ImageSharp.Tests
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img); Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
} }
[Fact] [Fact]
public void LoadFromStreamWithConfig() public void LoadFromStreamWithConfig()
{ {
Stream stream = new MemoryStream(); Stream stream = new MemoryStream();
Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, stream); Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, stream);
Assert.NotNull(img); Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream)); this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
} }
[Fact] [Fact]
@ -130,7 +127,6 @@ namespace ImageSharp.Tests
Assert.Equal(this.returnImage, img); Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream)); this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
} }
@ -138,7 +134,7 @@ namespace ImageSharp.Tests
public void LoadFromStreamWithDecoder() public void LoadFromStreamWithDecoder()
{ {
Stream stream = new MemoryStream(); Stream stream = new MemoryStream();
Image<Rgba32> img = Image.Load<Rgba32>(stream, this.localDecoder.Object); Image<Rgba32> img = Image.Load<Rgba32>(stream, this.localDecoder.Object);
Assert.NotNull(img); Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, stream)); this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, stream));
@ -158,13 +154,11 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromBytes() public void LoadFromBytes()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray()); Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray());
Assert.NotNull(img); Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
} }
[Fact] [Fact]
@ -182,7 +176,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromBytesWithConfig() public void LoadFromBytesWithConfig()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, this.DataStream.ToArray()); Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, this.DataStream.ToArray());
Assert.NotNull(img); Assert.NotNull(img);
@ -207,7 +201,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromBytesWithDecoder() public void LoadFromBytesWithDecoder()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray(), this.localDecoder.Object); Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray(), this.localDecoder.Object);
Assert.NotNull(img); Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, It.IsAny<Stream>())); this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, It.IsAny<Stream>()));
@ -228,13 +222,11 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromFile() public void LoadFromFile()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream); Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img); Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
} }
[Fact] [Fact]
@ -251,12 +243,11 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromFileWithConfig() public void LoadFromFileWithConfig()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, this.FilePath); Image<Rgba32> img = Image.Load<Rgba32>(this.LocalConfiguration, this.FilePath);
Assert.NotNull(img); Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream)); this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream));
} }
[Fact] [Fact]
@ -273,7 +264,7 @@ namespace ImageSharp.Tests
[Fact] [Fact]
public void LoadFromFileWithDecoder() public void LoadFromFileWithDecoder()
{ {
Image<Rgba32> img = Image.Load<Rgba32>(this.FilePath, this.localDecoder.Object); Image<Rgba32> img = Image.Load<Rgba32>(this.FilePath, this.localDecoder.Object);
Assert.NotNull(img); Assert.NotNull(img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, this.DataStream)); this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, this.DataStream));
@ -305,7 +296,6 @@ namespace ImageSharp.Tests
Assert.Equal(Rgba32.White, px[1, 0]); Assert.Equal(Rgba32.White, px[1, 0]);
Assert.Equal(Rgba32.Black, px[1, 1]); Assert.Equal(Rgba32.Black, px[1, 1]);
} }
} }
@ -327,7 +317,17 @@ namespace ImageSharp.Tests
Assert.Equal(Rgba32.White, px[1, 0]); Assert.Equal(Rgba32.White, px[1, 0]);
Assert.Equal(Rgba32.Black, px[1, 1]); Assert.Equal(Rgba32.Black, px[1, 1]);
}
}
[Fact]
public void LoadsImageWithoutThrowingCrcException()
{
var image1Provider = TestImageProvider<Rgba32>.File(TestImages.Png.VersioningImage1);
using (Image<Rgba32> img = image1Provider.GetImage())
{
Assert.Equal(166036, img.Pixels.Length);
} }
} }

54
tests/ImageSharp.Tests/Image/ImageRotationTests.cs

@ -0,0 +1,54 @@
using SixLabors.Primitives;
using Xunit;
namespace ImageSharp.Tests
{
public class ImageRotationTests
{
[Fact]
public void RotateImageByMinus90Degrees()
{
(Size original, Size rotated) = Rotate(-90);
Assert.Equal(new Size(original.Height, original.Width), rotated);
}
[Fact]
public void RotateImageBy90Degrees()
{
(Size original, Size rotated) = Rotate(90);
Assert.Equal(new Size(original.Height, original.Width), rotated);
}
[Fact]
public void RotateImageBy180Degrees()
{
(Size original, Size rotated) = Rotate(180);
Assert.Equal(original, rotated);
}
[Fact]
public void RotateImageBy270Degrees()
{
(Size original, Size rotated) = Rotate(270);
Assert.Equal(new Size(original.Height, original.Width), rotated);
}
[Fact]
public void RotateImageBy360Degrees()
{
(Size original, Size rotated) = Rotate(360);
Assert.Equal(original, rotated);
}
private static (Size original, Size rotated) Rotate(int angle)
{
TestFile file = TestFile.Create(TestImages.Bmp.Car);
using (Image<Rgba32> image = Image.Load<Rgba32>(file.FilePath))
{
Size original = image.Bounds.Size;
image.Rotate(angle);
return (original, image.Bounds.Size);
}
}
}
}

2
tests/ImageSharp.Tests/Image/ImageSaveTests.cs

@ -1,4 +1,4 @@
// <copyright file="PixelAccessorTests.cs" company="James Jackson-South"> // <copyright file="ImageSaveTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>

2
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -1,4 +1,4 @@
// <copyright file="PixelAccessorTests.cs" company="James Jackson-South"> // <copyright file="ImageTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>

10
tests/ImageSharp.Tests/TestImages.cs

@ -26,6 +26,7 @@ namespace ImageSharp.Tests
public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string SplashInterlaced = "Png/splash-interlaced.png";
public const string Interlaced = "Png/interlaced.png"; public const string Interlaced = "Png/interlaced.png";
public const string Rgb48Bpp = "Png/rgb-48bpp.png"; public const string Rgb48Bpp = "Png/rgb-48bpp.png";
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png"; public const string Filter0 = "Png/filter0.png";
@ -37,6 +38,12 @@ namespace ImageSharp.Tests
// Filter changing per scanline // Filter changing per scanline
public const string FilterVar = "Png/filterVar.png"; public const string FilterVar = "Png/filterVar.png";
public const string VimImage1 = "Png/vim16x16_1.png";
public const string VimImage2 = "Png/vim16x16_2.png";
public const string VersioningImage1 = "Png/versioning-1_1.png";
public const string VersioningImage2 = "Png/versioning-1_2.png";
public static class Bad public static class Bad
{ {
// Odd chunk lengths // Odd chunk lengths
@ -49,7 +56,8 @@ namespace ImageSharp.Tests
P1, Pd, Blur, Splash, Cross, P1, Pd, Blur, Splash, Cross,
Powerpoint, SplashInterlaced, Interlaced, Powerpoint, SplashInterlaced, Interlaced,
Filter0, Filter1, Filter2, Filter3, Filter4, Filter0, Filter1, Filter2, Filter3, Filter4,
FilterVar FilterVar, VimImage1, VimImage2, VersioningImage1,
VersioningImage2
}; };
} }

3
tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp-interlaced.png

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

3
tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png

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

3
tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png

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

3
tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png

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

3
tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png

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