diff --git a/.travis.yml b/.travis.yml index af8d4ad9d..4ae163530 100644 --- a/.travis.yml +++ b/.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" diff --git a/README.md b/README.md index e46b11379..7113d6ba1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# ImageSharp ImageSharp +# ImageSharp 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. diff --git a/appveyor.yml b/appveyor.yml index 079913311..fdbe46b01 100644 --- a/appveyor.yml +++ b/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 diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index bb230beac..948103fed 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -157,6 +157,10 @@ namespace ImageSharp.Formats } nextFlag = stream.ReadByte(); + if (nextFlag == -1) + { + break; + } } } finally diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7baf35295..492e952d8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -319,45 +319,48 @@ namespace ImageSharp.Formats /// The bytes to convert from. Cannot be null. /// The number of bytes per scanline /// The number of bits per value. - /// The resulting array. Is never null. + /// The resulting array. Is never null. /// is null. /// is less than or equals than zero. - private static byte[] ToArrayByBitsLength(byte[] source, int bytesPerScanline, int bits) + private static Span ToArrayByBitsLength(Span 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; + } + /// /// Reads the data chunk containing physical dimension data. /// @@ -649,13 +652,15 @@ namespace ImageSharp.Formats { var color = default(TPixel); Span rowSpan = pixels.GetRowSpan(this.currentRow); + + // Trim the first marker byte from the buffer var scanlineBuffer = new Span(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 newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); for (int x = 0; x < this.header.Width; x++) { byte intensity = (byte)(newScanline1[x] * factor); @@ -669,10 +674,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; @@ -682,7 +687,7 @@ namespace ImageSharp.Formats case PngColorType.Palette: - this.ProcessScanlineFromPalette(defilteredScanline, rowSpan); + this.ProcessScanlineFromPalette(scanlineBuffer, rowSpan); break; @@ -747,10 +752,10 @@ namespace ImageSharp.Formats /// The type of pixel we are expanding to /// The scanline /// Thecurrent output image row - private void ProcessScanlineFromPalette(byte[] defilteredScanline, Span row) + private void ProcessScanlineFromPalette(Span defilteredScanline, Span row) where TPixel : struct, IPixel { - byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + Span newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] pal = this.palette; var color = default(TPixel); @@ -762,19 +767,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; @@ -786,7 +783,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); @@ -810,15 +807,15 @@ namespace ImageSharp.Formats { var color = default(TPixel); + // Trim the first marker byte from the buffer + var scanlineBuffer = new Span(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 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)); @@ -829,10 +826,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; } @@ -841,31 +838,20 @@ namespace ImageSharp.Formats case PngColorType.Palette: - byte[] newScanline = ToArrayByBitsLength( - defilteredScanline, - this.bytesPerScanline, - this.header.BitDepth); + Span 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; @@ -875,7 +861,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; @@ -899,10 +885,8 @@ namespace ImageSharp.Formats using (var compressed = new Buffer(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(new Span(defilteredScanline, 1), compressed, length); - for (int x = pixelOffset, o = 0; - x < this.header.Width; - x += increment, o += 3) + 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 + 1]; @@ -915,11 +899,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; @@ -936,7 +920,7 @@ namespace ImageSharp.Formats using (var compressed = new Buffer(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(new Span(defilteredScanline, 1), compressed, length); + this.From16BitTo8Bit(scanlineBuffer, compressed, length); for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4) { rgba.R = compressed[o]; @@ -951,12 +935,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; @@ -1099,9 +1083,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!"); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index e6b1d180f..d563d072a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -79,7 +79,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); diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 51a1562f5..08ed69f3e 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/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 diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index fcc0010ec..c7147b226 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -3,15 +3,15 @@ // Licensed under the Apache License, Version 2.0. // +using System.IO; +using System.IO.Compression; +using System.Text; +using ImageSharp.Formats; +using ImageSharp.PixelFormats; +using Xunit; + namespace ImageSharp.Tests { - using System.IO; - 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; @@ -36,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 image = testFile.CreateImage(options)) { @@ -54,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 image = testFile.CreateImage(options)) { @@ -70,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 image = testFile.CreateImage(options)) { @@ -100,5 +100,67 @@ namespace ImageSharp.Tests Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); } } + + [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(() => + { + decoder.Decode(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(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; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index bac340a71..c977f5fb5 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/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(TestImageProvider provider) - where TPixel : struct, IPixel - { - // does saving a file then repoening mean both files are identical??? - using (Image 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 img2 = Image.Load(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(TestImageProvider provider) + // where TPixel : struct, IPixel + //{ + // // does saving a file then repoening mean both files are identical??? + // using (Image 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 img2 = Image.Load(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] diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index 0d0d00957..346636f34 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/tests/ImageSharp.Tests/Image/ImageEqualTests.cs b/tests/ImageSharp.Tests/Image/ImageEqualTests.cs new file mode 100644 index 000000000..399006012 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageEqualTests.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + public class ImageEqualTests + { + [Fact] + public void TestsThatVimImagesAreEqual() + { + var image1Provider = TestImageProvider.File(TestImages.Png.VimImage1); + var image2Provider = TestImageProvider.File(TestImages.Png.VimImage2); + + using (Image img1 = image1Provider.GetImage()) + using (Image img2 = image2Provider.GetImage()) + { + bool imagesEqual = AreImagesEqual(img1, img2); + Assert.True(imagesEqual); + } + } + + [Fact] + public void TestsThatVersioningImagesAreEqual() + { + var image1Provider = TestImageProvider.File(TestImages.Png.VersioningImage1); + var image2Provider = TestImageProvider.File(TestImages.Png.VersioningImage2); + + using (Image img1 = image1Provider.GetImage()) + using (Image img2 = image2Provider.GetImage()) + { + bool imagesEqual = AreImagesEqual(img1, img2); + Assert.True(imagesEqual); + } + } + + private bool AreImagesEqual(Image img1, Image 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; + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index bfbdd93f1..c8638d8ab 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -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(It.IsAny(), It.IsAny())) - .Callback((c, s) => { + .Callback((c, s) => + { using (var ms = new MemoryStream()) { s.CopyTo(ms); @@ -92,7 +92,6 @@ namespace ImageSharp.Tests Assert.NotNull(img); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -104,9 +103,8 @@ namespace ImageSharp.Tests Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } - + [Fact] public void LoadFromStreamWithConfig() @@ -117,7 +115,6 @@ namespace ImageSharp.Tests Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } [Fact] @@ -130,7 +127,6 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } @@ -162,9 +158,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -232,9 +226,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -256,7 +248,6 @@ namespace ImageSharp.Tests Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream)); - } [Fact] @@ -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.File(TestImages.Png.VersioningImage1); + + using (Image img = image1Provider.GetImage()) + { + Assert.Equal(166036, img.Pixels.Length); } } diff --git a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs new file mode 100644 index 000000000..56cec4219 --- /dev/null +++ b/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 image = Image.Load(file.FilePath)) + { + Size original = image.Bounds.Size; + image.Rotate(angle); + return (original, image.Bounds.Size); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index eecb98380..ad8a5cc7d 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 3157c27d8..217bf37fe 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 53e6ae1ef..644e4d3eb 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -41,6 +41,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 @@ -53,7 +59,8 @@ namespace ImageSharp.Tests P1, Pd, Blur, Splash, Cross, Powerpoint, SplashInterlaced, Interlaced, Filter0, Filter1, Filter2, Filter3, Filter4, - FilterVar + FilterVar, VimImage1, VimImage2, VersioningImage1, + VersioningImage2 }; } diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png new file mode 100644 index 000000000..0eb37aab8 --- /dev/null +++ b/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 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png new file mode 100644 index 000000000..d402c7fd2 --- /dev/null +++ b/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 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png new file mode 100644 index 000000000..a55a1a73d --- /dev/null +++ b/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 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png new file mode 100644 index 000000000..39d42c88f --- /dev/null +++ b/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