From f8573e334a4673a31665ee280a10b0e4eb96a15a Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Thu, 10 Aug 2017 15:08:14 +0700 Subject: [PATCH 01/11] Add an equivalent for System.Drawing.Image.GetPixelFormatSize() (#258) --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 11 +++ src/ImageSharp/Formats/Gif/GifDecoder.cs | 11 +++ src/ImageSharp/Formats/IImageDecoder.cs | 8 ++ src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 11 +++ .../Formats/Jpeg/JpegDecoderCore.cs | 22 +++++- src/ImageSharp/Formats/Png/PngDecoder.cs | 12 +++ src/ImageSharp/Formats/Png/PngDecoderCore.cs | 74 ++++++++++++++++++ src/ImageSharp/Image/Image.Decode.cs | 14 ++++ src/ImageSharp/Image/Image.FromStream.cs | 27 +++++++ .../Formats/Bmp/BmpDecoderTests.cs | 29 +++++++ .../Formats/Gif/GifDecoderTests.cs | 15 ++++ .../Formats/Jpg/JpegDecoderTests.cs | 16 ++++ .../Formats/Png/PngDecoderTests.cs | 18 +++++ .../Image/ImageDiscoverMimeType.cs | 7 +- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 26 +++--- tests/ImageSharp.Tests/TestFileSystem.cs | 2 +- tests/ImageSharp.Tests/TestFormat.cs | 15 ++-- tests/ImageSharp.Tests/TestImages.cs | 6 +- .../TestImages/Formats/Bmp/bpp8.bmp | Bin 0 -> 65002 bytes .../TestImages/Formats/Png/bpp1.png | Bin 0 -> 4403 bytes .../TestImages/Formats/Png/gray_4bpp.png | Bin 0 -> 7396 bytes .../TestImages/Formats/Png/palette-8bpp.png | Bin 0 -> 9171 bytes 22 files changed, 297 insertions(+), 27 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Bmp/bpp8.bmp create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/bpp1.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/gray_4bpp.png create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/palette-8bpp.png diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 5baf1b1a5a..bdd15c2d71 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -37,5 +37,16 @@ public Image Decode(Configuration configuration, Stream stream) return new BmpDecoderCore(configuration, this).Decode(stream); } + + /// + public int DetectPixelSize(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + byte[] buffer = new byte[2]; + stream.Skip(28); + stream.Read(buffer, 0, 2); + return BitConverter.ToInt16(buffer, 0); + } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 927289094f..4d847c9fe7 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -33,5 +33,16 @@ public Image Decode(Configuration configuration, Stream stream) var decoder = new GifDecoderCore(configuration, this); return decoder.Decode(stream); } + + /// + public int DetectPixelSize(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + byte[] buffer = new byte[1]; + stream.Skip(10); // Skip the identifier and size + stream.Read(buffer, 0, 1); // Skip the identifier and size + return (buffer[0] & 0x07) + 1; // The lowest 3 bits represent the bit depth minus 1 + } } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 66eabb1b82..a16ef2612c 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -25,5 +25,13 @@ public interface IImageDecoder /// The decoded image Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; + + /// + /// Detects the image pixel size from the specified stream. + /// + /// The configuration for the image. + /// The containing image data. + /// The color depth, in number of bits per pixel + int DetectPixelSize(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index b3caddeca7..8bdbdfe0cf 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -32,5 +32,16 @@ public Image Decode(Configuration configuration, Stream stream) return decoder.Decode(stream); } } + + /// + public int DetectPixelSize(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this)) + { + return decoder.DetectPixelSize(stream); + } + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 0ce927e516..60c9f1a1d5 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -30,6 +30,11 @@ internal sealed unsafe class JpegDecoderCore : IDisposable /// public const int MaxTq = 3; + /// + /// The only supported precision + /// + public const int SupportedPrecision = 8; + // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P #pragma warning disable SA1401 // FieldsMustBePrivate @@ -191,7 +196,7 @@ public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) public bool IgnoreMetadata { get; private set; } /// - /// Decodes the image from the specified and sets + /// Decodes the image from the specified and sets /// the data to image. /// /// The pixel format. @@ -208,6 +213,17 @@ public Image Decode(Stream stream) return image; } + /// + /// Detects the image pixel size from the specified stream. + /// + /// The containing image data. + /// The color depth, in number of bits per pixel + public int DetectPixelSize(Stream stream) + { + this.ProcessStream(new ImageMetaData(), stream, true); + return this.ComponentCount * SupportedPrecision; + } + /// /// Dispose /// @@ -1196,7 +1212,7 @@ private void ProcessStartOfFrameMarker(int remaining) this.InputProcessor.ReadFull(this.Temp, 0, remaining); // We only support 8-bit precision. - if (this.Temp[0] != 8) + if (this.Temp[0] != SupportedPrecision) { throw new ImageFormatException("Only 8-Bit precision supported."); } @@ -1238,7 +1254,7 @@ private void ProcessStartOfFrameMarker(int remaining) if (h == 3 || v == 3) { - throw new ImageFormatException("Lnsupported subsampling ratio"); + throw new ImageFormatException("Unsupported subsampling ratio"); } switch (this.ComponentCount) diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 61a8cb2127..dd71b70cc9 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -56,5 +56,17 @@ public Image Decode(Configuration configuration, Stream stream) var decoder = new PngDecoderCore(configuration, this); return decoder.Decode(stream); } + + /// + /// Detects the image pixel size from the specified stream. + /// + /// The configuration for the image. + /// The containing image data. + /// The color depth, in number of bits per pixel + public int DetectPixelSize(Configuration configuration, Stream stream) + { + var decoder = new PngDecoderCore(configuration, this); + return decoder.DetectPixelSize(stream); + } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 467d41ff41..7baf35295f 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -261,6 +261,58 @@ public Image Decode(Stream stream) } } + /// + /// Detects the image pixel size from the specified stream. + /// + /// The containing image data. + /// The color depth, in number of bits per pixel + public int DetectPixelSize(Stream stream) + { + this.currentStream = stream; + this.currentStream.Skip(8); + try + { + PngChunk currentChunk; + while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + { + try + { + switch (currentChunk.Type) + { + case PngChunkTypes.Header: + this.ReadHeaderChunk(currentChunk.Data); + this.ValidateHeader(); + this.isEndChunkReached = true; + break; + case PngChunkTypes.End: + this.isEndChunkReached = true; + break; + } + } + finally + { + // Data is rented in ReadChunkData() + if (currentChunk.Data != null) + { + ArrayPool.Shared.Return(currentChunk.Data); + } + } + } + } + finally + { + this.scanline?.Dispose(); + this.previousScanline?.Dispose(); + } + + if (this.header == null) + { + throw new ImageFormatException("PNG Image hasn't header chunk"); + } + + return this.CalculateBitsPerPixel(); + } + /// /// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits. /// @@ -343,6 +395,28 @@ private void InitializeImage(ImageMetaData metadata, out Image i this.scanline = Buffer.CreateClean(this.bytesPerScanline); } + /// + /// Calculates the correct number of bits per pixel for the given color type. + /// + /// The + private int CalculateBitsPerPixel() + { + switch (this.pngColorType) + { + case PngColorType.Grayscale: + case PngColorType.Palette: + return this.header.BitDepth; + case PngColorType.GrayscaleWithAlpha: + return this.header.BitDepth * 2; + case PngColorType.Rgb: + return this.header.BitDepth * 3; + case PngColorType.RgbWithAlpha: + return this.header.BitDepth * 4; + default: + throw new NotSupportedException("Unsupported PNG color type"); + } + } + /// /// Calculates the correct number of bytes per pixel for the given color type. /// diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 1013107062..05a01d825c 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -81,5 +81,19 @@ private static (Image img, IImageFormat format) Decode(Stream st Image img = decoder.Decode(config, stream); return (img, format); } + + /// + /// Detects the image pixel size. + /// + /// The stream. + /// the configuration. + /// + /// The color depth, in number of bits per pixel or null if suitable decoder not found. + /// + private static int? InternalDetectPixelSize(Stream stream, Configuration config) + { + IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat _); + return decoder?.DetectPixelSize(config, stream); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 29d93ae859..032c81c33d 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -39,6 +39,33 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream) return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default)); } + /// + /// By reading the header on the provided stream this calculates the images color depth. + /// + /// The image stream to read the header from. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The color depth, in number of bits per pixel or null if suitable decoder not found + public static int? DetectPixelSize(Stream stream) + { + return DetectPixelSize(null, stream); + } + + /// + /// By reading the header on the provided stream this calculates the images color depth. + /// + /// The configuration. + /// The image stream to read the header from. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The color depth, in number of bits per pixel or null if suitable decoder not found + public static int? DetectPixelSize(Configuration config, Stream stream) + { + return WithSeekableStream(stream, s => InternalDetectPixelSize(s, config ?? Configuration.Default)); + } + /// /// Create a new instance of the class from the given stream. /// diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs new file mode 100644 index 0000000000..a2eaf6df62 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -0,0 +1,29 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests +{ + using System.IO; + + using Xunit; + + public class BmpDecoderTests + { + [Theory] + [InlineData(TestImages.Bmp.Car, 24)] + [InlineData(TestImages.Bmp.F, 24)] + [InlineData(TestImages.Bmp.NegHeight, 24)] + [InlineData(TestImages.Bmp.Bpp8, 8)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 06bfd8990d..c952cd799c 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -6,6 +6,7 @@ // ReSharper disable InconsistentNaming namespace ImageSharp.Tests { + using System.IO; using System.Text; using Xunit; @@ -80,5 +81,19 @@ public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); } } + + [Theory] + [InlineData(TestImages.Gif.Cheers, 8)] + [InlineData(TestImages.Gif.Giphy, 8)] + [InlineData(TestImages.Gif.Rings, 8)] + [InlineData(TestImages.Gif.Trans, 8)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 9401d098de..4a9eb43252 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -152,5 +152,21 @@ public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() Assert.Null(image.MetaData.ExifProfile); } } + + [Theory] + [InlineData(TestImages.Jpeg.Progressive.Progress, 24)] + [InlineData(TestImages.Jpeg.Progressive.Fb, 24)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, 32)] + [InlineData(TestImages.Jpeg.Baseline.Ycck, 32)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg400, 8)] + [InlineData(TestImages.Jpeg.Baseline.Snake, 24)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index ee8a2ee55b..fcc0010ec7 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System.IO; using System.Text; using Xunit; @@ -82,5 +83,22 @@ public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); } } + + [Theory] + [InlineData(TestImages.Png.Bpp1, 1)] + [InlineData(TestImages.Png.Gray4Bpp, 4)] + [InlineData(TestImages.Png.Palette8Bpp, 8)] + [InlineData(TestImages.Png.Pd, 24)] + [InlineData(TestImages.Png.Blur, 32)] + [InlineData(TestImages.Png.Rgb48Bpp, 48)] + [InlineData(TestImages.Png.Rgb48BppInterlaced, 48)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index 59a39c4542..0d0d00957e 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -41,20 +41,20 @@ public DiscoverImageFormatTests() this.fileSystem = new Mock(); - this.LocalConfiguration = new Configuration() + this.LocalConfiguration = new Configuration { FileSystem = this.fileSystem.Object }; this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); - TestFormat.RegisterGloablTestFormat(); + TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker); this.FilePath = Guid.NewGuid().ToString(); this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); - TestFileSystem.RegisterGloablTestFormat(); + TestFileSystem.RegisterGlobalTestFormat(); TestFileSystem.Global.AddFile(this.FilePath, this.DataStream); } @@ -86,7 +86,6 @@ public void DiscoverImageFormatFilePath_WithConfig() Assert.Equal(localImageFormat, type); } - [Fact] public void DiscoverImageFormatStream() { diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index bb64ceda34..bfbdd93f17 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -55,28 +55,28 @@ public ImageLoadTests() this.fileSystem = new Mock(); - this.LocalConfiguration = new Configuration() + this.LocalConfiguration = new Configuration { FileSystem = this.fileSystem.Object }; this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); this.LocalConfiguration.SetDecoder(localImageFormatMock.Object, this.localDecoder.Object); - TestFormat.RegisterGloablTestFormat(); + TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker); this.FilePath = Guid.NewGuid().ToString(); this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); - TestFileSystem.RegisterGloablTestFormat(); + TestFileSystem.RegisterGlobalTestFormat(); TestFileSystem.Global.AddFile(this.FilePath, this.DataStream); } [Fact] public void LoadFromStream() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); @@ -87,7 +87,7 @@ public void LoadFromStream() public void LoadFromNoneSeekableStream() { NoneSeekableStream stream = new NoneSeekableStream(this.DataStream); - Image img = Image.Load(stream); + Image img = Image.Load(stream); Assert.NotNull(img); @@ -112,7 +112,7 @@ public void LoadFromStreamWithType() public void LoadFromStreamWithConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); @@ -138,7 +138,7 @@ public void LoadFromStreamWithTypeAndConfig() public void LoadFromStreamWithDecoder() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object); + Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream)); @@ -158,7 +158,7 @@ public void LoadFromStreamWithTypeAndDecoder() [Fact] public void LoadFromBytes() { - Image img = Image.Load(this.DataStream.ToArray()); + Image img = Image.Load(this.DataStream.ToArray()); Assert.NotNull(img); @@ -182,7 +182,7 @@ public void LoadFromBytesWithType() [Fact] public void LoadFromBytesWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); @@ -207,7 +207,7 @@ public void LoadFromBytesWithTypeAndConfig() [Fact] public void LoadFromBytesWithDecoder() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny())); @@ -228,7 +228,7 @@ public void LoadFromBytesWithTypeAndDecoder() [Fact] public void LoadFromFile() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); @@ -251,7 +251,7 @@ public void LoadFromFileWithType() [Fact] public void LoadFromFileWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); @@ -273,7 +273,7 @@ public void LoadFromFileWithTypeAndConfig() [Fact] public void LoadFromFileWithDecoder() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object); + Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream)); diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index d43b989f10..8759704415 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -22,7 +22,7 @@ public class TestFileSystem : ImageSharp.IO.IFileSystem public static TestFileSystem Global { get; } = new TestFileSystem(); - public static void RegisterGloablTestFormat() + public static void RegisterGlobalTestFormat() { Configuration.Default.FileSystem = Global; } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 5a3cd102e7..6c2bca3678 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -23,15 +23,15 @@ public class TestFormat : IConfigurationModule, IImageFormat { public static TestFormat GlobalTestFormat { get; } = new TestFormat(); - public static void RegisterGloablTestFormat() + public static void RegisterGlobalTestFormat() { Configuration.Default.Configure(GlobalTestFormat); } public TestFormat() { - this.Encoder = new TestEncoder(this); ; - this.Decoder = new TestDecoder(this); ; + this.Encoder = new TestEncoder(this); + this.Decoder = new TestDecoder(this); } public List DecodeCalls { get; } = new List(); @@ -200,10 +200,15 @@ public Image Decode(Configuration config, Stream stream) where T config = config }); - // TODO record this happend so we an verify it. + // TODO record this happend so we can verify it. return this.testFormat.Sample(); } + public int DetectPixelSize(Configuration configuration, Stream stream) + { + throw new NotImplementedException(); + } + public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); } @@ -222,7 +227,7 @@ public TestEncoder(TestFormat testFormat) public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - // TODO record this happend so we an verify it. + // TODO record this happend so we can verify it. } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3479457cf8..53e6ae1eff 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -25,6 +25,9 @@ public static class Png public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; + public const string Palette8Bpp = "Png/Palette-8bpp.png"; + public const string Bpp1 = "Png/bpp1.png"; + public const string Gray4Bpp = "Png/gray_4bpp.png"; public const string Rgb48Bpp = "Png/rgb-48bpp.png"; public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png"; @@ -108,8 +111,9 @@ public static class Bmp { public const string Car = "Bmp/Car.bmp"; public const string F = "Bmp/F.bmp"; + public const string Bpp8 = "Bmp/bpp8.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; - public static readonly string[] All = { Car, F, NegHeight }; + public static readonly string[] All = { Car, F, NegHeight, Bpp8 }; } public static class Gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/bpp8.bmp b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/bpp8.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8ea6f4acd7ccd80842f01fc6d1900a93e9a3863f GIT binary patch literal 65002 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 literal 0 HcmV?d00001 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/bpp1.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/bpp1.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac2c1ee93eb66016e4b45ccb8b498b14d45fdeb GIT binary patch literal 4403 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 WZzy_mMCje0OLa84o3(T zLQy!JDTKqB%(9$>{X$-o*oA#i3ixpa(W|5d8&dN#alsj@(lTkGD^s&FqcT%7Hm&K& z48Uf=qHqa+e4-h~$2H^Q((!T5_&9FDG8o1UnsI}4+`t((;3hgXhH;H%Tq7OVaK<&b ziLp|{xKuMPm5xg}<5JwjRE}YsqZ#K&$2pvF4h};a#~H?PnsJCfs6{Mq|)O4H}L?gPUN1bxJh`snj6l7^JufQrK9I#=wyp zI2;29hjGNF8gLo|PHMn$3^>dy#u@{|NMkH9T38?E7{kV(F)VB{ZbHz6jWwx8!_jDP z6Z$Z>m?f2Jq#TVDHz5~efpv1E8V*Op!C~q#QrK7xPO8CiG&oEi77vUjHX92ErXM4L zDa2S~U>IqPB{me}fjP#oF=z}6HxapsZp_9SM=Hfl1QOGSxy39FN6NuramD0fEU-=~ zjw8ikal|5x1pwoP4aWkFsl`Hx#RH>>&Bnrkal!;+3Nh9g7)BbKiP6G%V2&|t3>r6) zqKVO1H)dlEhh+>4B&H8@i&-43jQ*>J3Az6z7|ZBHKL1P71gZbxK9S!4LOH?lzvxV4 z`#-@Gl>gH(k^KK?O(^)!69$c8VOhe0hiSrG*~o1NaX8x3P$*ckaq!LH*`<<~)N3D| z6jS6T-X(%d%e#e>s{96vrYmOT9HUqH0Ytkx3+jqvDtKdrg5cM?Y*KQs3pFyF+xX1Y z%DQs=mge6#H-6u=_vMzy=d)x#ll(bdr>EWc8Gm7e#hTrxN8guzZKB=kj?{j2W=;vY zc6sJ{QiyYteU#&=rUtR&|3JvYb7QAZS=PN zi*?9Qsk}j~-wc&O`?~gJe*J!ckrEIho(*_;=D zaBs{0G0D`ryKe-p$mKhGBXR?ZM2fVKDqRC+>U*jH|kYn z4A>Nn@X%VClViH&_48-cI*Utiy1D{lpHs>@Ux-be4N9xNxdGDvLxcGr;F?)tg_ z&5PkvhZGkM*dL)2n=|`1lD@gPo|8IWdwMJ?{ExAm`L?;E7OvfGEE~2;_$$mM7D|l& zc{}cF$q#wG7I;c_{V~71LbV`4{Jji`qdPN;cTL?vVTX+XBUTLgR#n{GS9eAe=~h}t zB-0kOz)HmNNz+nN=<^Sgc&WD6eQK>nS*AZLR+y@oU*kT`>sp&uv8H7s-uG1$F)Bh; zBR17Ae;DOy9@b#UD7lp0zOOZ@ha`I_m!zvN&J;*C%R6@8pDye_kS|)W{Zj92b9w^; zl}r_j7mPN9jh|jHeq$GNq|3M8%w;%c>}iEHAF=ZcUQ(^QPxd{qD(d;{jHjxYAakL4 zgvvCZGPs}7@)yGamOl{x(E$l#vWzw=<^%CzezNC2Whz$WOuDvoA|Uw zc~2w|7l~%5;>4M=v00O&)~+3y-Y*Ihf4&+>KSdXWF5!Y_%gUhW4#OPyu-+u#K*r~5 z8`W`^n`UjvBP%cV9!W|AefV|%TR{&~AEBw6ndfBijsE@&DEnSk(-t>|i+^vuIYC95 zf9yb=4_(JxF8jN_QE6p$2JlRoEgJq68?~SH8*IBDZ`p0s6JNvqEsc!^t4#bBHy0O) zqzAE-v&18^enoXzo_+vr4%roFnMuebd!`(T>Bx88&hSE@N}DJjxuF*g3Y`(Mgs{cU zdw5$s)>nft`28!`vAmG`=9|1#)hBi%&u{!Gk3FLHXzM6tiKoD3ifYG794Z!dP{RI9 zthYh0*Comw6{mf?NXtmY+UC;h{ng=$$ecTR? zrCw5=L59G7N8Wy^-rNexKDAXInAmebX9RrbRva)2Ps^&QGu>RgTAGj;0IKr?T(5VF z1gJ_xbjmc|_tw$dkr*$2-L(IH4?|S|@uOwfWNyp+-q`juNf}?wvIB1xx@N5<*J+EG z-t;OYW-BC+_rkWB!MZbl*sU-g7Del2)tm0z1T2ZW7y$@Gp^b!B_zs^WJ;HCIuz`c5 zb{@NJ76rPQ>M+tmygoLtRAqbJ9Vr@USE|gmOsPF`vmBqT9{Uz@- z;y#sOAobXzfa}A2sBm+=e3JM8D-WD6jI|=;Gf8g+w6?Z3BvRF&GC>9s0)j%&w@PO; z-VI9G^3S?9z;ZwFZLF(XcbjF6vAN(-C?D2E*OI}MvqRH{IqDU2?Zq*5IAVf$KM|?1 zZcn~>l@$f%@w3`#T|9r8Z^4gwapF1=8rFe?StB*3?XAd|>ry(k3=P+_DDF6&`wJUo08~GPbvwwfo#>%wa8 zy{}aE9Z$EA;iAie%_Qob!+(D}#tsAJ4@He=d?1vPzF@<UvI01vty{B1oUGcR zJMs%A*&qY$?d@W_Foyq)3d=cqr)tq-rL}mm-+KPQK;6K>ziMnF*7=HwJGHUftNl|e zk0&m!?>sIu?shpxFGuR9)iHh)*ROweqvvr*i|)?>H|9QVu?ZVVOr_Qeqp1EzT+4PN zb+sRf^`OMb3W;5X(x?Elb6R)}JXu-PccEYAB2P#tkjI*kV-Dpf)__at;7hMdc&B@|4Cm~DLC4!oNJ#e!%XF5`*yt#BXF@M~e=c5SoOe+vQc2})^i9N= z3OUB25sBEXH94lmBipvK<9!N#{n*s5-G@xGXOttk*%5y;pR#jf$ky83^d@Av7qnIA zhud$pXR4R~5wM?OrO>%ioW$$a(2(enH(aQIP^>5Pw9=0@KchmPqeb%ETtIFHw}HHV zYY#kc1{4R*5B0TLd>M3X~RepipT zRY`zswh&)TG$)bqL=u}AGkqpv*QS$M9_ULhYw}+Ohz+bMsU3z}=EoxEkJy^K$ z*vMdF2EPy*5P{1lwbzTRw~LBYeZ7P|^hL-lmlN98qXlKwaA%2@G;o&zgxZ?`Ipp@3 ztHs8$;AP+@{ud%VBjD>^*w{IHzt$!nJiTiET_2VYf9&J6^o^Q%c3M6V42t~SmOCIwQ;(drapxzs!U?&{ zmmiOofRM!VBPb!A)GCl-6A+rJhR;=Rv`iYGT}Kk{WYCa>e|}|K`|z0G=k+E)8=;vS zOYFlu7*kYJZExeB+c8FbLOQY{Ju4XYNN%Mw7a=v@WdVc--lQOPYWG|)A1Y01m49wQ z*lm}X38&tr^OvjM5}JA|wOaalZ42Wt-2};aD1%@NU$|3ljdm6&xOaM^-4Ts^P>Nr9r>Cv$gV?|;B$A!n8r(4=@?gI&i(}s=T zyqJT#JkH$%vD(}U)*O?~U=hAiR0MMpp#)fGVo71Q5t#`ytDIH!#8X=O9&L!DEC5ba z;w`@cgcl5t!9PNdWD}BA%>&I#Awi3@J5PU`1sjRCdMCl!SuS?GWTsf&JP2kA?*fl}RK5B+NS$jHcDSK`1xd0Y1TWP7Fu zwjG0HGqiX*@Uvk%q_4_e-5ZY1@MOSs?%iVo1Ldptz8#Q0>IE`jYnhL-q#;hGTP6f6<+`)LO8LlZ6+H4L5={=m*7H(4 z8d<^u$0qTVC93XTS_2R&-V$hrbfyK3U74P8-?v~#wtiBj33|C=8DwWyn$3Sytb|8i z=fESgm?ab+DYr#Xup^K4()jzGFVd-*l>{wuoy}!d+tCX5Q6|D33dtB2X6X-EE@urq zyC#EYFn}fDSdFIP+2l>cfqyU4uwbIokE{T~9QtVAt%{7t^~6$bMHF@?;cI5 z5s@It(g0XkEu+DCjZ?&duVFVXY_Ucb^jk-AnQlg@z7;EFZZo|>y9*DJ3dTy!F{*YA z?2)?%LOQnoEbJ#$0t4|NYS^`pj}69WsPkn2c>H_Es+lK<1FVfqZ5sJ9x~_3Ej414WscxYfL{O z;$ZgA!ov&!v%sv89cMG3@90A9S8NJh=9(PeX|BMb9D z2K3KEII!nb>Li$0K`(CB(y5GCFc69tLXh1&=<=gSiHf^NuX)rm`q4NWwlKTJ(@eth`X^N&3mxS*F z2w6i>WycvGu;65)3 zh+;1-OmK|hh~SvBL7u<|yrA5QugZc+CIDgT+6i)| zjJA8ULG{fU3}uw1#(F934~qhkMzL{DQ)l*(Oz_k9(c|m@}^?@=E_tD zxbw?frblkJkjY@BL27A6vFH|YHJnk6$Hxb8=g$u?+%^vP>`37A0?$OjRD|E*TxMjj zJLt}>*ZgF{3S*@jutdEYfn;&%0WM<--va*J#8Ns=%&` zmTUPU<2~5GAS2QB_$5n-6|77>`4YfEdRCm1N9={zxxh}aOwwH~ybQ0%mIpwQe7-E( zw%qc%aX7S&F~0n@%)$wg8G$|5rI$rYqMxUe(GlakD_>ATEu8 zaGj)K zchet`d`C!|0N-P@!QEA`{x*C`6ii$Ovt(xKIADpj!wkRG1yOZz;^qj|baCmWO)ow( z^UeH*V+t@xQh#ssBrq>@5r@{eJ0pmz@N${odkm%;qe;N<+OnYG_1)>LL&ELGgYyx* z3lof#lpTXdt0Q=}tlSm->@;2ncD-_Q*MNLA9O3QIXnjX?;>p&3Z(5=)?j^{M-V!KaIo9V`NXOg1`|Vax2Bc2k6K4D2Nll9EUacj z`TkJk8Db^-mD$2=zc$w{Mqv*G@v}xOyb#aF{c=k<{(Zq6>pZMfO4Ti(9#xy7+lRc=;h?Z4yVlK8FD%ue zB=qiPGsskcgLa6<*B5sf1|kHZGJb5P`Ii z=mcm^H@uOs21``8JN1&eq~9XSjMw2wc0krMki{=#K8jfm*e#c3AdL7iAwppARd{fr z36Nx)naA3XoiY=zX?{a3*$a`Ae&vY33t4%j09GpougF)+NY$;a`{3jc<71P@7cCG$f3Z>C;!{>8|^&Zym>v5iO?4{v$@E*=3@kJM&sTY z-jS*L8sQN=@mpvJ)L~w&dxTRh?&~L6pzkmIPFmwI#2a&5##+Iz^5Iv%Y;XDNlB}YvXRB7@*epFja|q%EW0l= z&MyOO5aJm+4!LTIyhTn@ScCcdoJ2>%i6tWcN#J;CP#UNEICSUTVfIKcE1$Yy#m@a9 zE|LP;bTgHbSgVXeZRR86XVQW0O^pjD6NiGi3|})*NBXtuw-F;U8?-nG2c3t&Q|B~z zRahS<7L>uQr4PjYcbKum&vq;+&ptZ?jjf~YK^`ZPpi`z4f(p39S)qXUUdDT~sV0nE z8{Boej|ORv3M>pcZ_DR`ybvzbQ_R+`+g`J7I~N{229xAdI{kyv`v3BfWMpGG^@rv>UHE(b z%M2@Ytl+e~un>nGK1I<5z4uJ4o0E?-K;CRV1kU;Rz-!Dg0{#TOepSr!0XsZdu)pAZ z8vm+n6kEj`2>T1`6iz5Npw)Bal?iKrJ&KbXT{seHnLBJvuj2bULtF z*lrZf=I|#Em-KzK4|8YWi3jL(S3I4I%O0m2|ItcWToJQC87NI+P)UY6A6L3D*Svrl7dD#-7HNNmN1N)2Pk+;EM0YBwM z6oZ=SJ@$f-2PY5C9dlnY2?NOT%Nk#0H@F8!3BeMji@e>cvCr*xPY$EiijrmNwKX_{ZFHZ!GgG6 zx)QlFv|29MFNZ;OR0{>^#S;MvyRp?K%45X%ft}ABG0xlE&}!^(KaGe}USNYdB!nKP zBn#J={-;Zahks=7X#Qp&nzLFU?)7EHo6h_+xI~|*dV*%MuuGvS7Z_O;FPRl$c|>U` zn_9>2&y_x<{S2l!uU>G`4N+e;*=A28gL3hraWB6s$q40`J4d;z^z%e zNv$ri{ZFTs8IK8p1T?3eL1BO(ML={w=!Gk@XHH@{>_C2PwScx9nMbaYrw5N!SfV$) z2;~a>%ZfL8Ya}q`K)ijA)qaw<{r6^waNf;1rPiMG&t%d!;2`-QEB~8a*%I{4tOp_! zv}v25uwF$x|AGYNW9vdBui$j#V2*|0$>wIrOxoh5cK7E!r8SyJTXc);sHldM^?jE6 zcegq!dRU{Kg)CZp8{naM#w5NAl;!hJeA?KOz&bk6JhzdazrgwdwhiNa)ycn?%DM2Z z;n#m4Mu!c#&>t^A$uiOr+F4ktQ<46eUdtE(Oit`G78EHVjFyC$4f`m<^vVQO5pTw4 zL0Pw!0Z%DOXGWKJ`6BUd;I?0Nv!LLUz|Dq=rTtHwGhBEDg2WW(=4Owj%jDAY8fR%d zxH04?!W$c`r>U8AWUC4CtNPb3&S(QEiQTf4YZGJUj9dsPr|Elpm;YY$LH_+;bNTLb zP1S~wB|r`(>EnTD?gAUhm_ROEX$id=ID|cdFFOgu7lf)EpYJ^VuG28en?1Vv71FF? z#YJ#z=eeD{@T1B675etG85py`2vINMJRS4 z_xme+4U0P6ql+=>MJ*LXb8Uo>aD78Ox1}$`cmO6<9Y}|`GW<=;jO@>k96kUKCQFpV zL*2nTA*pIH=_zr~D+kb~#Cw#{xy2!km2{kvzteP?ab`H=E_n9L7JFRh@|PY>c4AIm zTCjkuz)nh#?8SMKaI2h;o(?BkqXR3_?@}K9k02U*w?{<8_+IvExit29rI^)-Qbs*Y znVlnfD-89_8v38ehP~KJYT1jIm-$eR)Ga~qEQ-&7e!4G8^}?JZ#yxSw=U%&Nh_A?1 zH8<>Yh)LwRQZf4WFM$94u@(6#;pXj~Wm+Ajs%9~EDoKjr`bm4xJqu3J<&Ul0*Md(O zji`M-+mqak_++y5ACEoAdY(55UPEQ5 z8K7Xp`^V?kPHq15_sXtrH;%Z4ES zm#ED5@1pwX32`Fob+-Fj9-Y=`moZ)3cx$29pqK{9AIhff?4DAN%b9XmSe)(HxuD2E zIfFMRZNl#-ds^{g$v1m=3~JU#FwMoG`QhuA=46CVN51)RGj5{0-KRlcbebBpM5ubw zJJhu83cSL~mW{1ISzX``mt`G)hf1?QzIR=tj!)-Kwk*!xeQ#>57(j=3N|c8~pY+r# z=~Cg^WBcIR+rytr(@9XrY2#8Czu{=fyE1#q%}aL}*B5@=`=@1fpI}g!$^>_9`te5sV*ngE<4WASJlvPjKAbGr74_PJuOD|>)Xqw{Vbt(=-MB_ z0J#+5iwB*)&-|;_b?oyN?fm7~o4QBa?~1L83E*)=?aXCI5&2 zJp4CVD0^h(M?&xKw|IOKqxtu!U|T*E{A-Q%j18HJipbj1-wW@{f7N$$!4r=UZ2kRH za+iVZxn)5>wG!qzHoLT3i0L6v>`HcSTUmQy&XOQ$wd0Qzt4h&GehKL#CG8Bb>eJl! z2ai1tNnpXeRP}{p2_fOKuG0aU^R{;iQv%9M^?uCv&wn~Zf3$XQ?+VI5RYt~NTvDmG zbhzQ%pEl+|ml)5YhE+c}rJ48h;hn@adEjwic1ZQNFCMRIe~D|R-V(o==@a4z!;SE& zW&`00tATQy5$0nn3QP7oML98}4zU-hs~d>Vw|#w;{I2n5dh^C|`E^zL->xg&=7j9v z3y(L6=;ogD;mYDdtE#B$k>RZG;(Ybu%NGSDJ-584+gPuwErXG-QE5EaKw(cxg!g9G z_+~Trv-T Date: Fri, 18 Aug 2017 09:45:02 +0700 Subject: [PATCH 02/11] fixes on code review --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 4 ++-- src/ImageSharp/Formats/Gif/GifDecoder.cs | 5 +++-- src/ImageSharp/Formats/IImageDecoder.cs | 4 ++-- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 4 ++-- src/ImageSharp/Formats/PixelTypeInfo.cs | 22 +++++++++++++++++++ src/ImageSharp/Formats/Png/PngDecoder.cs | 4 ++-- src/ImageSharp/Image/Image.Decode.cs | 6 ++--- src/ImageSharp/Image/Image.FromStream.cs | 16 +++++++++----- .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Gif/GifDecoderTests.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 2 +- .../Formats/Png/PngDecoderTests.cs | 2 +- tests/ImageSharp.Tests/TestFormat.cs | 2 +- 13 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 src/ImageSharp/Formats/PixelTypeInfo.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index bdd15c2d71..ee6676f5a1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -39,14 +39,14 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public int DetectPixelSize(Configuration configuration, Stream stream) + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); byte[] buffer = new byte[2]; stream.Skip(28); stream.Read(buffer, 0, 2); - return BitConverter.ToInt16(buffer, 0); + return new PixelTypeInfo(BitConverter.ToInt16(buffer, 0)); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 4d847c9fe7..63eb2eaf42 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -35,14 +35,15 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public int DetectPixelSize(Configuration configuration, Stream stream) + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); byte[] buffer = new byte[1]; stream.Skip(10); // Skip the identifier and size stream.Read(buffer, 0, 1); // Skip the identifier and size - return (buffer[0] & 0x07) + 1; // The lowest 3 bits represent the bit depth minus 1 + int bitsPerPixel = (buffer[0] & 0x07) + 1; // The lowest 3 bits represent the bit depth minus 1 + return new PixelTypeInfo(bitsPerPixel); } } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index a16ef2612c..6befafb39e 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -31,7 +31,7 @@ Image Decode(Configuration configuration, Stream stream) /// /// The configuration for the image. /// The containing image data. - /// The color depth, in number of bits per pixel - int DetectPixelSize(Configuration configuration, Stream stream); + /// The object + PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 8bdbdfe0cf..66e303c01c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -34,13 +34,13 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public int DetectPixelSize(Configuration configuration, Stream stream) + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this)) { - return decoder.DetectPixelSize(stream); + return new PixelTypeInfo(decoder.DetectPixelSize(stream)); } } } diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs new file mode 100644 index 0000000000..c331543515 --- /dev/null +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -0,0 +1,22 @@ +namespace ImageSharp.Formats +{ + /// + /// Stores information about pixel + /// + public class PixelTypeInfo + { + /// + /// Initializes a new instance of the class. + /// + /// color depth, in number of bits per pixel + internal PixelTypeInfo(int bitsPerPixel) + { + this.BitsPerPixel = bitsPerPixel; + } + + /// + /// Gets color depth, in number of bits per pixel + /// + public int BitsPerPixel { get; } + } +} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index dd71b70cc9..b7ffe8e72d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -63,10 +63,10 @@ public Image Decode(Configuration configuration, Stream stream) /// The configuration for the image. /// The containing image data. /// The color depth, in number of bits per pixel - public int DetectPixelSize(Configuration configuration, Stream stream) + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { var decoder = new PngDecoderCore(configuration, this); - return decoder.DetectPixelSize(stream); + return new PixelTypeInfo(decoder.DetectPixelSize(stream)); } } } diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 05a01d825c..313224c5fa 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -88,12 +88,12 @@ private static (Image img, IImageFormat format) Decode(Stream st /// The stream. /// the configuration. /// - /// The color depth, in number of bits per pixel or null if suitable decoder not found. + /// The or null if suitable decoder not found. /// - private static int? InternalDetectPixelSize(Stream stream, Configuration config) + private static PixelTypeInfo InternalDetectPixelType(Stream stream, Configuration config) { IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat _); - return decoder?.DetectPixelSize(config, stream); + return decoder?.DetectPixelType(config, stream); } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 032c81c33d..e84d89a912 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -46,10 +46,12 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream) /// /// Thrown if the stream is not readable nor seekable. /// - /// The color depth, in number of bits per pixel or null if suitable decoder not found - public static int? DetectPixelSize(Stream stream) + /// + /// The or null if suitable decoder not found. + /// + public static PixelTypeInfo DetectPixelType(Stream stream) { - return DetectPixelSize(null, stream); + return DetectPixelType(null, stream); } /// @@ -60,10 +62,12 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream) /// /// Thrown if the stream is not readable nor seekable. /// - /// The color depth, in number of bits per pixel or null if suitable decoder not found - public static int? DetectPixelSize(Configuration config, Stream stream) + /// + /// The or null if suitable decoder not found. + /// + public static PixelTypeInfo DetectPixelType(Configuration config, Stream stream) { - return WithSeekableStream(stream, s => InternalDetectPixelSize(s, config ?? Configuration.Default)); + return WithSeekableStream(stream, s => InternalDetectPixelType(s, config ?? Configuration.Default)); } /// diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index a2eaf6df62..84edca0014 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -22,7 +22,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); } } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index c952cd799c..b7af4525ec 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -92,7 +92,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 4a9eb43252..4ac7c5c5b5 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -165,7 +165,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index c7147b2261..f9b34e7c7f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -97,7 +97,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelSize(stream)); + Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 6c2bca3678..c760b9b98f 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -204,7 +204,7 @@ public Image Decode(Configuration config, Stream stream) where T return this.testFormat.Sample(); } - public int DetectPixelSize(Configuration configuration, Stream stream) + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { throw new NotImplementedException(); } From ebe664cc7a698a1518d1df57bd7849d20f9b29ad Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Thu, 24 Aug 2017 16:16:23 +0700 Subject: [PATCH 03/11] update packages --- src/ImageSharp/ImageSharp.csproj | 8 ++++---- tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 2 +- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 6 +++--- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8733131a74..2748275556 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -38,13 +38,13 @@ All - + - - + + - + ..\..\ImageSharp.ruleset diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 72593a0da4..6228afd9ea 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 0537ffda0e..7b9d28f039 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -3,10 +3,10 @@ // Licensed under the Apache License, Version 2.0. // -using ImageSharp.Formats; - namespace ImageSharp.Tests { + using System.IO; + using ImageSharp.PixelFormats; using Xunit; @@ -38,4 +38,4 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index b0429d9ede..bd20ba7c1c 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -10,7 +10,7 @@ - + From d6a2d40a80a60e0eb25861d7714f4cfd5b8cc90d Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Thu, 24 Aug 2017 17:36:40 +0700 Subject: [PATCH 04/11] fix test image filename --- tests/ImageSharp.Tests/TestImages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c80fde6d07..8960de8868 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -25,7 +25,7 @@ public static class Png public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; - public const string Palette8Bpp = "Png/Palette-8bpp.png"; + public const string Palette8Bpp = "Png/palette-8bpp.png"; public const string Bpp1 = "Png/bpp1.png"; public const string Gray4Bpp = "Png/gray_4bpp.png"; public const string Rgb48Bpp = "Png/rgb-48bpp.png"; From 93419ab0e4be428d1e1c81f8a65d31f2b29bdb17 Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Tue, 26 Sep 2017 16:58:11 +0700 Subject: [PATCH 05/11] fix bugs after merge --- .../Formats/Jpeg/GolangPort/OrigJpegDecoder.cs | 11 +++++++++++ src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 3 +-- .../Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs | 6 ++++++ src/ImageSharp/Formats/PixelTypeInfo.cs | 2 +- .../ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 2 ++ .../ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs | 2 ++ .../SystemDrawingReferenceDecoder.cs | 8 ++++++++ .../TestUtilities/Tests/TestImageProviderTests.cs | 10 ++++++++++ .../Formats => Images/Input}/Bmp/bpp8.bmp | Bin .../Formats => Images/Input}/Png/bpp1.png | Bin .../Formats => Images/Input}/Png/gray_4bpp.png | Bin .../Formats => Images/Input}/Png/palette-8bpp.png | Bin 12 files changed, 41 insertions(+), 3 deletions(-) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Bmp/bpp8.bmp (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Png/bpp1.png (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Png/gray_4bpp.png (100%) rename tests/{ImageSharp.Tests/TestImages/Formats => Images/Input}/Png/palette-8bpp.png (100%) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs index 13be70e30b..97a424c415 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs @@ -27,5 +27,16 @@ public Image Decode(Configuration configuration, Stream stream) return decoder.Decode(stream); } } + + /// + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + using (var decoder = new OrigJpegDecoderCore(configuration, this)) + { + return new PixelTypeInfo(decoder.DetectPixelSize(stream)); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index d4ec048d32..6845314c1c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -4,7 +4,6 @@ using System.IO; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -36,7 +35,7 @@ public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); - using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this)) + using (var decoder = new OrigJpegDecoderCore(configuration, this)) { return new PixelTypeInfo(decoder.DetectPixelSize(stream)); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs index 37ce0151f3..1458d54e5f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs @@ -27,5 +27,11 @@ public Image Decode(Configuration configuration, Stream stream) return decoder.Decode(stream); } } + + /// + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index c331543515..d4b72a00b4 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats { /// /// Stores information about pixel diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index c6598fab81..b6f894386b 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -8,6 +8,8 @@ namespace SixLabors.ImageSharp.Tests { + using System.IO; + using SixLabors.ImageSharp.Formats.Bmp; public class BmpDecoderTests : FileTestBase diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index f57b0f128d..a13730eb6c 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -11,6 +11,8 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { + using System.IO; + public class GifDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 23ff61eb3d..d36fd2a14d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -45,5 +45,13 @@ public Image Decode(Configuration configuration, Stream stream) } } } + + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + { + using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + { + return new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 88d7fa2b86..24becce2ed 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -83,6 +83,11 @@ public Image Decode(Configuration configuration, Stream stream) return new Image(42, 42); } + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + { + throw new NotImplementedException(); + } + // Couldn't make xUnit happy without this hackery: private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); @@ -145,6 +150,11 @@ public Image Decode(Configuration configuration, Stream stream) return new Image(42, 42); } + public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + { + throw new NotImplementedException(); + } + private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/bpp8.bmp b/tests/Images/Input/Bmp/bpp8.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/bpp8.bmp rename to tests/Images/Input/Bmp/bpp8.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/bpp1.png b/tests/Images/Input/Png/bpp1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/bpp1.png rename to tests/Images/Input/Png/bpp1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/gray_4bpp.png b/tests/Images/Input/Png/gray_4bpp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/gray_4bpp.png rename to tests/Images/Input/Png/gray_4bpp.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/palette-8bpp.png b/tests/Images/Input/Png/palette-8bpp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/palette-8bpp.png rename to tests/Images/Input/Png/palette-8bpp.png From 4a59c6ff1468c738d40ad496909e3e96d587832c Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Tue, 26 Sep 2017 17:05:38 +0700 Subject: [PATCH 06/11] remove duplicates in .csproj files --- src/ImageSharp/ImageSharp.csproj | 1 - tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 509270cccb..45d0a70b81 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -40,7 +40,6 @@ All - diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index b016fb3bae..e8a6e8c596 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -16,10 +16,6 @@ - - - - From a631a64dfb5a36e2a2087d7beac75c7be870a554 Mon Sep 17 00:00:00 2001 From: denisivan0v Date: Wed, 17 Jan 2018 16:55:53 +0700 Subject: [PATCH 07/11] Added an API to read base image information without decoding it - intoduced base IImage interface - introduced IImageInfoDetector interface - Image.DetectPixelType method expanded to Identify method that returns IImage --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 11 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 134 +++++++------ src/ImageSharp/Formats/Gif/GifDecoder.cs | 17 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 182 +++++++++++++----- .../Sections/GifLogicalScreenDescriptor.cs | 5 + src/ImageSharp/Formats/IImageDecoder.cs | 10 - src/ImageSharp/Formats/IImageInfoDetector.cs | 21 ++ .../Jpeg/GolangPort/OrigJpegDecoder.cs | 6 +- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 14 +- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 6 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoder.cs | 6 - .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/PixelTypeInfo.cs | 6 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 15 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 69 ++++--- src/ImageSharp/Image/IImage.cs | 31 +++ src/ImageSharp/Image/Image.Decode.cs | 10 +- src/ImageSharp/Image/Image.FromStream.cs | 16 +- src/ImageSharp/Image/ImageFrameCollection.cs | 4 +- src/ImageSharp/Image/ImageInfo.cs | 24 +++ src/ImageSharp/Image/Image{TPixel}.cs | 31 +-- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Gif/GifDecoderTests.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 2 +- .../Formats/Png/PngDecoderTests.cs | 2 +- tests/ImageSharp.Tests/TestFormat.cs | 5 - .../SystemDrawingReferenceDecoder.cs | 21 +- .../Tests/TestImageProviderTests.cs | 10 - 29 files changed, 418 insertions(+), 248 deletions(-) create mode 100644 src/ImageSharp/Formats/IImageInfoDetector.cs create mode 100644 src/ImageSharp/Image/IImage.cs create mode 100644 src/ImageSharp/Image/ImageInfo.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index b976d66797..e252e63406 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.PixelFormats; @@ -23,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Formats will be supported in a later releases. We advise always /// to use only 24 Bit Windows bitmaps. /// - public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions + public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { /// public Image Decode(Configuration configuration, Stream stream) @@ -36,14 +34,11 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + public IImage Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); - byte[] buffer = new byte[2]; - stream.Skip(28); - stream.Read(buffer, 0, 2); - return new PixelTypeInfo(BitConverter.ToInt16(buffer, 0)); + return new BmpDecoderCore(configuration, this).Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 04176e0333..ef9e761643 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Bmp @@ -94,62 +95,9 @@ public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) public Image Decode(Stream stream) where TPixel : struct, IPixel { - this.currentStream = stream; - try { - this.ReadFileHeader(); - this.ReadInfoHeader(); - - // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 - // If the height is negative, then this is a Windows bitmap whose origin - // is the upper-left corner and not the lower-left.The inverted flag - // indicates a lower-left origin.Our code will be outputting an - // upper-left origin pixel array. - bool inverted = false; - if (this.infoHeader.Height < 0) - { - inverted = true; - this.infoHeader.Height = -this.infoHeader.Height; - } - - int colorMapSize = -1; - - if (this.infoHeader.ClrUsed == 0) - { - if (this.infoHeader.BitsPerPixel == 1 || - this.infoHeader.BitsPerPixel == 4 || - this.infoHeader.BitsPerPixel == 8) - { - colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; - } - } - else - { - colorMapSize = this.infoHeader.ClrUsed * 4; - } - - byte[] palette = null; - - if (colorMapSize > 0) - { - // 256 * 4 - if (colorMapSize > 1024) - { - throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); - } - - palette = new byte[colorMapSize]; - - this.currentStream.Read(palette, 0, colorMapSize); - } - - if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue) - { - throw new ArgumentOutOfRangeException( - $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " - + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'"); - } + this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); using (PixelAccessor pixels = image.Lock()) @@ -192,6 +140,16 @@ public Image Decode(Stream stream) } } + /// + /// Reads the image base information from the specified stream. + /// + /// The containing image data. + public IImage Identify(Stream stream) + { + this.ReadImageHeaders(stream, out _, out _); + return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData()); + } + /// /// Returns the y- value based on the given height. /// @@ -624,5 +582,73 @@ private void ReadFileHeader() Offset = BitConverter.ToInt32(data, 10) }; } + + /// + /// Reads the and from the stream and sets the corresponding fields. + /// + private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette) + { + this.currentStream = stream; + + try + { + this.ReadFileHeader(); + this.ReadInfoHeader(); + + // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 + // If the height is negative, then this is a Windows bitmap whose origin + // is the upper-left corner and not the lower-left.The inverted flag + // indicates a lower-left origin.Our code will be outputting an + // upper-left origin pixel array. + inverted = false; + if (this.infoHeader.Height < 0) + { + inverted = true; + this.infoHeader.Height = -this.infoHeader.Height; + } + + int colorMapSize = -1; + + if (this.infoHeader.ClrUsed == 0) + { + if (this.infoHeader.BitsPerPixel == 1 || + this.infoHeader.BitsPerPixel == 4 || + this.infoHeader.BitsPerPixel == 8) + { + colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; + } + } + else + { + colorMapSize = this.infoHeader.ClrUsed * 4; + } + + palette = null; + + if (colorMapSize > 0) + { + // 256 * 4 + if (colorMapSize > 1024) + { + throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); + } + + palette = new byte[colorMapSize]; + + this.currentStream.Read(palette, 0, colorMapSize); + } + + if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue) + { + throw new ArgumentOutOfRangeException( + $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " + + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'"); + } + } + catch (IndexOutOfRangeException e) + { + throw new ImageFormatException("Bitmap does not have a valid format.", e); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 20bebb1d34..ccb6cf92c5 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; using System.IO; using System.Text; using SixLabors.ImageSharp.PixelFormats; @@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions + public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -33,20 +31,17 @@ public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { - var decoder = new GifDecoderCore(configuration, this); - return decoder.Decode(stream); + var decoder = new GifDecoderCore(configuration, this); + return decoder.Decode(stream); } /// - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + public IImage Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); - byte[] buffer = new byte[1]; - stream.Skip(10); // Skip the identifier and size - stream.Read(buffer, 0, 1); // Skip the identifier and size - int bitsPerPixel = (buffer[0] & 0x07) + 1; // The lowest 3 bits represent the bit depth minus 1 - return new PixelTypeInfo(bitsPerPixel); + var decoder = new GifDecoderCore(configuration, this); + return decoder.Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index ae20be7d5d..dbca49f068 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Performs the gif decoding operation. /// - /// The pixel format. - internal sealed class GifDecoderCore - where TPixel : struct, IPixel + internal sealed class GifDecoderCore { /// /// The temp buffer used to reduce allocations. @@ -46,11 +44,6 @@ internal sealed class GifDecoderCore /// private int globalColorTableLength; - /// - /// The previous frame. - /// - private ImageFrame previousFrame; - /// /// The area to restore. /// @@ -72,12 +65,7 @@ internal sealed class GifDecoderCore private ImageMetaData metaData; /// - /// The image to decode the information to. - /// - private Image image; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration. /// The decoder options. @@ -107,28 +95,84 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) /// /// Decodes the stream to the image. /// + /// The pixel format. /// The stream containing image data. /// The decoded image - public Image Decode(Stream stream) + public Image Decode(Stream stream) + where TPixel : struct, IPixel { + Image image = null; + ImageFrame previousFrame = null; try { - this.metaData = new ImageMetaData(); + this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); - this.currentStream = stream; + // Loop though the respective gif parts and read the data. + int nextFlag = stream.ReadByte(); + while (nextFlag != GifConstants.Terminator) + { + if (nextFlag == GifConstants.ImageLabel) + { + if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + { + break; + } - // Skip the identifier - this.currentStream.Skip(6); - this.ReadLogicalScreenDescriptor(); + this.ReadFrame(ref image, ref previousFrame); + } + else if (nextFlag == GifConstants.ExtensionIntroducer) + { + int label = stream.ReadByte(); + switch (label) + { + case GifConstants.GraphicControlLabel: + this.ReadGraphicalControlExtension(); + break; + case GifConstants.CommentLabel: + this.ReadComments(); + break; + case GifConstants.ApplicationExtensionLabel: - if (this.logicalScreenDescriptor.GlobalColorTableFlag) - { - this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength); + // The application extension length should be 11 but we've got test images that incorrectly + // set this to 252. + int appLength = stream.ReadByte(); + this.Skip(appLength); // No need to read. + break; + case GifConstants.PlainTextLabel: + int plainLength = stream.ReadByte(); + this.Skip(plainLength); // Not supported by any known decoder. + break; + } + } + else if (nextFlag == GifConstants.EndIntroducer) + { + break; + } - // Read the global color table from the stream - stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); + nextFlag = stream.ReadByte(); + if (nextFlag == -1) + { + break; + } } + } + finally + { + this.globalColorTable?.Dispose(); + } + + return image; + } + + /// + /// Reads the image base information from the specified stream. + /// + /// The containing image data. + public IImage Identify(Stream stream) + { + try + { + this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); // Loop though the respective gif parts and read the data. int nextFlag = stream.ReadByte(); @@ -136,12 +180,19 @@ public Image Decode(Stream stream) { if (nextFlag == GifConstants.ImageLabel) { - if (this.previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); + + // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. + if (imageDescriptor.LocalColorTableFlag) { - break; + int length = imageDescriptor.LocalColorTableSize * 3; + + // Skip local color table block + this.Skip(length); } - this.ReadFrame(); + // Skip image block + this.Skip(0); } else if (nextFlag == GifConstants.ExtensionIntroducer) { @@ -149,7 +200,9 @@ public Image Decode(Stream stream) switch (label) { case GifConstants.GraphicControlLabel: - this.ReadGraphicalControlExtension(); + + // Skip graphic control extension block + this.Skip(0); break; case GifConstants.CommentLabel: this.ReadComments(); @@ -184,7 +237,7 @@ public Image Decode(Stream stream) this.globalColorTable?.Dispose(); } - return this.image; + return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData); } /// @@ -242,6 +295,7 @@ private void ReadLogicalScreenDescriptor() { Width = BitConverter.ToInt16(this.buffer, 0), Height = BitConverter.ToInt16(this.buffer, 2), + BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1 BackgroundColorIndex = this.buffer[5], PixelAspectRatio = this.buffer[6], GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1, @@ -308,7 +362,11 @@ private void ReadComments() /// /// Reads an individual gif frame. /// - private void ReadFrame() + /// The pixel format. + /// The image to decode the information to. + /// The previous frame. + private void ReadFrame(ref Image image, ref ImageFrame previousFrame) + where TPixel : struct, IPixel { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); @@ -327,7 +385,7 @@ private void ReadFrame() indices = Buffer.CreateClean(imageDescriptor.Width * imageDescriptor.Height); this.ReadFrameIndices(imageDescriptor, indices); - this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor); + this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor); // Skip any remaining blocks this.Skip(0); @@ -357,10 +415,14 @@ private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span ind /// /// Reads the frames colors, mapping indices to colors. /// + /// The pixel format. + /// The image to decode the information to. + /// The previous frame. /// The indexed pixels. /// The color table containing the available colors. /// The - private void ReadFrameColors(Span indices, Span colorTable, GifImageDescriptor descriptor) + private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Span indices, Span colorTable, GifImageDescriptor descriptor) + where TPixel : struct, IPixel { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; @@ -371,30 +433,30 @@ private void ReadFrameColors(Span indices, Span colorTable, GifImage ImageFrame imageFrame; - if (this.previousFrame == null) + if (previousFrame == null) { // This initializes the image to become fully transparent because the alpha channel is zero. - this.image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); + image = new Image(this.configuration, new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), imageWidth, imageHeight, this.metaData); - this.SetFrameMetaData(this.image.Frames.RootFrame.MetaData); + this.SetFrameMetaData(image.Frames.RootFrame.MetaData); - imageFrame = this.image.Frames.RootFrame; + imageFrame = image.Frames.RootFrame; } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { - prevFrame = this.previousFrame; + prevFrame = previousFrame; } - currentFrame = this.image.Frames.AddFrame(this.previousFrame); // This clones the frame and adds it the collection + currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection this.SetFrameMetaData(currentFrame.MetaData); imageFrame = currentFrame; - this.RestoreToBackground(imageFrame); + this.RestoreToBackground(imageFrame, image.Width, image.Height); } int i = 0; @@ -466,11 +528,11 @@ private void ReadFrameColors(Span indices, Span colorTable, GifImage if (prevFrame != null) { - this.previousFrame = prevFrame; + previousFrame = prevFrame; return; } - this.previousFrame = currentFrame ?? this.image.Frames.RootFrame; + previousFrame = currentFrame ?? image.Frames.RootFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) @@ -482,8 +544,12 @@ private void ReadFrameColors(Span indices, Span colorTable, GifImage /// /// Restores the current frame area to the background. /// + /// The pixel format. /// The frame. - private void RestoreToBackground(ImageFrame frame) + /// Width of the image. + /// Height of the image. + private void RestoreToBackground(ImageFrame frame, int imageWidth, int imageHeight) + where TPixel : struct, IPixel { if (this.restoreArea == null) { @@ -491,8 +557,8 @@ private void RestoreToBackground(ImageFrame frame) } // Optimization for when the size of the frame is the same as the image size. - if (this.restoreArea.Value.Width == this.image.Width && - this.restoreArea.Value.Height == this.image.Height) + if (this.restoreArea.Value.Width == imageWidth && + this.restoreArea.Value.Height == imageHeight) { using (PixelAccessor pixelAccessor = frame.Lock()) { @@ -533,5 +599,29 @@ private void SetFrameMetaData(ImageFrameMetaData meta) meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; } } + + /// + /// Reads the logical screen descriptor and global color table blocks + /// + /// The stream containing image data. + private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream) + { + this.metaData = new ImageMetaData(); + + this.currentStream = stream; + + // Skip the identifier + this.currentStream.Skip(6); + this.ReadLogicalScreenDescriptor(); + + if (this.logicalScreenDescriptor.GlobalColorTableFlag) + { + this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; + this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength); + + // Read the global color table from the stream + stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs index b1109c3e25..9fea5b1126 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs @@ -21,6 +21,11 @@ internal sealed class GifLogicalScreenDescriptor /// rendered in the displaying device. /// public short Height { get; set; } + + /// + /// Gets or sets the color depth, in number of bits per pixel. + /// + public int BitsPerPixel { get; set; } /// /// Gets or sets the index at the Global Color Table for the Background Color. diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 0d16c7db22..ffc40314d8 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.PixelFormats; @@ -22,13 +20,5 @@ public interface IImageDecoder /// The decoded image Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; - - /// - /// Detects the image pixel size from the specified stream. - /// - /// The configuration for the image. - /// The containing image data. - /// The object - PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs new file mode 100644 index 0000000000..4e1dfc84f6 --- /dev/null +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Used for detecting the image base information without decoding it. + /// + public interface IImageInfoDetector + { + /// + /// Reads the image base information from the specified stream. + /// + /// The configuration for the image. + /// The containing image data. + /// The object + IImage Identify(Configuration configuration, Stream stream); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs index 97a424c415..7b082a3d6a 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// /// Image decoder for generating an image out of a jpg stream. /// - internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions + internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -29,13 +29,13 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + public IImage Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); using (var decoder = new OrigJpegDecoderCore(configuration, this)) { - return new PixelTypeInfo(decoder.DetectPixelSize(stream)); + return decoder.Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 92e8557286..b7cad3281e 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -127,6 +127,11 @@ public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions opti IEnumerable IRawJpegData.Components => this.Components; + /// + /// Gets the color depth, in number of bits per pixel. + /// + public int BitsPerPixel => this.ComponentCount * SupportedPrecision; + /// /// Gets the image height /// @@ -193,14 +198,13 @@ public Image Decode(Stream stream) } /// - /// Detects the image pixel size from the specified stream. + /// Reads the image base information from the specified stream. /// /// The containing image data. - /// The color depth, in number of bits per pixel - public int DetectPixelSize(Stream stream) + public IImage Identify(Stream stream) { this.ParseStream(stream, true); - return this.ComponentCount * SupportedPrecision; + return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); } /// @@ -785,7 +789,7 @@ private Image PostProcessIntoImage() { using (var postProcessor = new JpegImagePostProcessor(this)) { - var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); + var image = new Image(this.configuration, new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); postProcessor.PostProcess(image.Frames.RootFrame); return image; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 6845314c1c..ee7d7e6996 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Image decoder for generating an image out of a jpg stream. /// - public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions + public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -31,13 +31,13 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + public IImage Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); using (var decoder = new OrigJpegDecoderCore(configuration, this)) { - return new PixelTypeInfo(decoder.DetectPixelSize(stream)); + return decoder.Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs index 1458d54e5f..37ce0151f3 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs @@ -27,11 +27,5 @@ public Image Decode(Configuration configuration, Stream stream) return decoder.Decode(stream); } } - - /// - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) - { - throw new System.NotImplementedException(); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 211c24d208..c93adac2d4 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -159,7 +159,7 @@ public Image Decode(Stream stream) this.QuantizeAndInverseAllComponents(); - var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); + var image = new Image(this.configuration, null, this.ImageWidth, this.ImageHeight, metadata); this.FillPixelData(image.Frames.RootFrame); this.AssignResolution(image); return image; diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index d4b72a00b4..a2a2ce9ce2 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -1,21 +1,21 @@ namespace SixLabors.ImageSharp.Formats { /// - /// Stores information about pixel + /// Stores information about pixel. /// public class PixelTypeInfo { /// /// Initializes a new instance of the class. /// - /// color depth, in number of bits per pixel + /// Color depth, in number of bits per pixel. internal PixelTypeInfo(int bitsPerPixel) { this.BitsPerPixel = bitsPerPixel; } /// - /// Gets color depth, in number of bits per pixel + /// Gets color depth, in number of bits per pixel. /// public int BitsPerPixel { get; } } diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index abb41770f9..8e110732ee 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; using System.IO; using System.Text; using SixLabors.ImageSharp.PixelFormats; @@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// /// - public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions + public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -55,16 +53,11 @@ public Image Decode(Configuration configuration, Stream stream) return decoder.Decode(stream); } - /// - /// Detects the image pixel size from the specified stream. - /// - /// The configuration for the image. - /// The containing image data. - /// The color depth, in number of bits per pixel - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + /// + public IImage Identify(Configuration configuration, Stream stream) { var decoder = new PngDecoderCore(configuration, this); - return new PixelTypeInfo(decoder.DetectPixelSize(stream)); + return decoder.Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 52b571761b..e4c554437e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -237,7 +237,7 @@ public Image Decode(Stream stream) deframeStream.AllocateNewBytes(currentChunk.Length); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); - stream.Read(this.crcBuffer, 0, 4); + this.currentStream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: byte[] pal = new byte[currentChunk.Length]; @@ -279,42 +279,50 @@ public Image Decode(Stream stream) } /// - /// Detects the image pixel size from the specified stream. + /// Reads the image base information from the specified stream. /// /// The containing image data. - /// The color depth, in number of bits per pixel - public int DetectPixelSize(Stream stream) + public IImage Identify(Stream stream) { + var metadata = new ImageMetaData(); this.currentStream = stream; this.currentStream.Skip(8); try { - PngChunk currentChunk; - while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + PngChunk currentChunk; + while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + { + try { - try + switch (currentChunk.Type) { - switch (currentChunk.Type) - { - case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data); - this.ValidateHeader(); - this.isEndChunkReached = true; - break; - case PngChunkTypes.End: - this.isEndChunkReached = true; - break; - } + case PngChunkTypes.Header: + this.ReadHeaderChunk(currentChunk.Data); + this.ValidateHeader(); + break; + case PngChunkTypes.Physical: + this.ReadPhysicalChunk(metadata, currentChunk.Data); + break; + case PngChunkTypes.Data: + this.SkipChunkDataAndCrc(currentChunk); + break; + case PngChunkTypes.Text: + this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); + break; + case PngChunkTypes.End: + this.isEndChunkReached = true; + break; } - finally + } + finally + { + // Data is rented in ReadChunkData() + if (currentChunk.Data != null) { - // Data is rented in ReadChunkData() - if (currentChunk.Data != null) - { - ArrayPool.Shared.Return(currentChunk.Data); - } + ArrayPool.Shared.Return(currentChunk.Data); } } + } } finally { @@ -327,7 +335,7 @@ public int DetectPixelSize(Stream stream) throw new ImageFormatException("PNG Image hasn't header chunk"); } - return this.CalculateBitsPerPixel(); + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); } /// @@ -418,7 +426,7 @@ private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data) private void InitializeImage(ImageMetaData metadata, out Image image) where TPixel : struct, IPixel { - image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); + image = new Image(this.configuration, new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerSample = 1; @@ -1255,6 +1263,15 @@ private void ReadChunkCrc(PngChunk chunk) } } + /// + /// Skips the chunk data and the cycle redundancy chunk read from the data. + /// + private void SkipChunkDataAndCrc(PngChunk chunk) + { + this.currentStream.Skip(chunk.Length); + this.currentStream.Skip(4); + } + /// /// Reads the chunk data from the stream. /// diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs new file mode 100644 index 0000000000..f840c78f00 --- /dev/null +++ b/src/ImageSharp/Image/IImage.cs @@ -0,0 +1,31 @@ +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents the base image abstraction. + /// + public interface IImage + { + /// + /// Gets information about pixel. + /// + PixelTypeInfo PixelType { get; } + + /// + /// Gets the width. + /// + int Width { get; } + + /// + /// Gets the height. + /// + int Height { get; } + + /// + /// Gets the meta data of the image. + /// + ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index da2e036057..8f379cb109 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -81,17 +81,17 @@ private static (Image img, IImageFormat format) Decode(Stream st } /// - /// Detects the image pixel size. + /// Reads the image base information. /// /// The stream. /// the configuration. /// - /// The or null if suitable decoder not found. + /// The or null if suitable info detector not found. /// - private static PixelTypeInfo InternalDetectPixelType(Stream stream, Configuration config) + private static IImage InternalIdentity(Stream stream, Configuration config) { - IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat _); - return decoder?.DetectPixelType(config, stream); + var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector; + return detector?.Identify(config, stream); } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 2e0832b81f..0233efb208 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -37,22 +37,22 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream) } /// - /// By reading the header on the provided stream this calculates the images color depth. + /// By reading the header on the provided stream this reads the image base information. /// /// The image stream to read the header from. /// /// Thrown if the stream is not readable nor seekable. /// /// - /// The or null if suitable decoder not found. + /// The or null if suitable info detector not found. /// - public static PixelTypeInfo DetectPixelType(Stream stream) + public static IImage Identify(Stream stream) { - return DetectPixelType(null, stream); + return Identify(null, stream); } /// - /// By reading the header on the provided stream this calculates the images color depth. + /// By reading the header on the provided stream this reads the image base information. /// /// The configuration. /// The image stream to read the header from. @@ -60,11 +60,11 @@ public static PixelTypeInfo DetectPixelType(Stream stream) /// Thrown if the stream is not readable nor seekable. /// /// - /// The or null if suitable decoder not found. + /// The or null if suitable info detector not found. /// - public static PixelTypeInfo DetectPixelType(Configuration config, Stream stream) + public static IImage Identify(Configuration config, Stream stream) { - return WithSeekableStream(stream, s => InternalDetectPixelType(s, config ?? Configuration.Default)); + return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default)); } /// diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs index 3e9bb03435..ececcea895 100644 --- a/src/ImageSharp/Image/ImageFrameCollection.cs +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -129,7 +129,7 @@ public Image ExportFrame(int index) this.frames.Remove(frame); - return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame }); + return new Image(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { frame }); } /// @@ -137,7 +137,7 @@ public Image CloneFrame(int index) { ImageFrame frame = this[index]; ImageFrame clonedFrame = frame.Clone(); - return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame }); + return new Image(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { clonedFrame }); } /// diff --git a/src/ImageSharp/Image/ImageInfo.cs b/src/ImageSharp/Image/ImageInfo.cs new file mode 100644 index 0000000000..11dcc3e752 --- /dev/null +++ b/src/ImageSharp/Image/ImageInfo.cs @@ -0,0 +1,24 @@ +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp +{ + internal sealed class ImageInfo : IImage + { + public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData) + { + this.PixelType = pixelType; + this.Width = width; + this.Height = height; + this.MetaData = metaData; + } + + public PixelTypeInfo PixelType { get; } + + public int Width { get; } + + public int Height { get; } + + public ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 482971e540..2d0448d08f 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed partial class Image : IDisposable, IConfigurable + public sealed partial class Image : IImage, IDisposable, IConfigurable where TPixel : struct, IPixel { private Configuration configuration; @@ -33,7 +33,7 @@ public sealed partial class Image : IDisposable, IConfigurable /// The width of the image in pixels. /// The height of the image in pixels. public Image(Configuration configuration, int width, int height) - : this(configuration, width, height, new ImageMetaData()) + : this(configuration, null, width, height, new ImageMetaData()) { } @@ -55,12 +55,14 @@ public Image(int width, int height) /// /// The configuration providing initialization code which allows extending the library. /// + /// The information about pixel of the image. /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, PixelTypeInfo pixelType, int width, int height, ImageMetaData metadata) { this.configuration = configuration ?? Configuration.Default; + this.PixelType = pixelType; this.MetaData = metadata ?? new ImageMetaData(); this.frames = new ImageFrameCollection(this, width, height); } @@ -70,11 +72,13 @@ internal Image(Configuration configuration, int width, int height, ImageMetaData /// with the height and the width of the image. /// /// The configuration providing initialization code which allows extending the library. + /// The information about pixel of the image. /// The images metadata. /// The frames that will be owned by this image instance. - internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) + internal Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetaData metadata, IEnumerable> frames) { this.configuration = configuration ?? Configuration.Default; + this.PixelType = pixelType; this.MetaData = metadata ?? new ImageMetaData(); this.frames = new ImageFrameCollection(this, frames); @@ -84,20 +88,17 @@ internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable< /// Gets the pixel buffer. /// Configuration IConfigurable.Configuration => this.configuration; + + /// + public PixelTypeInfo PixelType { get; } - /// - /// Gets the width. - /// + /// public int Width => this.frames.RootFrame.Width; - /// - /// Gets the height. - /// + /// public int Height => this.frames.RootFrame.Height; - /// - /// Gets the meta data of the image. - /// + /// public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// @@ -144,7 +145,7 @@ public void Save(Stream stream, IImageEncoder encoder) public Image Clone() { IEnumerable> clonedFrames = this.frames.Select(x => x.Clone()); - return new Image(this.configuration, this.MetaData.Clone(), clonedFrames); + return new Image(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames); } /// @@ -162,7 +163,7 @@ public Image CloneAs() where TPixel2 : struct, IPixel { IEnumerable> clonedFrames = this.frames.Select(x => x.CloneAs()); - var target = new Image(this.configuration, this.MetaData.Clone(), clonedFrames); + var target = new Image(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames); return target; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 17b42c5040..f1fb0086ff 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -60,7 +60,7 @@ protected override Image CreateDestination(Image source, Rectang // For resize we know we are going to populate every pixel with fresh data and we want a different target size so // let's manually clone an empty set of images at the correct target and then have the base class process them in turn. IEnumerable> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders - var image = new Image(config, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added + var image = new Image(config, source.PixelType, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added return image; } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 17a559a94f..2c0121803b 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -49,7 +49,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 8838e4f711..9a095548a7 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -130,7 +130,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index f912a90ae6..cb1987aef4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -398,7 +398,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index b2b4b2f030..248e0a5eea 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -197,7 +197,7 @@ public void DetectPixelSize(string imagePath, int expectedPixelSize) TestFile testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { - Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index c12ae5aca4..078b708dfd 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -201,11 +201,6 @@ public Image Decode(Configuration config, Stream stream) where T return this.testFormat.Sample(); } - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) - { - throw new NotImplementedException(); - } - public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index d36fd2a14d..719d1b7583 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -1,19 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Drawing; -using System.Drawing.Drawing2D; + using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; -using PixelFormat = System.Drawing.Imaging.PixelFormat; - namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class SystemDrawingReferenceDecoder : IImageDecoder + using SixLabors.ImageSharp.MetaData; + + public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); @@ -22,7 +20,7 @@ public Image Decode(Configuration configuration, Stream stream) { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) { - if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb) + if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) { return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sourceBitmap); } @@ -32,12 +30,12 @@ public Image Decode(Configuration configuration, Stream stream) sourceBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { - using (var g = Graphics.FromImage(convertedBitmap)) + using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) { g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); } @@ -46,11 +44,12 @@ public Image Decode(Configuration configuration, Stream stream) } } - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) + public IImage Identify(Configuration configuration, Stream stream) { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) { - return new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); + var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); + return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetaData()); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 0c54f8a3c5..e3249fae9f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -83,11 +83,6 @@ public Image Decode(Configuration configuration, Stream stream) return new Image(42, 42); } - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) - { - throw new NotImplementedException(); - } - // Couldn't make xUnit happy without this hackery: private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); @@ -150,11 +145,6 @@ public Image Decode(Configuration configuration, Stream stream) return new Image(42, 42); } - public PixelTypeInfo DetectPixelType(Configuration configuration, Stream stream) - { - throw new NotImplementedException(); - } - private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; From b435f5eaa5534bf9783f6b939f2a7aefbabd04f6 Mon Sep 17 00:00:00 2001 From: denisivan0v Date: Wed, 17 Jan 2018 17:07:48 +0700 Subject: [PATCH 08/11] codestyle fixes --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 4 ++-- .../Formats/Gif/Sections/GifLogicalScreenDescriptor.cs | 2 +- src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs | 2 +- src/ImageSharp/Image/ImageInfo.cs | 2 +- src/ImageSharp/Image/Image{TPixel}.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index ef9e761643..cb510ac05a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -149,7 +149,7 @@ public IImage Identify(Stream stream) this.ReadImageHeaders(stream, out _, out _); return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData()); } - + /// /// Returns the y- value based on the given height. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index dbca49f068..257274c92c 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -186,7 +186,7 @@ public IImage Identify(Stream stream) if (imageDescriptor.LocalColorTableFlag) { int length = imageDescriptor.LocalColorTableSize * 3; - + // Skip local color table block this.Skip(length); } @@ -200,7 +200,7 @@ public IImage Identify(Stream stream) switch (label) { case GifConstants.GraphicControlLabel: - + // Skip graphic control extension block this.Skip(0); break; diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs index 9fea5b1126..05f232a4be 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs @@ -21,7 +21,7 @@ internal sealed class GifLogicalScreenDescriptor /// rendered in the displaying device. /// public short Height { get; set; } - + /// /// Gets or sets the color depth, in number of bits per pixel. /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index b7cad3281e..ef7a4234f8 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -131,7 +131,7 @@ public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions opti /// Gets the color depth, in number of bits per pixel. /// public int BitsPerPixel => this.ComponentCount * SupportedPrecision; - + /// /// Gets the image height /// diff --git a/src/ImageSharp/Image/ImageInfo.cs b/src/ImageSharp/Image/ImageInfo.cs index 11dcc3e752..0d8daaf1a8 100644 --- a/src/ImageSharp/Image/ImageInfo.cs +++ b/src/ImageSharp/Image/ImageInfo.cs @@ -12,7 +12,7 @@ public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData m this.Height = height; this.MetaData = metaData; } - + public PixelTypeInfo PixelType { get; } public int Width { get; } diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 2d0448d08f..9b20e3dfab 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -88,7 +88,7 @@ internal Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetaDa /// Gets the pixel buffer. /// Configuration IConfigurable.Configuration => this.configuration; - + /// public PixelTypeInfo PixelType { get; } From 9f719d5b9423083c112a69144e98a15bbb8c9409 Mon Sep 17 00:00:00 2001 From: denisivan0v Date: Wed, 17 Jan 2018 17:11:13 +0700 Subject: [PATCH 09/11] codestyle fix --- src/ImageSharp/Image/IImage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs index f840c78f00..58e5d50e51 100644 --- a/src/ImageSharp/Image/IImage.cs +++ b/src/ImageSharp/Image/IImage.cs @@ -17,7 +17,7 @@ public interface IImage /// Gets the width. /// int Width { get; } - + /// /// Gets the height. /// From e88e13806fc16d52941521f6a7f25e9b1d024518 Mon Sep 17 00:00:00 2001 From: denisivan0v Date: Thu, 18 Jan 2018 17:13:54 +0700 Subject: [PATCH 10/11] bugfix in GifDecoderCore.Identity --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 11 ---- .../Formats/GeneralFormatTests.cs | 59 +++++++++++++++++++ .../ImageSharp.Tests/ImageSharp.Tests.csproj | 1 + 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 257274c92c..7a08b4194e 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -180,17 +180,6 @@ public IImage Identify(Stream stream) { if (nextFlag == GifConstants.ImageLabel) { - GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); - - // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. - if (imageDescriptor.LocalColorTableFlag) - { - int length = imageDescriptor.LocalColorTableSize * 3; - - // Skip local color table block - this.Skip(length); - } - // Skip image block this.Skip(0); } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 473bc2b523..97128e2c93 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -3,11 +3,19 @@ using System.IO; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using Xunit; namespace SixLabors.ImageSharp.Tests { + using System; + + + public class GeneralFormatTests : FileTestBase { [Theory] @@ -146,5 +154,56 @@ public void ImageShouldPreservePixelByteOrderWhenSerialized() } } } + + [Theory] + [InlineData(10, 10, "png")] + [InlineData(100, 100, "png")] + [InlineData(100, 10, "png")] + [InlineData(10, 100, "png")] + [InlineData(10, 10, "gif")] + [InlineData(100, 100, "gif")] + [InlineData(100, 10, "gif")] + [InlineData(10, 100, "gif")] + [InlineData(10, 10, "bmp")] + [InlineData(100, 100, "bmp")] + [InlineData(100, 10, "bmp")] + [InlineData(10, 100, "bmp")] + [InlineData(10, 10, "jpg")] + [InlineData(100, 100, "jpg")] + [InlineData(100, 10, "jpg")] + [InlineData(10, 100, "jpg")] + public void CanIdentifyImageLoadedFromBytes(int width, int height, string format) + { + using (Image image = Image.LoadPixelData(new Rgba32[width * height], width, height)) + { + using (var memoryStream = new MemoryStream()) + { + image.Save(memoryStream, GetEncoder(format)); + memoryStream.Position = 0; + + var imageInfo = Image.Identify(memoryStream); + + Assert.Equal(imageInfo.Width, width); + Assert.Equal(imageInfo.Height, height); + } + } + } + + private static IImageEncoder GetEncoder(string format) + { + switch (format) + { + case "png": + return new PngEncoder(); + case "gif": + return new GifEncoder(); + case "bmp": + return new BmpEncoder(); + case "jpg": + return new JpegEncoder(); + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 2f45e4c83a..7e0329ef46 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -17,6 +17,7 @@ + From 61c9caf4727ad8542120aae79142f77da632514e Mon Sep 17 00:00:00 2001 From: denisivan0v Date: Fri, 19 Jan 2018 14:18:01 +0700 Subject: [PATCH 11/11] changes on code review --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 4 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 6 +- src/ImageSharp/Formats/IImageInfoDetector.cs | 6 +- .../Jpeg/GolangPort/OrigJpegDecoder.cs | 2 +- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 6 +- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 2 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/PixelTypeInfo.cs | 2 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 2 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 6 +- src/ImageSharp/Image/IImage.cs | 26 +- src/ImageSharp/Image/IImageInfo.cs | 31 +++ src/ImageSharp/Image/Image.Decode.cs | 4 +- src/ImageSharp/Image/Image.FromStream.cs | 12 +- src/ImageSharp/Image/ImageFrameCollection.cs | 4 +- src/ImageSharp/Image/ImageInfo.cs | 16 +- src/ImageSharp/Image/Image{TPixel}.cs | 18 +- src/ImageSharp/ImageSharp.csproj | 236 +++++++++--------- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../SystemDrawingReferenceDecoder.cs | 5 +- 22 files changed, 208 insertions(+), 188 deletions(-) create mode 100644 src/ImageSharp/Image/IImageInfo.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index e252e63406..78a9de6c45 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -34,7 +34,7 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index cb510ac05a..e552ac1042 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -141,10 +141,10 @@ public Image Decode(Stream stream) } /// - /// Reads the image base information from the specified stream. + /// Reads the raw image information from the specified stream. /// /// The containing image data. - public IImage Identify(Stream stream) + public IImageInfo Identify(Stream stream) { this.ReadImageHeaders(stream, out _, out _); return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData()); diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index ccb6cf92c5..c81c51e8b4 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -36,7 +36,7 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 7a08b4194e..3c22518057 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -165,10 +165,10 @@ public Image Decode(Stream stream) } /// - /// Reads the image base information from the specified stream. + /// Reads the raw image information from the specified stream. /// /// The containing image data. - public IImage Identify(Stream stream) + public IImageInfo Identify(Stream stream) { try { @@ -425,7 +425,7 @@ private void ReadFrameColors(ref Image image, ref ImageFrame(this.configuration, new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), imageWidth, imageHeight, this.metaData); + image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); this.SetFrameMetaData(image.Frames.RootFrame.MetaData); diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 4e1dfc84f6..37bc0866fa 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -6,16 +6,16 @@ namespace SixLabors.ImageSharp.Formats { /// - /// Used for detecting the image base information without decoding it. + /// Used for detecting the raw image information without decoding it. /// public interface IImageInfoDetector { /// - /// Reads the image base information from the specified stream. + /// Reads the raw image information from the specified stream. /// /// The configuration for the image. /// The containing image data. /// The object - IImage Identify(Configuration configuration, Stream stream); + IImageInfo Identify(Configuration configuration, Stream stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs index 7b082a3d6a..ecebe9480d 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs @@ -29,7 +29,7 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index ef7a4234f8..d788b65c4b 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -198,10 +198,10 @@ public Image Decode(Stream stream) } /// - /// Reads the image base information from the specified stream. + /// Reads the raw image information from the specified stream. /// /// The containing image data. - public IImage Identify(Stream stream) + public IImageInfo Identify(Stream stream) { this.ParseStream(stream, true); return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); @@ -789,7 +789,7 @@ private Image PostProcessIntoImage() { using (var postProcessor = new JpegImagePostProcessor(this)) { - var image = new Image(this.configuration, new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); postProcessor.PostProcess(image.Frames.RootFrame); return image; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index ee7d7e6996..91835b5d71 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -31,7 +31,7 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index c93adac2d4..211c24d208 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -159,7 +159,7 @@ public Image Decode(Stream stream) this.QuantizeAndInverseAllComponents(); - var image = new Image(this.configuration, null, this.ImageWidth, this.ImageHeight, metadata); + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); this.FillPixelData(image.Frames.RootFrame); this.AssignResolution(image); return image; diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index a2a2ce9ce2..cdb6db8d9f 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -1,7 +1,7 @@ namespace SixLabors.ImageSharp.Formats { /// - /// Stores information about pixel. + /// Stores the raw image pixel type information. /// public class PixelTypeInfo { diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 8e110732ee..9bde4f8cc3 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -54,7 +54,7 @@ public Image Decode(Configuration configuration, Stream stream) } /// - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { var decoder = new PngDecoderCore(configuration, this); return decoder.Identify(stream); diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index e4c554437e..5c9b753e58 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -279,10 +279,10 @@ public Image Decode(Stream stream) } /// - /// Reads the image base information from the specified stream. + /// Reads the raw image information from the specified stream. /// /// The containing image data. - public IImage Identify(Stream stream) + public IImageInfo Identify(Stream stream) { var metadata = new ImageMetaData(); this.currentStream = stream; @@ -426,7 +426,7 @@ private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data) private void InitializeImage(ImageMetaData metadata, out Image image) where TPixel : struct, IPixel { - image = new Image(this.configuration, new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); + image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerSample = 1; diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs index 58e5d50e51..afd00105e6 100644 --- a/src/ImageSharp/Image/IImage.cs +++ b/src/ImageSharp/Image/IImage.cs @@ -1,31 +1,9 @@ -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.MetaData; - -namespace SixLabors.ImageSharp +namespace SixLabors.ImageSharp { /// /// Represents the base image abstraction. /// - public interface IImage + public interface IImage : IImageInfo { - /// - /// Gets information about pixel. - /// - PixelTypeInfo PixelType { get; } - - /// - /// Gets the width. - /// - int Width { get; } - - /// - /// Gets the height. - /// - int Height { get; } - - /// - /// Gets the meta data of the image. - /// - ImageMetaData MetaData { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageInfo.cs b/src/ImageSharp/Image/IImageInfo.cs new file mode 100644 index 0000000000..b8dd59cc72 --- /dev/null +++ b/src/ImageSharp/Image/IImageInfo.cs @@ -0,0 +1,31 @@ +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents raw image information. + /// + public interface IImageInfo + { + /// + /// Gets the raw image pixel type information. + /// + PixelTypeInfo PixelType { get; } + + /// + /// Gets the width. + /// + int Width { get; } + + /// + /// Gets the height. + /// + int Height { get; } + + /// + /// Gets the meta data of the image. + /// + ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 8f379cb109..6af61f9f4a 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -86,9 +86,9 @@ private static (Image img, IImageFormat format) Decode(Stream st /// The stream. /// the configuration. /// - /// The or null if suitable info detector not found. + /// The or null if suitable info detector not found. /// - private static IImage InternalIdentity(Stream stream, Configuration config) + private static IImageInfo InternalIdentity(Stream stream, Configuration config) { var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector; return detector?.Identify(config, stream); diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 0233efb208..680e15aa7d 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -37,22 +37,22 @@ public static IImageFormat DetectFormat(Configuration config, Stream stream) } /// - /// By reading the header on the provided stream this reads the image base information. + /// By reading the header on the provided stream this reads the raw image information. /// /// The image stream to read the header from. /// /// Thrown if the stream is not readable nor seekable. /// /// - /// The or null if suitable info detector not found. + /// The or null if suitable info detector not found. /// - public static IImage Identify(Stream stream) + public static IImageInfo Identify(Stream stream) { return Identify(null, stream); } /// - /// By reading the header on the provided stream this reads the image base information. + /// By reading the header on the provided stream this reads the raw image information. /// /// The configuration. /// The image stream to read the header from. @@ -60,9 +60,9 @@ public static IImage Identify(Stream stream) /// Thrown if the stream is not readable nor seekable. /// /// - /// The or null if suitable info detector not found. + /// The or null if suitable info detector not found. /// - public static IImage Identify(Configuration config, Stream stream) + public static IImageInfo Identify(Configuration config, Stream stream) { return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default)); } diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs index ececcea895..3e9bb03435 100644 --- a/src/ImageSharp/Image/ImageFrameCollection.cs +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -129,7 +129,7 @@ public Image ExportFrame(int index) this.frames.Remove(frame); - return new Image(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { frame }); + return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame }); } /// @@ -137,7 +137,7 @@ public Image CloneFrame(int index) { ImageFrame frame = this[index]; ImageFrame clonedFrame = frame.Clone(); - return new Image(this.parent.GetConfiguration(), this.parent.PixelType, this.parent.MetaData.Clone(), new[] { clonedFrame }); + return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame }); } /// diff --git a/src/ImageSharp/Image/ImageInfo.cs b/src/ImageSharp/Image/ImageInfo.cs index 0d8daaf1a8..a315ee0ed5 100644 --- a/src/ImageSharp/Image/ImageInfo.cs +++ b/src/ImageSharp/Image/ImageInfo.cs @@ -3,8 +3,18 @@ namespace SixLabors.ImageSharp { - internal sealed class ImageInfo : IImage + /// + /// Stores the raw image information. + /// + internal sealed class ImageInfo : IImageInfo { + /// + /// Initializes a new instance of the class. + /// + /// The raw image pixel type information. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The images metadata. public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData) { this.PixelType = pixelType; @@ -13,12 +23,16 @@ public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData m this.MetaData = metaData; } + /// public PixelTypeInfo PixelType { get; } + /// public int Width { get; } + /// public int Height { get; } + /// public ImageMetaData MetaData { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 9b20e3dfab..be38b41f24 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; @@ -33,7 +33,7 @@ public sealed partial class Image : IImage, IDisposable, IConfigurable /// The width of the image in pixels. /// The height of the image in pixels. public Image(Configuration configuration, int width, int height) - : this(configuration, null, width, height, new ImageMetaData()) + : this(configuration, width, height, new ImageMetaData()) { } @@ -55,14 +55,13 @@ public Image(int width, int height) /// /// The configuration providing initialization code which allows extending the library. /// - /// The information about pixel of the image. /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, PixelTypeInfo pixelType, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) { this.configuration = configuration ?? Configuration.Default; - this.PixelType = pixelType; + this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata ?? new ImageMetaData(); this.frames = new ImageFrameCollection(this, width, height); } @@ -72,13 +71,12 @@ internal Image(Configuration configuration, PixelTypeInfo pixelType, int width, /// with the height and the width of the image. /// /// The configuration providing initialization code which allows extending the library. - /// The information about pixel of the image. /// The images metadata. /// The frames that will be owned by this image instance. - internal Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetaData metadata, IEnumerable> frames) + internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) { this.configuration = configuration ?? Configuration.Default; - this.PixelType = pixelType; + this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata ?? new ImageMetaData(); this.frames = new ImageFrameCollection(this, frames); @@ -145,7 +143,7 @@ public void Save(Stream stream, IImageEncoder encoder) public Image Clone() { IEnumerable> clonedFrames = this.frames.Select(x => x.Clone()); - return new Image(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames); + return new Image(this.configuration, this.MetaData.Clone(), clonedFrames); } /// @@ -163,7 +161,7 @@ public Image CloneAs() where TPixel2 : struct, IPixel { IEnumerable> clonedFrames = this.frames.Select(x => x.CloneAs()); - var target = new Image(this.configuration, this.PixelType, this.MetaData.Clone(), clonedFrames); + var target = new Image(this.configuration, this.MetaData.Clone(), clonedFrames); return target; } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8c22237cf7..1d22e59cb2 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,120 +1,120 @@  - - A cross-platform library for the processing of image files; written in C# - SixLabors.ImageSharp - $(packageversion) - 0.0.1 - Six Labors and contributors - netstandard1.1;netstandard1.3;netstandard2.0 - true - true - SixLabors.ImageSharp - SixLabors.ImageSharp - Image Resize Crop Gif Jpg Jpeg Bitmap Png Core - https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/SixLabors/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/SixLabors/ImageSharp - false - false - false - false - false - false - false - false - false - full - portable - True - IOperation - - - - - - - - - All - - - - - - - - - - - - - ..\..\ImageSharp.ruleset - SixLabors.ImageSharp - - - true - - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - PixelOperations{TPixel}.Generated.cs - - - TextTemplatingFileGenerator - Rgba32.PixelOperations.Generated.cs - - - PorterDuffFunctions.Generated.cs - TextTemplatingFileGenerator - - - DefaultPixelBlenders.Generated.cs - TextTemplatingFileGenerator - - - - - - - - True - True - Block8x8F.Generated.tt - - - True - True - Block8x8F.Generated.tt - - - True - True - PixelOperations{TPixel}.Generated.tt - - - True - True - Rgba32.PixelOperations.Generated.tt - - - True - True - DefaultPixelBlenders.Generated.tt - - - True - True - PorterDuffFunctions.Generated.tt - - + + A cross-platform library for the processing of image files; written in C# + SixLabors.ImageSharp + $(packageversion) + 0.0.1 + Six Labors and contributors + netstandard1.1;netstandard1.3;netstandard2.0 + true + true + SixLabors.ImageSharp + SixLabors.ImageSharp + Image Resize Crop Gif Jpg Jpeg Bitmap Png Core + https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabors/ImageSharp + http://www.apache.org/licenses/LICENSE-2.0 + git + https://github.com/SixLabors/ImageSharp + false + false + false + false + false + false + false + false + false + full + portable + True + IOperation + + + + + + + + + All + + + + + + + + + + + + + ..\..\ImageSharp.ruleset + SixLabors.ImageSharp + + + true + + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + PixelOperations{TPixel}.Generated.cs + + + TextTemplatingFileGenerator + Rgba32.PixelOperations.Generated.cs + + + PorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + + DefaultPixelBlenders.Generated.cs + TextTemplatingFileGenerator + + + + + + + + True + True + Block8x8F.Generated.tt + + + True + True + Block8x8F.Generated.tt + + + True + True + PixelOperations{TPixel}.Generated.tt + + + True + True + Rgba32.PixelOperations.Generated.tt + + + True + True + DefaultPixelBlenders.Generated.tt + + + True + True + PorterDuffFunctions.Generated.tt + + \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index f1fb0086ff..17b42c5040 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -60,7 +60,7 @@ protected override Image CreateDestination(Image source, Rectang // For resize we know we are going to populate every pixel with fresh data and we want a different target size so // let's manually clone an empty set of images at the correct target and then have the base class process them in turn. IEnumerable> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders - var image = new Image(config, source.PixelType, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added + var image = new Image(config, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added return image; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 719d1b7583..0e967e9278 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -5,12 +5,11 @@ using System.IO; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - using SixLabors.ImageSharp.MetaData; - public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); @@ -44,7 +43,7 @@ public Image Decode(Configuration configuration, Stream stream) } } - public IImage Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream) { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) {