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:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# 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:
- 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:
coverity_scan:
project:
name: "JimBobSquarePants/ImageSharp"
name: "SixLabors/ImageSharp"
description: "Build submitted via Travis CI"
notification_email: james_south@hotmail.com
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`.
@ -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).
[![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 issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues)
[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network)
[![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/SixLabors/ImageSharp.svg)](https://github.com/SixLabors/ImageSharp/issues)
[![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/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)
[![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/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|
|-------------|:----------:|:-----------:|
|**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)|
|**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)|
|**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/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
@ -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.
```bash
git clone https://github.com/JimBobSquarePants/ImageSharp
git clone https://github.com/SixLabors/ImageSharp
```
### Features
@ -121,7 +121,7 @@ For optimized access within a loop it is recommended that the following methods
1. `image.GetRowSpan(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.

4
appveyor.yml

@ -7,10 +7,6 @@ skip_branch_with_pr: true
init:
- 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:
- cmd: build.cmd

10
src/ImageSharp/Configuration.cs

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

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

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <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>
internal sealed class BmpFormat : IImageFormat
{

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

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

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

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <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>
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="bytesPerScanline">The number of bytes per scanline</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.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.MustBeGreaterThan(bits, 0, nameof(bits));
byte[] result;
if (bits < 8)
if (bits >= 8)
{
result = new byte[bytesPerScanline * 8 / bits];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
return source;
}
// ReSharper disable once ForCanBeConvertedToForeach
// First byte is the marker so skip.
for (int i = 1; i < bytesPerScanline; i++)
byte[] result = new byte[bytesPerScanline * 8 / bits];
int mask = 0xFF >> (8 - bits);
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];
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex;
result[resultOffset] = (byte)colorIndex;
resultOffset++;
}
resultOffset++;
}
}
else
{
result = source;
}
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>
/// Reads the data chunk containing physical dimension data.
/// </summary>
@ -575,13 +578,15 @@ namespace ImageSharp.Formats
{
var color = default(TPixel);
Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
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++)
{
byte intensity = (byte)(newScanline1[x] * factor);
@ -595,10 +600,10 @@ namespace ImageSharp.Formats
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 alpha = defilteredScanline[offset + this.bytesPerSample];
byte intensity = scanlineBuffer[offset];
byte alpha = scanlineBuffer[offset + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
@ -608,7 +613,7 @@ namespace ImageSharp.Formats
case PngColorType.Palette:
this.ProcessScanlineFromPalette(defilteredScanline, rowSpan);
this.ProcessScanlineFromPalette(scanlineBuffer, rowSpan);
break;
@ -673,10 +678,10 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</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>
{
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
Span<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] pal = this.palette;
var color = default(TPixel);
@ -688,19 +693,11 @@ namespace ImageSharp.Formats
// channel and we should try to read it.
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x + 1];
int index = newScanline[x];
int pixelOffset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
if (rgba.A > 0)
{
rgba.Rgb = pal.GetRgb24(pixelOffset);
}
else
{
rgba = default(Rgba32);
}
rgba.Rgb = pal.GetRgb24(pixelOffset);
color.PackFromRgba32(rgba);
row[x] = color;
@ -712,7 +709,7 @@ namespace ImageSharp.Formats
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x + 1];
int index = newScanline[x];
int pixelOffset = index * 3;
rgba.Rgb = pal.GetRgb24(pixelOffset);
@ -736,15 +733,15 @@ namespace ImageSharp.Formats
{
var color = default(TPixel);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength(
defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
Span<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
byte intensity = (byte)(newScanline1[o] * factor);
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
@ -755,10 +752,10 @@ namespace ImageSharp.Formats
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 alpha = defilteredScanline[o + this.bytesPerSample];
byte intensity = scanlineBuffer[o];
byte alpha = scanlineBuffer[o + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
}
@ -767,31 +764,20 @@ namespace ImageSharp.Formats
case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength(
defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
Span<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
var rgba = default(Rgba32);
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
// 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 offset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
if (rgba.A > 0)
{
rgba.Rgb = this.palette.GetRgb24(offset);
}
else
{
rgba = default(Rgba32);
}
rgba.Rgb = this.palette.GetRgb24(offset);
color.PackFromRgba32(rgba);
rowSpan[x] = color;
@ -801,7 +787,7 @@ namespace ImageSharp.Formats
{
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 offset = index * 3;
@ -825,14 +811,12 @@ namespace ImageSharp.Formats
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length);
for (int x = pixelOffset, o = 1;
x < this.header.Width;
x += increment, o += this.bytesPerPixel)
this.From16BitTo8Bit(scanlineBuffer, compressed, length);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3)
{
rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample];
rgba.B = compressed[o + (2 * this.bytesPerSample)];
rgba.G = compressed[o + 1];
rgba.B = compressed[o + 2];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
@ -841,11 +825,11 @@ namespace ImageSharp.Formats
}
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.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.R = scanlineBuffer[o];
rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
@ -862,13 +846,13 @@ namespace ImageSharp.Formats
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
this.From16BitTo8Bit(scanlineBuffer, compressed, length);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4)
{
rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample];
rgba.B = compressed[o + (2 * this.bytesPerSample)];
rgba.A = compressed[o + (3 * this.bytesPerSample)];
rgba.G = compressed[o + 1];
rgba.B = compressed[o + 2];
rgba.A = compressed[o + 3];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
@ -877,12 +861,12 @@ namespace ImageSharp.Formats
}
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.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)];
rgba.R = scanlineBuffer[o];
rgba.G = scanlineBuffer[o + this.bytesPerSample];
rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)];
rgba.A = scanlineBuffer[o + (3 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
@ -1025,9 +1009,9 @@ namespace ImageSharp.Formats
this.crc.Update(this.chunkTypeBuffer);
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;
/// <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>
internal sealed class PngFormat : IImageFormat
{

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

@ -80,7 +80,7 @@ namespace ImageSharp.Processing.Processors
return;
}
this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0));
this.processMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
if (this.Expand)
{
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.Png.Splash),
// 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.ChunkLength2), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Bad.ChunkLength1), // 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.Blur), // 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.
// </copyright>
using System.IO;
using System.IO.Compression;
using System.Text;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
using Xunit;
namespace ImageSharp.Tests
{
using System.Text;
using Xunit;
using ImageSharp.Formats;
using ImageSharp.PixelFormats;
public class PngDecoderTests
{
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 =
{
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]
@ -35,12 +36,12 @@ namespace ImageSharp.Tests
[Fact]
public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead()
{
PngDecoder options = new PngDecoder()
var options = new PngDecoder()
{
IgnoreMetadata = false
};
TestFile testFile = TestFile.Create(TestImages.Png.Blur);
var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options))
{
@ -53,12 +54,12 @@ namespace ImageSharp.Tests
[Fact]
public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored()
{
PngDecoder options = new PngDecoder()
var options = new PngDecoder()
{
IgnoreMetadata = true
};
TestFile testFile = TestFile.Create(TestImages.Png.Blur);
var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options))
{
@ -69,12 +70,12 @@ namespace ImageSharp.Tests
[Fact]
public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding()
{
PngDecoder options = new PngDecoder()
var options = new PngDecoder()
{
TextEncoding = Encoding.Unicode
};
TestFile testFile = TestFile.Create(TestImages.Png.Blur);
var testFile = TestFile.Create(TestImages.Png.Blur);
using (Image<Rgba32> image = testFile.CreateImage(options))
{
@ -82,5 +83,67 @@ namespace ImageSharp.Tests
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]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
// does saving a file then repoening mean both files are identical???
using (Image<TPixel> image = provider.GetImage())
using (MemoryStream ms = new MemoryStream())
{
// image.Save(provider.Utility.GetTestOutputFileName("bmp"));
image.Save(ms, new PngEncoder() { PaletteSize = 256 });
ms.Position = 0;
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
{
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
ImageComparer.CheckSimilarity(image, img2, 0.03f);
}
}
}
// JJS: Disabled for now as the decoder now correctly decodes the full pixel components if the
// paletted image has alpha of 0
//[Theory]
//[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
//public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider)
// where TPixel : struct, IPixel<TPixel>
//{
// // does saving a file then repoening mean both files are identical???
// using (Image<TPixel> image = provider.GetImage())
// using (MemoryStream ms = new MemoryStream())
// {
// // image.Save(provider.Utility.GetTestOutputFileName("bmp"));
// image.Save(ms, new PngEncoder() { PaletteSize = 256 });
// ms.Position = 0;
// using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
// {
// // 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.
//[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.
// Licensed under the Apache License, Version 2.0.
// </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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -10,7 +10,6 @@ namespace ImageSharp.Tests
using ImageSharp.Formats;
using ImageSharp.IO;
using ImageSharp.PixelFormats;
using Moq;
using Xunit;
@ -44,7 +43,8 @@ namespace ImageSharp.Tests
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())
{
s.CopyTo(ms);
@ -76,7 +76,7 @@ namespace ImageSharp.Tests
[Fact]
public void LoadFromStream()
{
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
@ -87,12 +87,11 @@ namespace ImageSharp.Tests
public void LoadFromNoneSeekableStream()
{
NoneSeekableStream stream = new NoneSeekableStream(this.DataStream);
Image<Rgba32> img = Image.Load<Rgba32>(stream);
Image<Rgba32> img = Image.Load<Rgba32>(stream);
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
@ -104,20 +103,18 @@ namespace ImageSharp.Tests
Assert.Equal(TestFormat.GlobalTestFormat.Sample<Rgba32>(), img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
public void LoadFromStreamWithConfig()
{
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);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
}
[Fact]
@ -130,7 +127,6 @@ namespace ImageSharp.Tests
Assert.Equal(this.returnImage, img);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, stream));
}
@ -138,7 +134,7 @@ namespace ImageSharp.Tests
public void LoadFromStreamWithDecoder()
{
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);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, stream));
@ -158,13 +154,11 @@ namespace ImageSharp.Tests
[Fact]
public void LoadFromBytes()
{
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray());
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream.ToArray());
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
@ -182,7 +176,7 @@ namespace ImageSharp.Tests
[Fact]
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);
@ -207,7 +201,7 @@ namespace ImageSharp.Tests
[Fact]
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);
this.localDecoder.Verify(x => x.Decode<Rgba32>(Configuration.Default, It.IsAny<Stream>()));
@ -228,13 +222,11 @@ namespace ImageSharp.Tests
[Fact]
public void LoadFromFile()
{
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Image<Rgba32> img = Image.Load<Rgba32>(this.DataStream);
Assert.NotNull(img);
TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default);
}
[Fact]
@ -251,12 +243,11 @@ namespace ImageSharp.Tests
[Fact]
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);
this.localDecoder.Verify(x => x.Decode<Rgba32>(this.LocalConfiguration, this.DataStream));
}
[Fact]
@ -273,7 +264,7 @@ namespace ImageSharp.Tests
[Fact]
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);
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.Black, px[1, 1]);
}
}
@ -327,7 +317,17 @@ namespace ImageSharp.Tests
Assert.Equal(Rgba32.White, px[1, 0]);
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.
// Licensed under the Apache License, Version 2.0.
// </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.
// Licensed under the Apache License, Version 2.0.
// </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 Interlaced = "Png/interlaced.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
public const string Filter0 = "Png/filter0.png";
@ -37,6 +38,12 @@ namespace ImageSharp.Tests
// Filter changing per scanline
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
{
// Odd chunk lengths
@ -49,7 +56,8 @@ namespace ImageSharp.Tests
P1, Pd, Blur, Splash, Cross,
Powerpoint, SplashInterlaced, Interlaced,
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