From a3185fd6e787f9d0479de3d38dec04bd773cab85 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Mon, 20 Mar 2017 17:27:01 +0000 Subject: [PATCH 01/18] Move image loading out of constructors and into static methods --- ImageSharp.sln | 2 +- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 5 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 15 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 10 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 48 ++- src/ImageSharp/Formats/IImageDecoder.cs | 4 +- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 5 +- .../Formats/Jpeg/JpegDecoderCore.cs | 120 +++--- src/ImageSharp/Formats/Png/PngDecoder.cs | 10 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 39 +- src/ImageSharp/Image.Decode.cs | 67 ++++ src/ImageSharp/Image.FromBytes.cs | 148 ++++++++ src/ImageSharp/Image.FromFile.cs | 148 ++++++++ src/ImageSharp/Image.FromStream.cs | 184 ++++++++++ src/ImageSharp/Image.cs | 190 +--------- src/ImageSharp/Image/IImageBase.cs | 10 - src/ImageSharp/Image/IImageBase{TColor}.cs | 10 - src/ImageSharp/Image/ImageBase{TColor}.cs | 41 +-- src/ImageSharp/Image/Image{TColor}.cs | 296 +-------------- src/ImageSharp/MetaData/ImageMetaData.cs | 44 ++- .../MetaData/Profiles/Exif/ExifProfile.cs | 2 +- .../ImageSharp.Benchmarks/Image/DecodeBmp.cs | 2 +- .../Image/DecodeFilteredPng.cs | 2 +- .../ImageSharp.Benchmarks/Image/DecodeGif.cs | 2 +- .../ImageSharp.Benchmarks/Image/DecodeJpeg.cs | 2 +- .../Image/DecodeJpegMultiple.cs | 2 +- .../ImageSharp.Benchmarks/Image/DecodePng.cs | 2 +- .../ImageSharp.Benchmarks/Image/EncodeBmp.cs | 2 +- .../ImageSharp.Benchmarks/Image/EncodeGif.cs | 2 +- .../Image/EncodeIndexedPng.cs | 2 +- .../ImageSharp.Benchmarks/Image/EncodeJpeg.cs | 2 +- .../ImageSharp.Benchmarks/Image/EncodePng.cs | 2 +- .../Image/MultiImageBenchmarkBase.cs | 2 +- .../Samplers/DetectEdges.cs | 2 +- .../Formats/GeneralFormatTests.cs | 2 +- .../Formats/Gif/GifEncoderTests.cs | 6 +- .../Formats/Jpg/JpegDecoderTests.cs | 5 +- .../Formats/Jpg/JpegEncoderTests.cs | 4 +- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 2 +- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 341 ++++++++++++++++++ tests/ImageSharp.Tests/Image/ImageTests.cs | 10 +- .../Profiles/Exif/ExifProfileTests.cs | 6 +- tests/ImageSharp.Tests/TestFile.cs | 4 +- tests/ImageSharp.Tests/TestFormat.cs | 174 +++++++++ .../TestUtilities/Factories/GenericFactory.cs | 2 +- .../TestUtilities/Factories/ImageFactory.cs | 2 +- 46 files changed, 1293 insertions(+), 689 deletions(-) create mode 100644 src/ImageSharp/Image.Decode.cs create mode 100644 src/ImageSharp/Image.FromBytes.cs create mode 100644 src/ImageSharp/Image.FromFile.cs create mode 100644 src/ImageSharp/Image.FromStream.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageLoadTests.cs create mode 100644 tests/ImageSharp.Tests/TestFormat.cs diff --git a/ImageSharp.sln b/ImageSharp.sln index 9c729493b..628fa7015 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 9f490a3a9..2bc1c8cc3 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -26,13 +26,12 @@ namespace ImageSharp.Formats public class BmpDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); - new BmpDecoderCore().Decode(image, stream); + return new BmpDecoderCore().Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index a75031ea1..adfa4b6ac 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -48,16 +48,13 @@ namespace ImageSharp.Formats /// the data to image. /// /// The pixel format. - /// The image, where the data should be set to. - /// Cannot be null (Nothing in Visual Basic). /// The stream, where the image should be /// decoded from. Cannot be null (Nothing in Visual Basic). /// - /// is null. - /// - or - /// is null. /// - public void Decode(Image image, Stream stream) + /// The decoded image. + public Image Decode(Stream stream) where TColor : struct, IPixel { this.currentStream = stream; @@ -110,14 +107,14 @@ namespace ImageSharp.Formats this.currentStream.Read(palette, 0, colorMapSize); } - if (this.infoHeader.Width > image.MaxWidth || this.infoHeader.Height > image.MaxHeight) + if (this.infoHeader.Width > Image.MaxWidth || this.infoHeader.Height > Image.MaxHeight) { throw new ArgumentOutOfRangeException( $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " - + $"bigger then the max allowed size '{image.MaxWidth}x{image.MaxHeight}'"); + + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - image.InitPixels(this.infoHeader.Width, this.infoHeader.Height); + Image image = new Image(this.infoHeader.Width, this.infoHeader.Height); using (PixelAccessor pixels = image.Lock()) { @@ -151,6 +148,8 @@ namespace ImageSharp.Formats throw new NotSupportedException("Does not support this kind of bitmap files."); } } + + return image; } catch (IndexOutOfRangeException e) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index b1e8ba928..16b036e68 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -14,25 +14,25 @@ namespace ImageSharp.Formats public class GifDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel { IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); - this.Decode(image, stream, gifOptions); + return this.Decode(stream, gifOptions); } /// /// Decodes the image from the specified stream to the . /// /// The pixel format. - /// The to decode to. /// The containing image data. /// The options for the decoder. - public void Decode(Image image, Stream stream, IGifDecoderOptions options) + /// The image thats been decoded. + public Image Decode(Stream stream, IGifDecoderOptions options) where TColor : struct, IPixel { - new GifDecoderCore(options).Decode(image, stream); + return new GifDecoderCore(options).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index ab1edc2c7..22a26345f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -27,11 +27,6 @@ namespace ImageSharp.Formats /// private readonly IGifDecoderOptions options; - /// - /// The image to decode the information to. - /// - private Image decodedImage; - /// /// The currently loaded stream. /// @@ -67,6 +62,16 @@ namespace ImageSharp.Formats /// private GifGraphicsControlExtension graphicsControlExtension; + /// + /// The metadata + /// + private ImageMetaData metaData; + + /// + /// The image to decode the information to. + /// + private Image image; + /// /// Initializes a new instance of the class. /// @@ -79,13 +84,13 @@ namespace ImageSharp.Formats /// /// Decodes the stream to the image. /// - /// The image to decode to. /// The stream containing image data. - public void Decode(Image image, Stream stream) + /// The decoded image + public Image Decode(Stream stream) { try { - this.decodedImage = image; + this.metaData = new ImageMetaData(); this.currentStream = stream; @@ -144,6 +149,8 @@ namespace ImageSharp.Formats ArrayPool.Shared.Return(this.globalColorTable); } } + + return this.image; } /// @@ -212,11 +219,13 @@ namespace ImageSharp.Formats throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); } - if (this.logicalScreenDescriptor.Width > this.decodedImage.MaxWidth || this.logicalScreenDescriptor.Height > this.decodedImage.MaxHeight) + /* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short. + if (this.logicalScreenDescriptor.Width > Image.MaxWidth || this.logicalScreenDescriptor.Height > Image.MaxHeight) { throw new ArgumentOutOfRangeException( - $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{this.decodedImage.MaxWidth}x{this.decodedImage.MaxHeight}'"); + $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } + */ } /// @@ -261,7 +270,7 @@ namespace ImageSharp.Formats { this.currentStream.Read(commentsBuffer, 0, length); string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length); - this.decodedImage.MetaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); + this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); } finally { @@ -343,14 +352,15 @@ namespace ImageSharp.Formats if (this.previousFrame == null) { - this.decodedImage.MetaData.Quality = colorTableLength / 3; + this.metaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - this.decodedImage.InitPixels(imageWidth, imageHeight); + this.image = new Image(imageWidth, imageHeight); + this.image.MetaData.LoadFrom(this.metaData); - this.SetFrameDelay(this.decodedImage.MetaData); + this.SetFrameDelay(this.metaData); - image = this.decodedImage; + image = this.image; } else { @@ -368,7 +378,7 @@ namespace ImageSharp.Formats this.RestoreToBackground(image); - this.decodedImage.Frames.Add(currentFrame); + this.image.Frames.Add(currentFrame); } int i = 0; @@ -441,7 +451,7 @@ namespace ImageSharp.Formats return; } - this.previousFrame = currentFrame == null ? this.decodedImage.ToFrame() : currentFrame; + this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) @@ -462,8 +472,8 @@ namespace ImageSharp.Formats } // Optimization for when the size of the frame is the same as the image size. - if (this.restoreArea.Value.Width == this.decodedImage.Width && - this.restoreArea.Value.Height == this.decodedImage.Height) + if (this.restoreArea.Value.Width == this.image.Width && + this.restoreArea.Value.Height == this.image.Height) { using (PixelAccessor pixelAccessor = frame.Lock()) { diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index df98870dd..c4a9cf8c3 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -17,10 +17,10 @@ namespace ImageSharp.Formats /// Decodes the image from the specified stream to the . /// /// The pixel format. - /// The to decode to. /// The containing image data. /// The options for the decoder. - void Decode(Image image, Stream stream, IDecoderOptions options) + /// The decoded image + Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index eeb371d1e..3a91f8010 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -14,15 +14,14 @@ namespace ImageSharp.Formats public class JpegDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); using (JpegDecoderCore decoder = new JpegDecoderCore(options)) { - decoder.Decode(image, stream, false); + return decoder.Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index f1b85fa0b..fa656e71e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -180,18 +180,30 @@ namespace ImageSharp.Formats /// the data to image. /// /// The pixel format. - /// The image, where the data should be set to. /// The stream, where the image should be. - /// Whether to decode metadata only. - public void Decode(Image image, Stream stream, bool metadataOnly) + /// The decoded image. + public Image Decode(Stream stream) where TColor : struct, IPixel { - this.ProcessStream(image, stream, metadataOnly); - if (!metadataOnly) - { - this.ProcessBlocksIntoJpegImageChannels(); - this.ConvertJpegPixelsToImagePixels(image); - } + ImageMetaData metadata = new ImageMetaData(); + this.ProcessStream(metadata, stream, false); + this.ProcessBlocksIntoJpegImageChannels(); + Image image = this.ConvertJpegPixelsToImagePixels(metadata); + + return image; + } + + /// + /// Decodes the image from the specified and sets + /// the data to image. + /// + /// The stream, where the image should be. + /// The image metadata. + public ImageMetaData DecodeMetaData(Stream stream) + { + ImageMetaData metadata = new ImageMetaData(); + this.ProcessStream(metadata, stream, true); + return metadata; } /// @@ -276,12 +288,10 @@ namespace ImageSharp.Formats /// /// Read metadata from stream and read the blocks in the scans into . /// - /// The pixel type - /// The + /// The metadata /// The stream /// Whether to decode metadata only. - private void ProcessStream(Image image, Stream stream, bool metadataOnly) - where TColor : struct, IPixel + private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly) { this.InputStream = stream; this.InputProcessor = new InputProcessor(stream, this.Temp); @@ -429,7 +439,7 @@ namespace ImageSharp.Formats this.ProcessApplicationHeader(remaining); break; case JpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining, image); + this.ProcessApp1Marker(remaining, metadata); break; case JpegConstants.Markers.APP14: this.ProcessApp14Marker(remaining); @@ -496,13 +506,18 @@ namespace ImageSharp.Formats /// Convert the pixel data in and/or into pixels of /// /// The pixel type - /// The destination image - private void ConvertJpegPixelsToImagePixels(Image image) + /// The metadata for the image. + /// The decoded image. + private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) where TColor : struct, IPixel { + Image image = new Image(this.ImageWidth, this.ImageHeight); + image.MetaData.LoadFrom(metadata); + if (this.grayImage.IsInitialized) { - this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image); + this.ConvertFromGrayScale(image); + return image; } else if (this.ycbcrImage != null) { @@ -519,27 +534,27 @@ namespace ImageSharp.Formats // TODO: YCbCrA? if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck) { - this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image); + this.ConvertFromYcck(image); } else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) { // Assume CMYK - this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image); + this.ConvertFromCmyk(image); } - return; + return image; } if (this.ComponentCount == 3) { if (this.IsRGB()) { - this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image); - return; + this.ConvertFromRGB(image); + return image; } - this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image); - return; + this.ConvertFromYCbCr(image); + return image; } throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces."); @@ -582,28 +597,24 @@ namespace ImageSharp.Formats /// Converts the image from the original CMYK image pixels. /// /// The pixel format. - /// The image width. - /// The image height. /// The image. - private void ConvertFromCmyk(int width, int height, Image image) + private void ConvertFromCmyk(Image image) where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - image.InitPixels(width, height); - using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, - height, + image.Height, y => { // TODO: Simplify + optimize + share duplicate code across converter methods int yo = this.ycbcrImage.GetRowYOffset(y); int co = this.ycbcrImage.GetRowCOffset(y); - for (int x = 0; x < width; x++) + for (int x = 0; x < image.Width; x++) { byte cyan = this.ycbcrImage.YChannel.Pixels[yo + x]; byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; @@ -623,24 +634,20 @@ namespace ImageSharp.Formats /// Converts the image from the original grayscale image pixels. /// /// The pixel format. - /// The image width. - /// The image height. /// The image. - private void ConvertFromGrayScale(int width, int height, Image image) + private void ConvertFromGrayScale(Image image) where TColor : struct, IPixel { - image.InitPixels(width, height); - using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, - height, + image.Height, image.Configuration.ParallelOptions, y => { int yoff = this.grayImage.GetRowOffset(y); - for (int x = 0; x < width; x++) + for (int x = 0; x < image.Width; x++) { byte rgb = this.grayImage.Pixels[yoff + x]; @@ -658,20 +665,17 @@ namespace ImageSharp.Formats /// Converts the image from the original RBG image pixels. /// /// The pixel format. - /// The image width. - /// The height. /// The image. - private void ConvertFromRGB(int width, int height, Image image) + private void ConvertFromRGB(Image image) where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - image.InitPixels(width, height); using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, - height, + image.Height, image.Configuration.ParallelOptions, y => { @@ -679,7 +683,7 @@ namespace ImageSharp.Formats int yo = this.ycbcrImage.GetRowYOffset(y); int co = this.ycbcrImage.GetRowCOffset(y); - for (int x = 0; x < width; x++) + for (int x = 0; x < image.Width; x++) { byte red = this.ycbcrImage.YChannel.Pixels[yo + x]; byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; @@ -699,20 +703,16 @@ namespace ImageSharp.Formats /// Converts the image from the original YCbCr image pixels. /// /// The pixel format. - /// The image width. - /// The image height. /// The image. - private void ConvertFromYCbCr(int width, int height, Image image) + private void ConvertFromYCbCr(Image image) where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - image.InitPixels(width, height); - using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, - height, + image.Height, image.Configuration.ParallelOptions, y => { @@ -720,7 +720,7 @@ namespace ImageSharp.Formats int yo = this.ycbcrImage.GetRowYOffset(y); int co = this.ycbcrImage.GetRowCOffset(y); - for (int x = 0; x < width; x++) + for (int x = 0; x < image.Width; x++) { byte yy = this.ycbcrImage.YChannel.Pixels[yo + x]; byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; @@ -740,28 +740,24 @@ namespace ImageSharp.Formats /// Converts the image from the original YCCK image pixels. /// /// The pixel format. - /// The image width. - /// The image height. /// The image. - private void ConvertFromYcck(int width, int height, Image image) + private void ConvertFromYcck(Image image) where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - image.InitPixels(width, height); - using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, - height, + image.Height, y => { // TODO: Simplify + optimize + share duplicate code across converter methods int yo = this.ycbcrImage.GetRowYOffset(y); int co = this.ycbcrImage.GetRowCOffset(y); - for (int x = 0; x < width; x++) + for (int x = 0; x < image.Width; x++) { byte yy = this.ycbcrImage.YChannel.Pixels[yo + x]; byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; @@ -959,11 +955,9 @@ namespace ImageSharp.Formats /// /// Processes the App1 marker retrieving any stored metadata /// - /// The pixel format. /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp1Marker(int remaining, Image image) - where TColor : struct, IPixel + /// The image. + private void ProcessApp1Marker(int remaining, ImageMetaData metadata) { if (remaining < 6 || this.options.IgnoreMetadata) { @@ -978,7 +972,7 @@ namespace ImageSharp.Formats && profile[5] == '\0') { this.isExif = true; - image.MetaData.ExifProfile = new ExifProfile(profile); + metadata.ExifProfile = new ExifProfile(profile); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index d527e1654..5b7d97fc7 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -31,25 +31,25 @@ namespace ImageSharp.Formats public class PngDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel { IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); - this.Decode(image, stream, pngOptions); + return this.Decode(stream, pngOptions); } /// /// Decodes the image from the specified stream to the . /// /// The pixel format. - /// The to decode to. /// The containing image data. /// The options for the decoder. - public void Decode(Image image, Stream stream, IPngDecoderOptions options) + /// The decoded image. + public Image Decode(Stream stream, IPngDecoderOptions options) where TColor : struct, IPixel { - new PngDecoderCore(options).Decode(image, stream); + return new PngDecoderCore(options).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index fd03ed39b..dadf7ab7d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -148,7 +148,6 @@ namespace ImageSharp.Formats /// Decodes the stream to the image. /// /// The pixel format. - /// The image to decode to. /// The stream containing image data. /// /// Thrown if the stream does not contain and end chunk. @@ -156,10 +155,11 @@ namespace ImageSharp.Formats /// /// Thrown if the image is larger than the maximum allowable size. /// - public void Decode(Image image, Stream stream) + /// The decoded image + public Image Decode(Stream stream) where TColor : struct, IPixel { - Image currentImage = image; + ImageMetaData metadata = new ImageMetaData(); this.currentStream = stream; this.currentStream.Skip(8); @@ -177,7 +177,7 @@ namespace ImageSharp.Formats this.ValidateHeader(); break; case PngChunkTypes.Physical: - this.ReadPhysicalChunk(currentImage, currentChunk.Data); + this.ReadPhysicalChunk(metadata, currentChunk.Data); break; case PngChunkTypes.Data: dataStream.Write(currentChunk.Data, 0, currentChunk.Length); @@ -186,7 +186,7 @@ namespace ImageSharp.Formats byte[] pal = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); this.palette = pal; - image.MetaData.Quality = pal.Length / 3; + metadata.Quality = pal.Length / 3; break; case PngChunkTypes.PaletteAlpha: byte[] alpha = new byte[currentChunk.Length]; @@ -194,7 +194,7 @@ namespace ImageSharp.Formats this.paletteAlpha = alpha; break; case PngChunkTypes.Text: - this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length); + this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; @@ -208,17 +208,20 @@ namespace ImageSharp.Formats } } - if (this.header.Width > image.MaxWidth || this.header.Height > image.MaxHeight) + if (this.header.Width > Image.MaxWidth || this.header.Height > Image.MaxHeight) { - throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'"); + throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - image.InitPixels(this.header.Width, this.header.Height); + Image image = new Image(this.header.Width, this.header.Height); + image.MetaData.LoadFrom(metadata); using (PixelAccessor pixels = image.Lock()) { this.ReadScanlines(dataStream, pixels); } + + return image; } } @@ -270,18 +273,16 @@ namespace ImageSharp.Formats /// /// Reads the data chunk containing physical dimension data. /// - /// The pixel format. - /// The image to read to. + /// The metadata to read to. /// The data containing physical data. - private void ReadPhysicalChunk(Image image, byte[] data) - where TColor : struct, IPixel + private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data) { data.ReverseBytes(0, 4); data.ReverseBytes(4, 4); // 39.3700787 = inches in a meter. - image.MetaData.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d; - image.MetaData.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; + metadata.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d; + metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; } /// @@ -768,12 +769,10 @@ namespace ImageSharp.Formats /// /// Reads a text chunk containing image properties from the data. /// - /// The pixel format. - /// The image to decode to. + /// The metadata to decode to. /// The containing data. /// The maximum length to read. - private void ReadTextChunk(Image image, byte[] data, int length) - where TColor : struct, IPixel + private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length) { if (this.options.IgnoreMetadata) { @@ -794,7 +793,7 @@ namespace ImageSharp.Formats string name = this.options.TextEncoding.GetString(data, 0, zeroIndex); string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1); - image.MetaData.Properties.Add(new ImageProperty(name, value)); + metadata.Properties.Add(new ImageProperty(name, value)); } /// diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs new file mode 100644 index 000000000..f31f2c0a5 --- /dev/null +++ b/src/ImageSharp/Image.Decode.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using Formats; + + /// + /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha + /// packed into a single unsigned integer value. + /// + public sealed partial class Image + { + /// + /// Decodes the image stream to the current image. + /// + /// The pixel format. + /// The stream. + /// The options for the decoder. + /// the configuration. + /// The decoded image + /// + /// [true] if can successfull decode the image otherwise [false]. + /// + private static bool Decode(Stream stream, IDecoderOptions options, Configuration config, out Image img) + where TColor : struct, IPixel + { + img = null; + int maxHeaderSize = config.MaxHeaderSize; + if (maxHeaderSize <= 0) + { + return false; + } + + IImageFormat format; + byte[] header = ArrayPool.Shared.Rent(maxHeaderSize); + try + { + long startPosition = stream.Position; + stream.Read(header, 0, maxHeaderSize); + stream.Position = startPosition; + format = config.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header)); + } + finally + { + ArrayPool.Shared.Return(header); + } + + if (format == null) + { + return false; + } + + img = format.Decoder.Decode(stream, options); + img.CurrentImageFormat = format; + return true; + } + } +} diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs new file mode 100644 index 000000000..e68a57fe7 --- /dev/null +++ b/src/ImageSharp/Image.FromBytes.cs @@ -0,0 +1,148 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using Formats; + + /// + /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha + /// packed into a single unsigned integer value. + /// + public sealed partial class Image + { + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream) + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, IDecoderOptions options) + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, Configuration config) + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, IDecoderOptions options, Configuration config) + { + using (MemoryStream ms = new MemoryStream(stream)) + { + return Load(ms, options, config); + } + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream) + where TColor : struct, IPixel + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, IDecoderOptions options) + where TColor : struct, IPixel + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, Configuration config) + where TColor : struct, IPixel + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] stream, IDecoderOptions options, Configuration config) + where TColor : struct, IPixel + { + using (MemoryStream ms = new MemoryStream(stream)) + { + return Load(ms, options, config); + } + } + } +} diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs new file mode 100644 index 000000000..b0adb1f96 --- /dev/null +++ b/src/ImageSharp/Image.FromFile.cs @@ -0,0 +1,148 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using Formats; + +#if !NETSTANDARD1_1 + /// + /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha + /// packed into a single unsigned integer value. + /// + public sealed partial class Image + { + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream) + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, IDecoderOptions options) + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, Configuration config) + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, IDecoderOptions options, Configuration config) + { + return new Image(Image.Load(stream, options, config)); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream) + where TColor : struct, IPixel + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, IDecoderOptions options) + where TColor : struct, IPixel + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, Configuration config) + where TColor : struct, IPixel + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string stream, IDecoderOptions options, Configuration config) + where TColor : struct, IPixel + { + config = config ?? Configuration.Default; + using (Stream s = config.FileSystem.OpenRead(stream)) + { + return Load(s, options, config); + } + } + } +#endif +} diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs new file mode 100644 index 000000000..36309db5b --- /dev/null +++ b/src/ImageSharp/Image.FromStream.cs @@ -0,0 +1,184 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using Formats; + + /// + /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha + /// packed into a single unsigned integer value. + /// + public sealed partial class Image + { + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream) + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IDecoderOptions options) + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, Configuration config) + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IDecoderOptions options, Configuration config) + { + return new Image(Load(stream, options, config)); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream) + where TColor : struct, IPixel + { + return Load(stream, null, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IDecoderOptions options) + where TColor : struct, IPixel + { + return Load(stream, options, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The config for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, Configuration config) + where TColor : struct, IPixel + { + return Load(stream, null, config); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The options for the decoder. + /// The configuration options. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IDecoderOptions options, Configuration config) + where TColor : struct, IPixel + { + config = config ?? Configuration.Default; + + if (!config.ImageFormats.Any()) + { + throw new InvalidOperationException("No image formats have been configured."); + } + + if (!stream.CanRead) + { + throw new NotSupportedException("Cannot read from the stream."); + } + + if (stream.CanSeek) + { + if (Decode(stream, options, config, out Image img)) + { + return img; + } + } + else + { + // We want to be able to load images from things like HttpContext.Request.Body + using (MemoryStream ms = new MemoryStream()) + { + stream.CopyTo(ms); + ms.Position = 0; + + if (Decode(ms, options, config, out Image img)) + { + return img; + } + } + } + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); + + foreach (IImageFormat format in config.ImageFormats) + { + stringBuilder.AppendLine("-" + format); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + } +} diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 8bfd8ee1a..352107021 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System; using System.Diagnostics; using System.IO; @@ -15,7 +16,7 @@ namespace ImageSharp /// packed into a single unsigned integer value. /// [DebuggerDisplay("Image: {Width}x{Height}")] - public sealed class Image : Image + public sealed partial class Image : Image { /// /// Initializes a new instance of the class @@ -26,190 +27,19 @@ namespace ImageSharp /// /// The configuration providing initialization code which allows extending the library. /// - public Image(int width, int height, Configuration configuration = null) + public Image(int width, int height, Configuration configuration) : base(width, height, configuration) { } /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// Thrown if the is null. - public Image(Stream stream) - : base(stream, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options) - : base(stream, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(Stream stream, Configuration configuration) - : base(stream, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options, Configuration configuration) - : base(stream, options, configuration) - { - } - -#if !NETSTANDARD1_1 - /// - /// Initializes a new instance of the class. - /// - /// - /// A file path to read image information. - /// - /// Thrown if the is null. - public Image(string filePath) - : base(filePath, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// A file path to read image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options) - : base(filePath, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// A file path to read image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(string filePath, Configuration configuration) - : base(filePath, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// A file path to read image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options, Configuration configuration) - : base(filePath, options, configuration) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// Thrown if the is null. - public Image(byte[] bytes) - : base(bytes, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options) - : base(bytes, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(byte[] bytes, Configuration configuration) - : base(bytes, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class + /// with the height and the width of the image. /// - /// - /// The byte array containing image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options, Configuration configuration) - : base(bytes, options, configuration) + /// The width of the image in pixels. + /// The height of the image in pixels. + public Image(int width, int height) + : this(width, height, null) { } @@ -219,7 +49,7 @@ namespace ImageSharp /// /// The other image, where the clone should be made from. /// is null. - public Image(Image other) + internal Image(Image other) : base(other) { } diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs index 707fea235..393d83077 100644 --- a/src/ImageSharp/Image/IImageBase.cs +++ b/src/ImageSharp/Image/IImageBase.cs @@ -15,16 +15,6 @@ namespace ImageSharp /// Rectangle Bounds { get; } - /// - /// Gets or sets the maximum allowable width in pixels. - /// - int MaxWidth { get; set; } - - /// - /// Gets or sets the maximum allowable height in pixels. - /// - int MaxHeight { get; set; } - /// /// Gets the width in pixels. /// diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index e894fba4a..14bdffc67 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -21,16 +21,6 @@ namespace ImageSharp /// TColor[] Pixels { get; } - /// - /// Sets the size of the pixel array of the image to the given width and height. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// - /// Thrown if either or are less than or equal to 0. - /// - void InitPixels(int width, int height); - /// /// Locks the image providing access to the pixels. /// diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 878ba09b3..cfce7184b 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -18,6 +18,16 @@ namespace ImageSharp public abstract class ImageBase : IImageBase where TColor : struct, IPixel { + /// + /// Gets or sets the maximum allowable width in pixels. + /// + public const int MaxWidth = int.MaxValue; + + /// + /// Gets or sets the maximum allowable height in pixels. + /// + public const int MaxHeight = int.MaxValue; + /// /// The image pixels /// @@ -40,7 +50,7 @@ namespace ImageSharp /// /// The configuration providing initialization code which allows extending the library. /// - protected ImageBase(Configuration configuration = null) + protected ImageBase(Configuration configuration) { this.Configuration = configuration ?? Configuration.Default; } @@ -56,10 +66,15 @@ namespace ImageSharp /// /// Thrown if either or are less than or equal to 0. /// - protected ImageBase(int width, int height, Configuration configuration = null) + protected ImageBase(int width, int height, Configuration configuration) + : this(configuration) { - this.Configuration = configuration ?? Configuration.Default; - this.InitPixels(width, height); + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + this.Width = width; + this.Height = height; + this.RentPixels(); this.ClearPixels(); } @@ -73,6 +88,7 @@ namespace ImageSharp /// Thrown if the given is null. /// protected ImageBase(ImageBase other) + : this(other.Configuration) { Guard.NotNull(other, nameof(other), "Other image cannot be null."); @@ -90,12 +106,6 @@ namespace ImageSharp } } - /// - public int MaxWidth { get; set; } = int.MaxValue; - - /// - public int MaxHeight { get; set; } = int.MaxValue; - /// public TColor[] Pixels => this.pixelBuffer; @@ -139,17 +149,6 @@ namespace ImageSharp GC.SuppressFinalize(this); } - /// - public void InitPixels(int width, int height) - { - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - this.Width = width; - this.Height = height; - this.RentPixels(); - } - /// public PixelAccessor Lock() { diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 34724cc97..82991948c 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -35,7 +35,7 @@ namespace ImageSharp /// /// The configuration providing initialization code which allows extending the library. /// - public Image(int width, int height, Configuration configuration = null) + public Image(int width, int height, Configuration configuration) : base(width, height, configuration) { if (!this.Configuration.ImageFormats.Any()) @@ -43,203 +43,19 @@ namespace ImageSharp throw new InvalidOperationException("No image formats have been configured."); } + this.MetaData = new ImageMetaData(); this.CurrentImageFormat = this.Configuration.ImageFormats.First(); } /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// Thrown if the is null. - public Image(Stream stream) - : this(stream, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options) - : this(stream, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(Stream stream, Configuration configuration) - : this(stream, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The stream containing image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options, Configuration configuration) - : base(configuration) - { - Guard.NotNull(stream, nameof(stream)); - this.Load(stream, options); - } - -#if !NETSTANDARD1_1 - /// - /// Initializes a new instance of the class. - /// - /// - /// The file containing image information. - /// - /// Thrown if the is null. - public Image(string filePath) - : this(filePath, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The file containing image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options) - : this(filePath, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The file containing image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(string filePath, Configuration configuration) - : this(filePath, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The file containing image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options, Configuration configuration) - : base(configuration) - { - Guard.NotNull(filePath, nameof(filePath)); - - using (Stream fs = this.Configuration.FileSystem.OpenRead(filePath)) - { - this.Load(fs, options); - } - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// Thrown if the is null. - public Image(byte[] bytes) - : this(bytes, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// - /// The options for the decoder. - /// - /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options) - : this(bytes, options, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The byte array containing image information. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(byte[] bytes, Configuration configuration) - : this(bytes, null, configuration) - { - } - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class + /// with the height and the width of the image. /// - /// - /// The byte array containing image information. - /// - /// - /// The options for the decoder. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options, Configuration configuration) - : base(configuration) + /// The width of the image in pixels. + /// The height of the image in pixels. + public Image(int width, int height) + : this(width, height, null) { - Guard.NotNull(bytes, nameof(bytes)); - - using (MemoryStream stream = new MemoryStream(bytes, false)) - { - this.Load(stream, options); - } } /// @@ -271,7 +87,6 @@ namespace ImageSharp public Image(ImageBase other) : base(other) { - this.CopyProperties(other); } /// @@ -588,103 +403,8 @@ namespace ImageSharp /// private void CopyProperties(IImage other) { - base.CopyProperties(other); - this.CurrentImageFormat = other.CurrentImageFormat; this.MetaData = new ImageMetaData(other.MetaData); } - - /// - /// Loads the image from the given stream. - /// - /// The stream containing image information. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - private void Load(Stream stream, IDecoderOptions options) - { - if (!this.Configuration.ImageFormats.Any()) - { - throw new InvalidOperationException("No image formats have been configured."); - } - - if (!stream.CanRead) - { - throw new NotSupportedException("Cannot read from the stream."); - } - - if (stream.CanSeek) - { - if (this.Decode(stream, options)) - { - return; - } - } - else - { - // We want to be able to load images from things like HttpContext.Request.Body - using (MemoryStream ms = new MemoryStream()) - { - stream.CopyTo(ms); - ms.Position = 0; - - if (this.Decode(ms, options)) - { - return; - } - } - } - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); - - foreach (IImageFormat format in this.Configuration.ImageFormats) - { - stringBuilder.AppendLine("-" + format); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } - - /// - /// Decodes the image stream to the current image. - /// - /// The stream. - /// The options for the decoder. - /// - /// The . - /// - private bool Decode(Stream stream, IDecoderOptions options) - { - int maxHeaderSize = this.Configuration.MaxHeaderSize; - if (maxHeaderSize <= 0) - { - return false; - } - - IImageFormat format; - byte[] header = ArrayPool.Shared.Rent(maxHeaderSize); - try - { - long startPosition = stream.Position; - stream.Read(header, 0, maxHeaderSize); - stream.Position = startPosition; - format = this.Configuration.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header)); - } - finally - { - ArrayPool.Shared.Return(header); - } - - if (format == null) - { - return false; - } - - format.Decoder.Decode(this, stream, options); - this.CurrentImageFormat = format; - return true; - } } } \ No newline at end of file diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index de1e42476..d7d5e88b5 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System; using System.Collections.Generic; /// @@ -47,21 +48,7 @@ namespace ImageSharp { DebugGuard.NotNull(other, nameof(other)); - this.HorizontalResolution = other.HorizontalResolution; - this.VerticalResolution = other.VerticalResolution; - this.Quality = other.Quality; - this.FrameDelay = other.FrameDelay; - this.RepeatCount = other.RepeatCount; - - foreach (ImageProperty property in other.Properties) - { - this.Properties.Add(new ImageProperty(property)); - } - - if (other.ExifProfile != null) - { - this.ExifProfile = new ExifProfile(other.ExifProfile); - } + this.LoadFrom(other); } /// @@ -143,5 +130,32 @@ namespace ImageSharp { this.ExifProfile?.Sync(this); } + + /// + /// Sets the current metadata values based on a previous metadata object. + /// + /// Meta data object to copy values from. + internal void LoadFrom(ImageMetaData other) + { + this.HorizontalResolution = other.HorizontalResolution; + this.VerticalResolution = other.VerticalResolution; + this.Quality = other.Quality; + this.FrameDelay = other.FrameDelay; + this.RepeatCount = other.RepeatCount; + + foreach (ImageProperty property in other.Properties) + { + this.Properties.Add(new ImageProperty(property)); + } + + if (other.ExifProfile != null) + { + this.ExifProfile = new ExifProfile(other.ExifProfile); + } + else + { + this.ExifProfile = null; + } + } } } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index b363286b0..c4a94c5ff 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -137,7 +137,7 @@ namespace ImageSharp using (MemoryStream memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength)) { - return new Image(memStream); + return Image.Load(memStream); } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index 431bbeb07..acde8e0db 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -43,7 +43,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes)) { - using (CoreImage image = new CoreImage(memoryStream)) + using (CoreImage image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index 517915bac..6786cfdc0 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Benchmarks.Image private Size LoadPng(MemoryStream stream) { - using (Image image = new Image(stream)) + using (Image image = Image.Load(stream)) { return new Size(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index cb70213da..a9bb4c7b3 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -43,7 +43,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.gifBytes)) { - using (CoreImage image = new CoreImage(memoryStream)) + using (CoreImage image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs index cbbe9c9f2..6ce230370 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs @@ -43,7 +43,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) { - using (CoreImage image = new CoreImage(memoryStream)) + using (CoreImage image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs index c79d61538..5c3c1e115 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image public void DecodeJpegImageSharp() { this.ForEachStream( - ms => new ImageSharp.Image(ms) + ms => ImageSharp.Image.Load(ms) ); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index 79c8dbc23..620a48a3b 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -43,7 +43,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) { - using (CoreImage image = new CoreImage(memoryStream)) + using (CoreImage image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index b0a3b4499..6ed577338 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = new CoreImage(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index 0810f3fe1..fabeba1bd 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = new CoreImage(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index ba383873c..1318c1674 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -34,7 +34,7 @@ namespace ImageSharp.Benchmarks.Image ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; this.bmpStream = File.OpenRead(path); - this.bmpCore = new Image(this.bmpStream); + this.bmpCore = Image.Load(this.bmpStream); this.bmpStream.Position = 0; } } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs index f835f9666..7649812ec 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = new CoreImage(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 0bb0e922c..4c1feb6c2 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -38,7 +38,7 @@ namespace ImageSharp.Benchmarks.Image ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; this.bmpStream = File.OpenRead(path); - this.bmpCore = new CoreImage(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs index 4fee634f5..a084ca025 100644 --- a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs @@ -154,7 +154,7 @@ namespace ImageSharp.Benchmarks.Image using (MemoryStream ms1 = new MemoryStream(bytes)) { - this.FileNamesToImageSharpImages[fn] = new Image(ms1); + this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); } diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index 28bec5124..28661b9d6 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -23,7 +23,7 @@ namespace ImageSharp.Benchmarks { using (FileStream stream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp")) { - this.image = new CoreImage(stream); + this.image = CoreImage.Load(stream); } } } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index ae795c2ec..1ecd04690 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -150,7 +150,7 @@ namespace ImageSharp.Tests serialized = memoryStream.ToArray(); } - using (Image image2 = new Image(serialized)) + using (Image image2 = Image.Load(serialized)) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image2.Save(output); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index a140b7a3c..897778bc3 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Tests input.Save(memStream, new GifFormat(), options); memStream.Position = 0; - using (Image output = new Image(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(1, output.MetaData.Properties.Count); Assert.Equal("Comments", output.MetaData.Properties[0].Name); @@ -56,7 +56,7 @@ namespace ImageSharp.Tests input.SaveAsGif(memStream, options); memStream.Position = 0; - using (Image output = new Image(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(0, output.MetaData.Properties.Count); } @@ -77,7 +77,7 @@ namespace ImageSharp.Tests input.Save(memStream, new GifFormat()); memStream.Position = 0; - using (Image output = new Image(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(1, output.MetaData.Properties.Count); Assert.Equal("Comments", output.MetaData.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 5723f9b23..416c88a50 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -89,11 +89,10 @@ namespace ImageSharp.Tests { image.Save(ms, new JpegEncoder()); ms.Seek(0, SeekOrigin.Begin); - - Image mirror = provider.Factory.CreateImage(1, 1); + using (JpegDecoderCore decoder = new JpegDecoderCore(null)) { - decoder.Decode(mirror, ms, true); + Image mirror = decoder.Decode(ms); Assert.Equal(decoder.ImageWidth, image.Width); Assert.Equal(decoder.ImageHeight, image.Height); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index c97eb1461..0833cb868 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -88,7 +88,7 @@ namespace ImageSharp.Tests input.Save(memStream, new JpegFormat(), options); memStream.Position = 0; - using (Image output = new Image(memStream)) + using (Image output = Image.Load(memStream)) { Assert.NotNull(output.MetaData.ExifProfile); } @@ -113,7 +113,7 @@ namespace ImageSharp.Tests input.SaveAsJpeg(memStream, options); memStream.Position = 0; - using (Image output = new Image(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Null(output.MetaData.ExifProfile); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 50e678bf0..28a64a765 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -50,7 +50,7 @@ namespace ImageSharp.Tests ExecutionCount, () => { - Image img = new Image(bytes); + Image img = Image.Load(bytes); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs new file mode 100644 index 000000000..2c97ea06f --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -0,0 +1,341 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + using ImageSharp.Formats; + using ImageSharp.IO; + using Moq; + using Xunit; + + /// + /// Tests the class. + /// + public class ImageLoadTests : IDisposable + { + private readonly Mock fileSystem; + private readonly IDecoderOptions decoderOptions; + private Image returnImage; + private Mock localDecoder; + private Mock localFormat; + private readonly string FilePath; + + public Configuration LocalConfiguration { get; private set; } + public byte[] Marker { get; private set; } + public MemoryStream DataStream { get; private set; } + public byte[] DecodedData { get; private set; } + + public ImageLoadTests() + { + this.returnImage = new Image(1, 1); + + this.localDecoder = new Mock(); + this.localFormat = new Mock(); + this.localFormat.Setup(x => x.Decoder).Returns(this.localDecoder.Object); + this.localFormat.Setup(x => x.Encoder).Returns(new Mock().Object); + this.localFormat.Setup(x => x.MimeType).Returns("img/test"); + this.localFormat.Setup(x => x.Extension).Returns("png"); + this.localFormat.Setup(x => x.HeaderSize).Returns(1); + this.localFormat.Setup(x => x.IsSupportedFileFormat(It.IsAny())).Returns(true); + this.localFormat.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) + .Callback((s, o) => { + using (var ms = new MemoryStream()) + { + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); + } + }) + .Returns(this.returnImage); + + this.fileSystem = new Mock(); + + this.LocalConfiguration = new Configuration(this.localFormat.Object) + { + FileSystem = this.fileSystem.Object + }; + TestFormat.RegisterGloablTestFormat(); + this.Marker = Guid.NewGuid().ToByteArray(); + this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker); + this.decoderOptions = new Mock().Object; + + this.FilePath = Guid.NewGuid().ToString(); + this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); + } + + [Fact] + public void LoadFromStream() + { + Image img = Image.Load(this.DataStream); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromStreamWithType() + { + Image img = Image.Load(this.DataStream); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromStreamWithOptions() + { + Image img = Image.Load(this.DataStream, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromStreamWithTypeAndOptions() + { + Image img = Image.Load(this.DataStream, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromStreamWithConfig() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(stream, null)); + } + + [Fact] + public void LoadFromStreamWithTypeAndConfig() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(stream, null)); + } + + [Fact] + public void LoadFromStreamWithConfigAndOptions() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + } + + [Fact] + public void LoadFromStreamWithTypeAndConfigAndOptions() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + } + + [Fact] + public void LoadFromBytes() + { + Image img = Image.Load(this.DataStream.ToArray()); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromBytesWithType() + { + Image img = Image.Load(this.DataStream.ToArray()); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromBytesWithOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromBytesWithTypeAndOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromBytesWithConfig() + { + Image img = Image.Load(this.DataStream.ToArray(), this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithTypeAndConfig() + { + Image img = Image.Load(this.DataStream.ToArray(), this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithConfigAndOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithTypeAndConfigAndOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromFile() + { + Image img = Image.Load(this.DataStream); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromFileWithType() + { + Image img = Image.Load(this.DataStream); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + } + + [Fact] + public void LoadFromFileWithOptions() + { + Image img = Image.Load(this.DataStream, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromFileWithTypeAndOptions() + { + Image img = Image.Load(this.DataStream, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + } + + [Fact] + public void LoadFromFileWithConfig() + { + Image img = Image.Load(this.FilePath, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + } + + [Fact] + public void LoadFromFileWithTypeAndConfig() + { + Image img = Image.Load(this.FilePath, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + } + + [Fact] + public void LoadFromFileWithConfigAndOptions() + { + Image img = Image.Load(this.FilePath, this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + } + + [Fact] + public void LoadFromFileWithTypeAndConfigAndOptions() + { + Image img = Image.Load(FilePath, this.decoderOptions, this.LocalConfiguration); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + } + + public void Dispose() + { + // clean up the global object; + this.returnImage?.Dispose(); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index b0a031a78..02b0e5ad9 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -21,11 +21,11 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - new Image((byte[])null); + Image.Load((byte[])null); }); TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = new Image(file.Bytes)) + using (Image image = Image.Load(file.Bytes)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -36,7 +36,7 @@ namespace ImageSharp.Tests public void ConstructorFileSystem() { TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = new Image(file.FilePath)) + using (Image image = Image.Load(file.FilePath)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -49,7 +49,7 @@ namespace ImageSharp.Tests System.IO.FileNotFoundException ex = Assert.Throws( () => { - new Image(Guid.NewGuid().ToString()); + Image.Load(Guid.NewGuid().ToString()); }); } @@ -59,7 +59,7 @@ namespace ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - new Image((string) null); + Image.Load((string) null); }); } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 785d9dcfc..1bc31286d 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -73,7 +73,7 @@ namespace ImageSharp.Tests image.SaveAsJpeg(memStream); memStream.Position = 0; - image = new Image(memStream); + image = Image.Load(memStream); profile = image.MetaData.ExifProfile; Assert.NotNull(profile); @@ -91,7 +91,7 @@ namespace ImageSharp.Tests image.SaveAsJpeg(memStream); memStream.Position = 0; - image = new Image(memStream); + image = Image.Load(memStream); profile = image.MetaData.ExifProfile; Assert.NotNull(profile); @@ -286,7 +286,7 @@ namespace ImageSharp.Tests image.Dispose(); memStream.Position = 0; - return new Image(memStream); + return Image.Load(memStream); } } diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 701025e87..eedc0d306 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -46,7 +46,7 @@ namespace ImageSharp.Tests this.file = file; this.Bytes = File.ReadAllBytes(file); - this.image = new Image(this.Bytes); + this.image = Image.Load(this.Bytes); } /// @@ -139,7 +139,7 @@ namespace ImageSharp.Tests /// public Image CreateImage(IDecoderOptions options) { - return new Image(this.Bytes, options); + return Image.Load(this.Bytes, options); } /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs new file mode 100644 index 000000000..3a40ed420 --- /dev/null +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -0,0 +1,174 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using ImageSharp.Formats; + using Xunit; + + /// + /// A test image file. + /// + public class TestFormat : ImageSharp.Formats.IImageFormat + { + public static TestFormat GlobalTestFormat { get; } = new TestFormat(); + + public static void RegisterGloablTestFormat() + { + Configuration.Default.AddImageFormat(GlobalTestFormat); + } + + public TestFormat() + { + this.Encoder = new TestEncoder(this); ; + this.Decoder = new TestDecoder(this); ; + } + + public List DecodeCalls { get; } = new List(); + + public IImageEncoder Encoder { get; } + + public IImageDecoder Decoder { get; } + + private byte[] header = Guid.NewGuid().ToByteArray(); + + public MemoryStream CreateStream(byte[] marker = null) + { + MemoryStream ms = new MemoryStream(); + byte[] data = this.header; + ms.Write(data, 0, data.Length); + if (marker != null) + { + ms.Write(marker, 0, marker.Length); + } + ms.Position = 0; + return ms; + } + + Dictionary _sampleImages = new Dictionary(); + + public void VerifyDecodeCall(byte[] marker, IDecoderOptions options) + { + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, options)).ToArray(); + + Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); + + foreach (DecodeOperation d in discovered) { + this.DecodeCalls.Remove(d); + } + } + + public Image Sample() + where TColor : struct, IPixel + { + lock (this._sampleImages) + { + if (!this._sampleImages.ContainsKey(typeof(TColor))) + { + this._sampleImages.Add(typeof(TColor), new Image(1, 1)); + } + + return (Image)this._sampleImages[typeof(TColor)]; + } + } + + public string MimeType => "img/test"; + + public string Extension => "test_ext"; + + public IEnumerable SupportedExtensions => new[] { "test_ext" }; + + public int HeaderSize => this.header.Length; + + public bool IsSupportedFileFormat(byte[] header) + { + if (header.Length < this.header.Length) + { + return false; + } + for (int i = 0; i < this.header.Length; i++) + { + if (header[i] != this.header[i]) + { + return false; + } + } + return true; + } + public struct DecodeOperation + { + public byte[] marker; + public IDecoderOptions options; + + public bool IsMatch(byte[] testMarker, IDecoderOptions testOptions) + { + if(this.options != testOptions) + { + return false; + } + + if (testMarker.Length != this.marker.Length) + { + return false; + } + + for (int i = 0; i < this.marker.Length; i++) + { + if (testMarker[i] != this.marker[i]) + { + return false; + } + } + return true; + } + } + + public class TestDecoder : ImageSharp.Formats.IImageDecoder + { + private TestFormat testFormat; + + public TestDecoder(TestFormat testFormat) + { + this.testFormat = testFormat; + } + + public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel + { + var ms = new MemoryStream(); + stream.CopyTo(ms); + var marker = ms.ToArray().Skip(this.testFormat.header.Length).ToArray(); + this.testFormat.DecodeCalls.Add(new DecodeOperation + { + marker = marker, + options = options + }); + + // TODO record this happend so we an verify it. + return this.testFormat.Sample(); + } + } + + public class TestEncoder : ImageSharp.Formats.IImageEncoder + { + private TestFormat testFormat; + + public TestEncoder(TestFormat testFormat) + { + this.testFormat = testFormat; + } + + public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel + { + // TODO record this happend so we an verify it. + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs index 87b7ace6a..c2fe0dc5c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Tests public virtual Image CreateImage(byte[] bytes) { - return new Image(bytes); + return Image.Load(bytes); } public virtual Image CreateImage(Image other) diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs index a8d398c1e..2361bc01c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Tests { public class ImageFactory : GenericFactory { - public override Image CreateImage(byte[] bytes) => new Image(bytes); + public override Image CreateImage(byte[] bytes) => Image.Load(bytes); public override Image CreateImage(int width, int height) => new Image(width, height); From 04dd7056648d8fbbb71f2f99dad25e94ce98fb4a Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Tue, 21 Mar 2017 18:37:45 +0000 Subject: [PATCH 02/18] fix parameter names --- src/ImageSharp/Image.FromBytes.cs | 64 +++++++++++++++---------------- src/ImageSharp/Image.FromFile.cs | 64 +++++++++++++++---------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index e68a57fe7..3f1a3031c 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -20,126 +20,126 @@ namespace ImageSharp public sealed partial class Image { /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// - /// The stream containing image information. + /// The byte array containing image data. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream) + public static Image Load(byte[] data) { - return Load(stream, null, null); + return Load(data, null, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// - /// The stream containing image information. + /// The byte array containing image data. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, IDecoderOptions options) + public static Image Load(byte[] data, IDecoderOptions options) { - return Load(stream, options, null); + return Load(data, options, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// - /// The stream containing image information. + /// The byte array containing image data. /// The config for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, Configuration config) + public static Image Load(byte[] data, Configuration config) { - return Load(stream, null, config); + return Load(data, null, config); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// - /// The stream containing image information. + /// The byte array containing image data. /// The options for the decoder. /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, IDecoderOptions options, Configuration config) + public static Image Load(byte[] data, IDecoderOptions options, Configuration config) { - using (MemoryStream ms = new MemoryStream(stream)) + using (MemoryStream ms = new MemoryStream(data)) { return Load(ms, options, config); } } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// /// The pixel format. - /// The stream containing image information. + /// The byte array containing image data. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream) + public static Image Load(byte[] data) where TColor : struct, IPixel { - return Load(stream, null, null); + return Load(data, null, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// /// The pixel format. - /// The stream containing image information. + /// The byte array containing image data. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, IDecoderOptions options) + public static Image Load(byte[] data, IDecoderOptions options) where TColor : struct, IPixel { - return Load(stream, options, null); + return Load(data, options, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// /// The pixel format. - /// The stream containing image information. + /// The byte array containing image data. /// The config for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, Configuration config) + public static Image Load(byte[] data, Configuration config) where TColor : struct, IPixel { - return Load(stream, null, config); + return Load(data, null, config); } /// - /// Loads the image from the given stream. + /// Loads the image from the given byte array. /// /// The pixel format. - /// The stream containing image information. + /// The byte array containing image data. /// The options for the decoder. /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] stream, IDecoderOptions options, Configuration config) + public static Image Load(byte[] data, IDecoderOptions options, Configuration config) where TColor : struct, IPixel { - using (MemoryStream ms = new MemoryStream(stream)) + using (MemoryStream ms = new MemoryStream(data)) { return Load(ms, options, config); } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index b0adb1f96..9b8ba83de 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -21,124 +21,124 @@ namespace ImageSharp public sealed partial class Image { /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// - /// The stream containing image information. + /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream) + public static Image Load(string path) { - return Load(stream, null, null); + return Load(path, null, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// - /// The stream containing image information. + /// The file path to the image. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, IDecoderOptions options) + public static Image Load(string path, IDecoderOptions options) { - return Load(stream, options, null); + return Load(path, options, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// - /// The stream containing image information. + /// The file path to the image. /// The config for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, Configuration config) + public static Image Load(string path, Configuration config) { - return Load(stream, null, config); + return Load(path, null, config); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// - /// The stream containing image information. + /// The file path to the image. /// The options for the decoder. /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, IDecoderOptions options, Configuration config) + public static Image Load(string path, IDecoderOptions options, Configuration config) { - return new Image(Image.Load(stream, options, config)); + return new Image(Load(path, options, config)); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// /// The pixel format. - /// The stream containing image information. + /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream) + public static Image Load(string path) where TColor : struct, IPixel { - return Load(stream, null, null); + return Load(path, null, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// /// The pixel format. - /// The stream containing image information. + /// The file path to the image. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, IDecoderOptions options) + public static Image Load(string path, IDecoderOptions options) where TColor : struct, IPixel { - return Load(stream, options, null); + return Load(path, options, null); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// /// The pixel format. - /// The stream containing image information. + /// The file path to the image. /// The config for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, Configuration config) + public static Image Load(string path, Configuration config) where TColor : struct, IPixel { - return Load(stream, null, config); + return Load(path, null, config); } /// - /// Loads the image from the given stream. + /// Loads the image from the given file. /// /// The pixel format. - /// The stream containing image information. + /// The file path to the image. /// The options for the decoder. /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string stream, IDecoderOptions options, Configuration config) + public static Image Load(string path, IDecoderOptions options, Configuration config) where TColor : struct, IPixel { config = config ?? Configuration.Default; - using (Stream s = config.FileSystem.OpenRead(stream)) + using (Stream s = config.FileSystem.OpenRead(path)) { return Load(s, options, config); } From 1343c78411010268a092ec0d83b3f4574daf4269 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 22 Mar 2017 06:58:16 +0000 Subject: [PATCH 03/18] Add IImageDecoder overloads Adds overloads the the Image.Load(..) methods that take an IImageDecoder. --- src/ImageSharp/Image.Decode.cs | 40 +++--- src/ImageSharp/Image.FromBytes.cs | 72 +++++++++- src/ImageSharp/Image.FromFile.cs | 70 ++++++++- src/ImageSharp/Image.FromStream.cs | 108 +++++++++++--- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 133 +++++++++++++++++- tests/ImageSharp.Tests/TestFileSystem.cs | 72 ++++++++++ 6 files changed, 449 insertions(+), 46 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestFileSystem.cs diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index f31f2c0a5..c95b71b1e 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -19,25 +19,12 @@ namespace ImageSharp /// public sealed partial class Image { - /// - /// Decodes the image stream to the current image. - /// - /// The pixel format. - /// The stream. - /// The options for the decoder. - /// the configuration. - /// The decoded image - /// - /// [true] if can successfull decode the image otherwise [false]. - /// - private static bool Decode(Stream stream, IDecoderOptions options, Configuration config, out Image img) - where TColor : struct, IPixel + private static IImageFormat DiscoverFormat(Stream stream, IDecoderOptions options, Configuration config) { - img = null; int maxHeaderSize = config.MaxHeaderSize; if (maxHeaderSize <= 0) { - return false; + return null; } IImageFormat format; @@ -54,14 +41,31 @@ namespace ImageSharp ArrayPool.Shared.Return(header); } + return format; + } + + /// + /// Decodes the image stream to the current image. + /// + /// The pixel format. + /// The stream. + /// The options for the decoder. + /// the configuration. + /// + /// The decoded image + /// + private static Image Decode(Stream stream, IDecoderOptions options, Configuration config) + where TColor : struct, IPixel + { + IImageFormat format = DiscoverFormat(stream, options, config); if (format == null) { - return false; + return null; } - img = format.Decoder.Decode(stream, options); + Image img = format.Decoder.Decode(stream, options); img.CurrentImageFormat = format; - return true; + return img; } } } diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 3f1a3031c..8e0ac04a5 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The image public static Image Load(byte[] data) { - return Load(data, null, null); + return Load(data, null, (Configuration)null); } /// @@ -60,6 +60,20 @@ namespace ImageSharp return Load(data, null, config); } + /// + /// Loads the image from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] data, IImageDecoder decoder) + { + return Load(data, decoder, null); + } + /// /// Loads the image from the given byte array. /// @@ -78,6 +92,24 @@ namespace ImageSharp } } + /// + /// Loads the image from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) + { + using (MemoryStream ms = new MemoryStream(data)) + { + return Load(ms, decoder, options); + } + } + /// /// Loads the image from the given byte array. /// @@ -90,7 +122,7 @@ namespace ImageSharp public static Image Load(byte[] data) where TColor : struct, IPixel { - return Load(data, null, null); + return Load(data, null, (Configuration)null); } /// @@ -125,6 +157,22 @@ namespace ImageSharp return Load(data, null, config); } + /// + /// Loads the image from the given byte array. + /// + /// The pixel format. + /// The byte array containing image data. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] data, IImageDecoder decoder) + where TColor : struct, IPixel + { + return Load(data, decoder, null); + } + /// /// Loads the image from the given byte array. /// @@ -144,5 +192,25 @@ namespace ImageSharp return Load(ms, options, config); } } + + /// + /// Loads the image from the given byte array. + /// + /// The pixel format. + /// The byte array containing image data. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) + where TColor : struct, IPixel + { + using (MemoryStream ms = new MemoryStream(data)) + { + return Load(ms, decoder, options); + } + } } } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 9b8ba83de..e9015eaa9 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -30,7 +30,7 @@ namespace ImageSharp /// The image public static Image Load(string path) { - return Load(path, null, null); + return Load(path, null, (Configuration)null); } /// @@ -61,6 +61,35 @@ namespace ImageSharp return Load(path, null, config); } + /// + /// Loads the image from the given file. + /// + /// The file path to the image. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string path, IImageDecoder decoder) + { + return Load(path, decoder, null); + } + + /// + /// Loads the image from the given file. + /// + /// The file path to the image. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) + { + return new Image(Load(path, decoder, options)); + } + /// /// Loads the image from the given file. /// @@ -88,7 +117,7 @@ namespace ImageSharp public static Image Load(string path) where TColor : struct, IPixel { - return Load(path, null, null); + return Load(path, null, (Configuration)null); } /// @@ -123,6 +152,22 @@ namespace ImageSharp return Load(path, null, config); } + /// + /// Loads the image from the given file. + /// + /// The pixel format. + /// The file path to the image. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string path, IImageDecoder decoder) + where TColor : struct, IPixel + { + return Load(path, decoder, null); + } + /// /// Loads the image from the given file. /// @@ -143,6 +188,27 @@ namespace ImageSharp return Load(s, options, config); } } + + /// + /// Loads the image from the given file. + /// + /// The pixel format. + /// The file path to the image. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) + where TColor : struct, IPixel + { + Configuration config = Configuration.Default; + using (Stream s = config.FileSystem.OpenRead(path)) + { + return Load(s, decoder, options); + } + } } #endif } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 36309db5b..b40aa62cd 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The image public static Image Load(Stream stream) { - return Load(stream, null, null); + return Load(stream, null, (Configuration)null); } /// @@ -60,6 +60,20 @@ namespace ImageSharp return Load(stream, null, config); } + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IImageDecoder decoder) + { + return Load(stream, decoder, null); + } + /// /// Loads the image from the given stream. /// @@ -75,6 +89,21 @@ namespace ImageSharp return new Image(Load(stream, options, config)); } + /// + /// Loads the image from the given stream. + /// + /// The stream containing image information. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) + { + return new Image(Load(stream, decoder, options)); + } + /// /// Loads the image from the given stream. /// @@ -87,7 +116,7 @@ namespace ImageSharp public static Image Load(Stream stream) where TColor : struct, IPixel { - return Load(stream, null, null); + return Load(stream, null, (Configuration)null); } /// @@ -122,6 +151,39 @@ namespace ImageSharp return Load(stream, null, config); } + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IImageDecoder decoder) + where TColor : struct, IPixel + { + return Load(stream, decoder, null); + } + + /// + /// Loads the image from the given stream. + /// + /// The pixel format. + /// The stream containing image information. + /// The decoder. + /// The options for the decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The image + public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) + where TColor : struct, IPixel + { + return WithSeekableStream(stream, s => decoder.Decode(s, options)); + } + /// /// Loads the image from the given stream. /// @@ -138,11 +200,29 @@ namespace ImageSharp { config = config ?? Configuration.Default; - if (!config.ImageFormats.Any()) + Image img = WithSeekableStream(stream, s => + { + return Decode(stream, options, config); + }); + + if (img != null) + { + return img; + } + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); + + foreach (IImageFormat format in config.ImageFormats) { - throw new InvalidOperationException("No image formats have been configured."); + stringBuilder.AppendLine("-" + format); } + throw new NotSupportedException(stringBuilder.ToString()); + } + + private static T WithSeekableStream(Stream stream, Func action) + { if (!stream.CanRead) { throw new NotSupportedException("Cannot read from the stream."); @@ -150,10 +230,7 @@ namespace ImageSharp if (stream.CanSeek) { - if (Decode(stream, options, config, out Image img)) - { - return img; - } + return action(stream); } else { @@ -163,22 +240,9 @@ namespace ImageSharp stream.CopyTo(ms); ms.Position = 0; - if (Decode(ms, options, config, out Image img)) - { - return img; - } + return action(stream); } } - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); - - foreach (IImageFormat format in config.ImageFormats) - { - stringBuilder.AppendLine("-" + format); - } - - throw new NotSupportedException(stringBuilder.ToString()); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index 2c97ea06f..da3d2fb06 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -66,6 +66,9 @@ namespace ImageSharp.Tests this.FilePath = Guid.NewGuid().ToString(); this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); + + TestFileSystem.RegisterGloablTestFormat(); + TestFileSystem.Global.AddFile(this.FilePath, this.DataStream); } [Fact] @@ -157,6 +160,50 @@ namespace ImageSharp.Tests this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); } + + + [Fact] + public void LoadFromStreamWithDecoder() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(stream, null)); + } + + [Fact] + public void LoadFromStreamWithTypeAndDecoder() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.localDecoder.Object); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(stream, null)); + } + + [Fact] + public void LoadFromStreamWithDecoderAndOptions() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + } + + [Fact] + public void LoadFromStreamWithTypeAndDecoderAndOptions() + { + Stream stream = new MemoryStream(); + Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + } + [Fact] public void LoadFromBytes() { @@ -246,7 +293,50 @@ namespace ImageSharp.Tests this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } - + + + [Fact] + public void LoadFromBytesWithDecoder() + { + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithTypeAndDecoder() + { + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithDecoderAndOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + + [Fact] + public void LoadFromBytesWithTypeAndDecoderAndOptions() + { + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); + } + [Fact] public void LoadFromFile() { @@ -324,7 +414,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithTypeAndConfigAndOptions() { - Image img = Image.Load(FilePath, this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.FilePath, this.decoderOptions, this.LocalConfiguration); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -332,6 +422,45 @@ namespace ImageSharp.Tests this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); } + + [Fact] + public void LoadFromFileWithDecoder() + { + Image img = Image.Load(this.FilePath, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + } + + [Fact] + public void LoadFromFileWithTypeAndDecoder() + { + Image img = Image.Load(this.FilePath, this.localDecoder.Object); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + } + + [Fact] + public void LoadFromFileWithDecoderAndOptions() + { + Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + } + + [Fact] + public void LoadFromFileWithTypeAndDecoderAndOptions() + { + Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); + + Assert.NotNull(img); + Assert.Equal(this.returnImage, img); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + } + public void Dispose() { // clean up the global object; diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs new file mode 100644 index 000000000..78499ac8e --- /dev/null +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using ImageSharp.Formats; + using Xunit; + + /// + /// A test image file. + /// + public class TestFileSystem : ImageSharp.IO.IFileSystem + { + + public static TestFileSystem Global { get; } = new TestFileSystem(); + + public static void RegisterGloablTestFormat() + { + Configuration.Default.FileSystem = Global; + } + + Dictionary fileSystem = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public void AddFile(string path, Stream data) + { + fileSystem.Add(path, data); + } + + public Stream Create(string path) + { + MemoryStream stream = new MemoryStream(); + lock (fileSystem) + { + if (fileSystem.ContainsKey(path)) + { + fileSystem[path] = stream; + } + else + { + fileSystem.Add(path, stream); + } + } + return stream; + } + + + public Stream OpenRead(string path) + { + lock (fileSystem) + { + if (fileSystem.ContainsKey(path)) + { + + Stream stream = fileSystem[path]; + stream.Position = 0; + return stream; + } + } + + return File.OpenRead(path); + } + } +} + From cfce9d2efc8bf3210dfc2b1f823b2c2c136ff51a Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 22 Mar 2017 07:09:43 +0000 Subject: [PATCH 04/18] remove decode metadata only it has no tests and nothing called it --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index fa656e71e..3f946fb79 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -193,19 +193,6 @@ namespace ImageSharp.Formats return image; } - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The stream, where the image should be. - /// The image metadata. - public ImageMetaData DecodeMetaData(Stream stream) - { - ImageMetaData metadata = new ImageMetaData(); - this.ProcessStream(metadata, stream, true); - return metadata; - } - /// /// Dispose /// From 53bba0269aa741148c33342816c276d1c696afcb Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 22 Mar 2017 09:56:22 +0000 Subject: [PATCH 05/18] pass configuration & ensure only single image created --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 5 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 14 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 10 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 11 +- src/ImageSharp/Formats/IImageDecoder.cs | 3 +- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 4 +- .../Formats/Jpeg/JpegDecoderCore.cs | 12 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 10 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 11 +- src/ImageSharp/Image.Create.cs | 46 +++++++ src/ImageSharp/Image.Decode.cs | 6 +- src/ImageSharp/Image.FromFile.cs | 6 +- src/ImageSharp/Image.FromStream.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 2 +- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 128 ++++++++++++------ tests/ImageSharp.Tests/TestFormat.cs | 23 +++- 16 files changed, 221 insertions(+), 72 deletions(-) create mode 100644 src/ImageSharp/Image.Create.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 2bc1c8cc3..8414807e0 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -26,12 +26,13 @@ namespace ImageSharp.Formats public class BmpDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + where TColor : struct, IPixel { Guard.NotNull(stream, "stream"); - return new BmpDecoderCore().Decode(stream); + return new BmpDecoderCore(configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index adfa4b6ac..18e4e858b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -43,6 +43,17 @@ namespace ImageSharp.Formats /// private BmpInfoHeader infoHeader; + private Configuration configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + public BmpDecoderCore(Configuration configuration) + { + this.configuration = configuration; + } + /// /// Decodes the image from the specified this._stream and sets /// the data to image. @@ -114,8 +125,7 @@ namespace ImageSharp.Formats + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - Image image = new Image(this.infoHeader.Width, this.infoHeader.Height); - + Image image = Image.Create(this.infoHeader.Width, this.infoHeader.Height, this.configuration); using (PixelAccessor pixels = image.Lock()) { switch (this.infoHeader.Compression) diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 16b036e68..2e56b0bab 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -14,12 +14,13 @@ namespace ImageSharp.Formats public class GifDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + where TColor : struct, IPixel { IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); - return this.Decode(stream, gifOptions); + return this.Decode(stream, gifOptions, configuration); } /// @@ -28,11 +29,12 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing image data. /// The options for the decoder. + /// The configuration. /// The image thats been decoded. - public Image Decode(Stream stream, IGifDecoderOptions options) + public Image Decode(Stream stream, IGifDecoderOptions options, Configuration configuration) where TColor : struct, IPixel { - return new GifDecoderCore(options).Decode(stream); + return new GifDecoderCore(options, configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 22a26345f..79348a7ab 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -27,6 +27,11 @@ namespace ImageSharp.Formats /// private readonly IGifDecoderOptions options; + /// + /// The global configuration. + /// + private readonly Configuration configuration; + /// /// The currently loaded stream. /// @@ -76,9 +81,11 @@ namespace ImageSharp.Formats /// Initializes a new instance of the class. /// /// The decoder options. - public GifDecoderCore(IGifDecoderOptions options) + /// The configuration. + public GifDecoderCore(IGifDecoderOptions options, Configuration configuration) { this.options = options ?? new GifDecoderOptions(); + this.configuration = configuration ?? Configuration.Default; } /// @@ -355,7 +362,7 @@ namespace ImageSharp.Formats this.metaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - this.image = new Image(imageWidth, imageHeight); + this.image = Image.Create(imageWidth, imageHeight, this.configuration); this.image.MetaData.LoadFrom(this.metaData); this.SetFrameDelay(this.metaData); diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index c4a9cf8c3..8da23d499 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -19,8 +19,9 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing image data. /// The options for the decoder. + /// The configuration for the image. /// The decoded image - Image Decode(Stream stream, IDecoderOptions options) + Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 3a91f8010..254506af0 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -14,12 +14,12 @@ namespace ImageSharp.Formats public class JpegDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) where TColor : struct, IPixel { Guard.NotNull(stream, "stream"); - using (JpegDecoderCore decoder = new JpegDecoderCore(options)) + using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration)) { return decoder.Decode(stream); } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 3f946fb79..fc9506b54 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -42,6 +42,11 @@ namespace ImageSharp.Formats /// private readonly IDecoderOptions options; + /// + /// The global configuration + /// + private readonly Configuration configuration; + /// /// The App14 marker color-space /// @@ -91,8 +96,10 @@ namespace ImageSharp.Formats /// Initializes a new instance of the class. /// /// The decoder options. - public JpegDecoderCore(IDecoderOptions options) + /// The configuration. + public JpegDecoderCore(IDecoderOptions options, Configuration configuration) { + this.configuration = configuration ?? Configuration.Default; this.options = options ?? new DecoderOptions(); this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.QuantizationTables = new Block8x8F[MaxTq + 1]; @@ -498,7 +505,8 @@ namespace ImageSharp.Formats private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) where TColor : struct, IPixel { - Image image = new Image(this.ImageWidth, this.ImageHeight); + Image image = Image.Create(this.ImageWidth, this.ImageHeight, this.configuration); + image.MetaData.LoadFrom(metadata); if (this.grayImage.IsInitialized) diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 5b7d97fc7..bf17ad142 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -31,12 +31,13 @@ namespace ImageSharp.Formats public class PngDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options) + public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + where TColor : struct, IPixel { IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); - return this.Decode(stream, pngOptions); + return this.Decode(stream, pngOptions, configuration); } /// @@ -45,11 +46,12 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing image data. /// The options for the decoder. + /// The configuration for the image. /// The decoded image. - public Image Decode(Stream stream, IPngDecoderOptions options) + public Image Decode(Stream stream, IPngDecoderOptions options, Configuration configuration) where TColor : struct, IPixel { - return new PngDecoderCore(options).Decode(stream); + return new PngDecoderCore(options, configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index dadf7ab7d..7705e9549 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -74,6 +74,11 @@ namespace ImageSharp.Formats /// private readonly Crc32 crc = new Crc32(); + /// + /// The global configuration. + /// + private readonly Configuration configuration; + /// /// The stream to decode from. /// @@ -134,8 +139,10 @@ namespace ImageSharp.Formats /// Initializes a new instance of the class. /// /// The decoder options. - public PngDecoderCore(IPngDecoderOptions options) + /// The configuration. + public PngDecoderCore(IPngDecoderOptions options, Configuration configuration) { + this.configuration = configuration ?? Configuration.Default; this.options = options ?? new PngDecoderOptions(); } @@ -213,7 +220,7 @@ namespace ImageSharp.Formats throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - Image image = new Image(this.header.Width, this.header.Height); + Image image = Image.Create(this.header.Width, this.header.Height, this.configuration); image.MetaData.LoadFrom(metadata); using (PixelAccessor pixels = image.Lock()) diff --git a/src/ImageSharp/Image.Create.cs b/src/ImageSharp/Image.Create.cs new file mode 100644 index 000000000..bc430724a --- /dev/null +++ b/src/ImageSharp/Image.Create.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Diagnostics; + using System.IO; + + using Formats; + + /// + /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha + /// packed into a single unsigned integer value. + /// + public sealed partial class Image + { + /// + /// Create a new instance of the class + /// with the height and the width of the image. + /// + /// The pixel format. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// + /// A new unless is in which case it returns + /// + internal static Image Create(int width, int height, Configuration configuration) + where TColor : struct, IPixel + { + if (typeof(TColor) == typeof(Color)) + { + return new Image(width, height, configuration) as Image; + } + else + { + return new Image(width, height, configuration); + } + } + } +} diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index c95b71b1e..32943ead5 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -19,7 +19,7 @@ namespace ImageSharp /// public sealed partial class Image { - private static IImageFormat DiscoverFormat(Stream stream, IDecoderOptions options, Configuration config) + private static IImageFormat DiscoverFormat(Stream stream, Configuration config) { int maxHeaderSize = config.MaxHeaderSize; if (maxHeaderSize <= 0) @@ -57,13 +57,13 @@ namespace ImageSharp private static Image Decode(Stream stream, IDecoderOptions options, Configuration config) where TColor : struct, IPixel { - IImageFormat format = DiscoverFormat(stream, options, config); + IImageFormat format = DiscoverFormat(stream, config); if (format == null) { return null; } - Image img = format.Decoder.Decode(stream, options); + Image img = format.Decoder.Decode(stream, options, config); img.CurrentImageFormat = format; return img; } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index e9015eaa9..b7fb0eef5 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -102,7 +102,11 @@ namespace ImageSharp /// The image public static Image Load(string path, IDecoderOptions options, Configuration config) { - return new Image(Load(path, options, config)); + config = config ?? Configuration.Default; + using (Stream s = config.FileSystem.OpenRead(path)) + { + return Load(s, options, config); + } } /// diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index b40aa62cd..e995c4799 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -181,7 +181,7 @@ namespace ImageSharp public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) where TColor : struct, IPixel { - return WithSeekableStream(stream, s => decoder.Decode(s, options)); + return WithSeekableStream(stream, s => decoder.Decode(s, options, Configuration.Default)); } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 416c88a50..cdd892dce 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -90,7 +90,7 @@ namespace ImageSharp.Tests image.Save(ms, new JpegEncoder()); ms.Seek(0, SeekOrigin.Begin); - using (JpegDecoderCore decoder = new JpegDecoderCore(null)) + using (JpegDecoderCore decoder = new JpegDecoderCore(null, null)) { Image mirror = decoder.Decode(ms); diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index da3d2fb06..52b8f6ee2 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Tests public ImageLoadTests() { - this.returnImage = new Image(1, 1); + this.returnImage = new Image(1, 1); this.localDecoder = new Mock(); this.localFormat = new Mock(); @@ -43,8 +43,10 @@ namespace ImageSharp.Tests this.localFormat.Setup(x => x.HeaderSize).Returns(1); this.localFormat.Setup(x => x.IsSupportedFileFormat(It.IsAny())).Returns(true); this.localFormat.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); - this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) - .Callback((s, o) => { + + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) + + .Callback((s, o, c) => { using (var ms = new MemoryStream()) { s.CopyTo(ms); @@ -79,7 +81,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -90,7 +94,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -100,7 +106,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -111,7 +119,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -122,7 +132,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, null)); + + this.localDecoder.Verify(x => x.Decode(stream, null, this.LocalConfiguration)); + } [Fact] @@ -134,7 +146,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, null)); + + this.localDecoder.Verify(x => x.Decode(stream, null, this.LocalConfiguration)); + } [Fact] @@ -145,7 +159,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, this.LocalConfiguration)); + } [Fact] @@ -157,7 +173,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, this.LocalConfiguration)); + } @@ -169,7 +187,7 @@ namespace ImageSharp.Tests Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(stream, null)); + this.localDecoder.Verify(x => x.Decode(stream, null, Configuration.Default)); } [Fact] @@ -180,7 +198,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(stream, null)); + this.localDecoder.Verify(x => x.Decode(stream, null, Configuration.Default)); } [Fact] @@ -190,7 +208,7 @@ namespace ImageSharp.Tests Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, Configuration.Default)); } [Fact] @@ -201,7 +219,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, Configuration.Default)); } [Fact] @@ -212,7 +230,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -223,7 +243,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -233,7 +255,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -244,7 +268,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -254,7 +280,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, this.LocalConfiguration)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -267,7 +295,9 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, this.LocalConfiguration)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -278,7 +308,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, this.LocalConfiguration)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -290,7 +322,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, this.LocalConfiguration)); + Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -301,7 +335,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, Configuration.Default)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -312,7 +346,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, Configuration.Default)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -322,7 +356,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, Configuration.Default)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -333,7 +367,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, Configuration.Default)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -345,7 +379,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -356,7 +392,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + } [Fact] @@ -366,7 +404,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -377,7 +417,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions); + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); + } [Fact] @@ -387,7 +429,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + + this.localDecoder.Verify(x => x.Decode(this.DataStream, null, this.LocalConfiguration)); + } [Fact] @@ -398,7 +442,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + + this.localDecoder.Verify(x => x.Decode(this.DataStream, null, this.LocalConfiguration)); + } [Fact] @@ -408,7 +454,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, this.LocalConfiguration)); + } [Fact] @@ -419,7 +467,9 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, this.LocalConfiguration)); + } @@ -429,7 +479,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null, Configuration.Default)); } [Fact] @@ -439,7 +489,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(this.DataStream, null, Configuration.Default)); } [Fact] @@ -448,7 +498,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, Configuration.Default)); } [Fact] @@ -458,7 +508,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, Configuration.Default)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 3a40ed420..5894151dc 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -55,9 +55,11 @@ namespace ImageSharp.Tests Dictionary _sampleImages = new Dictionary(); - public void VerifyDecodeCall(byte[] marker, IDecoderOptions options) + + public void VerifyDecodeCall(byte[] marker, IDecoderOptions options, Configuration config) { - DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, options)).ToArray(); + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, options, config)).ToArray(); + Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); @@ -107,10 +109,16 @@ namespace ImageSharp.Tests { public byte[] marker; public IDecoderOptions options; + internal Configuration config; - public bool IsMatch(byte[] testMarker, IDecoderOptions testOptions) + public bool IsMatch(byte[] testMarker, IDecoderOptions testOptions, Configuration config) { - if(this.options != testOptions) + if (this.options != testOptions) + { + return false; + } + + if (this.config != config) { return false; } @@ -140,7 +148,9 @@ namespace ImageSharp.Tests this.testFormat = testFormat; } - public Image Decode(Stream stream, IDecoderOptions options) where TColor : struct, IPixel + + public Image Decode(Stream stream, IDecoderOptions options, Configuration config) where TColor : struct, IPixel + { var ms = new MemoryStream(); stream.CopyTo(ms); @@ -148,7 +158,8 @@ namespace ImageSharp.Tests this.testFormat.DecodeCalls.Add(new DecodeOperation { marker = marker, - options = options + options = options, + config = config }); // TODO record this happend so we an verify it. From fb5899f0fdfed6a3ad8bb15dfa5ed165e2a41933 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 22 Mar 2017 10:14:13 +0000 Subject: [PATCH 06/18] fix test filesystem to prevent it breaking existing tests --- tests/ImageSharp.Tests/TestFileSystem.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index 78499ac8e..d43b989f1 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -36,29 +36,28 @@ namespace ImageSharp.Tests public Stream Create(string path) { - MemoryStream stream = new MemoryStream(); + // if we have injected a fake file use it instead lock (fileSystem) { if (fileSystem.ContainsKey(path)) { - fileSystem[path] = stream; - } - else - { - fileSystem.Add(path, stream); + Stream stream = fileSystem[path]; + stream.Position = 0; + return stream; } } - return stream; + + return File.Create(path); } public Stream OpenRead(string path) { + // if we have injected a fake file use it instead lock (fileSystem) { if (fileSystem.ContainsKey(path)) { - Stream stream = fileSystem[path]; stream.Position = 0; return stream; From 152e71135c117e2396c265a6113e2cbf0c74ac39 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 23 Mar 2017 10:08:30 +0000 Subject: [PATCH 07/18] add range checks --- .../Processors/FillRegionProcessor.cs | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 66e1f4380..3c07d41a8 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -65,6 +65,15 @@ namespace ImageSharp.Drawing.Processors int maxX = Math.Min(source.Width, rect.Right); int minY = Math.Max(0, rect.Top); int maxY = Math.Min(source.Height, rect.Bottom); + if (minX >= maxX) + { + return; // no effect inside image; + } + + if (minY >= maxY) + { + return; // no effect inside image; + } ArrayPool arrayPool = ArrayPool.Shared; @@ -122,22 +131,32 @@ namespace ImageSharp.Drawing.Processors int startX = (int)Math.Floor(scanStart); int endX = (int)Math.Floor(scanEnd); - for (float x = scanStart; x < startX + 1; x += subpixelFraction) + if (startX >= 0 && startX < scanline.Length) { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = scanStart; x < startX + 1; x += subpixelFraction) + { + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; + } } - for (float x = endX; x < scanEnd; x += subpixelFraction) + if (endX >= 0 && endX < scanline.Length) { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = endX; x < scanEnd; x += subpixelFraction) + { + scanline[endX] += subpixelFractionPoint; + scanlineDirty = true; + } } - for (int x = startX + 1; x < endX; x++) + int nextX = startX + 1; + if (nextX >= 0 && nextX < scanline.Length && endX >= 0 && endX < scanline.Length) { - scanline[x] += subpixelFraction; - scanlineDirty = true; + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } } } } From 3db50fea44216e62dd4fb7ac46d48ad5e1f3211b Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 23 Mar 2017 10:15:59 +0000 Subject: [PATCH 08/18] reduce range check --- src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 3c07d41a8..060ead6e4 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -150,7 +150,7 @@ namespace ImageSharp.Drawing.Processors } int nextX = startX + 1; - if (nextX >= 0 && nextX < scanline.Length && endX >= 0 && endX < scanline.Length) + if (nextX >= 0 && endX < scanline.Length) { for (int x = nextX; x < endX; x++) { From 69daa8464a30a62d5901d986a3ab620bbbbcff34 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 23 Mar 2017 10:18:25 +0000 Subject: [PATCH 09/18] scan to end of line even if longer --- src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 060ead6e4..9a616d408 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -150,7 +150,8 @@ namespace ImageSharp.Drawing.Processors } int nextX = startX + 1; - if (nextX >= 0 && endX < scanline.Length) + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + if (nextX >= 0) { for (int x = nextX; x < endX; x++) { From 422ff51502745fdd7311f94872f817fd199d28dd Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 23 Mar 2017 15:21:13 +0000 Subject: [PATCH 10/18] Default text drawing at an image dpi independent size This changes the default behavour to by defaulting all text to be drawn as 72 dpi as apposed to the images native resolution thus ensuing that the pixel hight per point is consisten across images by defualt. Can be switched back to image resolution byt setting `UseImageResolution` in `TextGraphicsOptions` to `true` --- src/ImageSharp.Drawing/Text/DrawText.cs | 9 ++++++- .../Text/TextGraphicsOptions.cs | 7 +++++ .../ImageSharp.Tests/Drawing/Text/DrawText.cs | 27 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs index 28781fab2..1f5f4cdb1 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.cs @@ -18,6 +18,8 @@ namespace ImageSharp /// public static partial class ImageExtensions { + private static readonly Vector2 DefaultTextDpi = new Vector2(72); + /// /// Draws the text onto the the image filled via the brush. /// @@ -169,7 +171,12 @@ namespace ImageSharp TextRenderer renderer = new TextRenderer(glyphBuilder); - Vector2 dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); + Vector2 dpi = DefaultTextDpi; + if (options.UseImageResolution) + { + dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); + } + FontSpan style = new FontSpan(font) { ApplyKerning = options.ApplyKerning, diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs index 253bb2aac..b58a40b34 100644 --- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs @@ -35,6 +35,12 @@ namespace ImageSharp.Drawing /// public float TabWidth; + /// + /// Flag weather to use the current image resultion to for point size scaling. + /// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution + /// + public bool UseImageResolution; + /// /// Initializes a new instance of the struct. /// @@ -45,6 +51,7 @@ namespace ImageSharp.Drawing this.ApplyKerning = true; this.TabWidth = 4; this.AntialiasSubpixelDepth = 16; + this.UseImageResolution = false; } /// diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 68db4d9a2..52b7fcbb6 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -219,5 +219,32 @@ namespace ImageSharp.Tests.Drawing.Text Assert.IsType>(this.img.ProcessorApplications[0].processor); Assert.IsType>(this.img.ProcessorApplications[1].processor); } + + [Fact] + public void GlyphHeightChangesBasedOnuseImageResolutionFlag() + { + this.img.MetaData.VerticalResolution = 1; + this.img.MetaData.HorizontalResolution = 1; + this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true) { + UseImageResolution = false + }); + + this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true) + { + UseImageResolution = true + }); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(2, this.img.ProcessorApplications.Count); + FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); + FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); + + ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); + ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); + + // magic numbers based on the font used at well known resolutions + Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); + Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + } } } From 9ec4ae84bb1f53682a9ff0d3034bf0d26887c3c9 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 25 Mar 2017 07:28:02 +0000 Subject: [PATCH 11/18] make constructor public --- src/ImageSharp/Image.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 352107021..195af6056 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -49,7 +49,7 @@ namespace ImageSharp /// /// The other image, where the clone should be made from. /// is null. - internal Image(Image other) + public Image(Image other) : base(other) { } From 2e4c1cd1b0e59516aea5d47dfe5aa102befbe7e8 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 25 Mar 2017 07:28:35 +0000 Subject: [PATCH 12/18] Clean up usings and add comments --- src/ImageSharp/Image.Decode.cs | 10 +++++++--- src/ImageSharp/Image.FromBytes.cs | 4 ---- src/ImageSharp/Image.FromFile.cs | 6 +----- src/ImageSharp/Image.FromStream.cs | 16 +++++++--------- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 32943ead5..a86a6e948 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -5,12 +5,9 @@ namespace ImageSharp { - using System; using System.Buffers; - using System.Diagnostics; using System.IO; using System.Linq; - using System.Text; using Formats; /// @@ -19,8 +16,15 @@ namespace ImageSharp /// public sealed partial class Image { + /// + /// By reading the header on the provided stream this calculates the images format. + /// + /// The image stream to read the header from. + /// The configuration. + /// The image format or null if none found. private static IImageFormat DiscoverFormat(Stream stream, Configuration config) { + // This is probably a candidate for making into a public API in the future! int maxHeaderSize = config.MaxHeaderSize; if (maxHeaderSize <= 0) { diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 8e0ac04a5..d23d0baee 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -6,11 +6,7 @@ namespace ImageSharp { using System; - using System.Buffers; - using System.Diagnostics; using System.IO; - using System.Linq; - using System.Text; using Formats; /// diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index b7fb0eef5..1710909e1 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -5,15 +5,11 @@ namespace ImageSharp { +#if !NETSTANDARD1_1 using System; - using System.Buffers; - using System.Diagnostics; using System.IO; - using System.Linq; - using System.Text; using Formats; -#if !NETSTANDARD1_1 /// /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha /// packed into a single unsigned integer value. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index e995c4799..16793e16c 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -6,10 +6,7 @@ namespace ImageSharp { using System; - using System.Buffers; - using System.Diagnostics; using System.IO; - using System.Linq; using System.Text; using Formats; @@ -86,7 +83,9 @@ namespace ImageSharp /// The image public static Image Load(Stream stream, IDecoderOptions options, Configuration config) { - return new Image(Load(stream, options, config)); + Image image = Load(stream, options, config); + + return image as Image ?? new Image(image); } /// @@ -101,7 +100,9 @@ namespace ImageSharp /// The image public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) { - return new Image(Load(stream, decoder, options)); + Image image = new Image(Load(stream, decoder, options)); + + return image as Image ?? new Image(image); } /// @@ -200,10 +201,7 @@ namespace ImageSharp { config = config ?? Configuration.Default; - Image img = WithSeekableStream(stream, s => - { - return Decode(stream, options, config); - }); + Image img = WithSeekableStream(stream, s => Decode(stream, options, config)); if (img != null) { From 0aa20b66e9ff854ac7e841a943dfa2b776b2ffbf Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 09:31:45 +0100 Subject: [PATCH 13/18] Move metadata loading into constructor/creator. --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 3 +- .../Formats/Jpeg/JpegDecoderCore.cs | 4 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +- src/ImageSharp/Image.Create.cs | 26 ++++++++-- src/ImageSharp/Image.cs | 15 ++++++ src/ImageSharp/Image/Image{TColor}.cs | 34 ++++++++++---- src/ImageSharp/MetaData/ImageMetaData.cs | 47 ++++++++----------- 7 files changed, 85 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 79348a7ab..4c119ca73 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -362,8 +362,7 @@ namespace ImageSharp.Formats this.metaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - this.image = Image.Create(imageWidth, imageHeight, this.configuration); - this.image.MetaData.LoadFrom(this.metaData); + this.image = Image.Create(imageWidth, imageHeight, this.metaData, this.configuration); this.SetFrameDelay(this.metaData); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index fc9506b54..33533aa12 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -505,9 +505,7 @@ namespace ImageSharp.Formats private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) where TColor : struct, IPixel { - Image image = Image.Create(this.ImageWidth, this.ImageHeight, this.configuration); - - image.MetaData.LoadFrom(metadata); + Image image = Image.Create(this.ImageWidth, this.ImageHeight, metadata, this.configuration); if (this.grayImage.IsInitialized) { diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7705e9549..d6529940e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -220,8 +220,7 @@ namespace ImageSharp.Formats throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - Image image = Image.Create(this.header.Width, this.header.Height, this.configuration); - image.MetaData.LoadFrom(metadata); + Image image = Image.Create(this.header.Width, this.header.Height, metadata, this.configuration); using (PixelAccessor pixels = image.Lock()) { diff --git a/src/ImageSharp/Image.Create.cs b/src/ImageSharp/Image.Create.cs index bc430724a..fcecefd7b 100644 --- a/src/ImageSharp/Image.Create.cs +++ b/src/ImageSharp/Image.Create.cs @@ -24,23 +24,43 @@ namespace ImageSharp /// The pixel format. /// The width of the image in pixels. /// The height of the image in pixels. + /// The images matadata to preload. /// /// The configuration providing initialization code which allows extending the library. /// /// /// A new unless is in which case it returns /// - internal static Image Create(int width, int height, Configuration configuration) + internal static Image Create(int width, int height, ImageMetaData metadata, Configuration configuration) where TColor : struct, IPixel { if (typeof(TColor) == typeof(Color)) { - return new Image(width, height, configuration) as Image; + return new Image(width, height, metadata, configuration) as Image; } else { - return new Image(width, height, configuration); + return new Image(width, height, metadata, configuration); } } + + /// + /// Create a new instance of the class + /// with the height and the width of the image. + /// + /// The pixel format. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// + /// A new unless is in which case it returns + /// + internal static Image Create(int width, int height, Configuration configuration) + where TColor : struct, IPixel + { + return Image.Create(width, height, null, configuration); + } } } diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 195af6056..00688afc9 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -53,5 +53,20 @@ namespace ImageSharp : base(other) { } + + /// + /// Initializes a new instance of the class + /// with the height and the width of the image. + /// + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The metadata. + /// + /// The configuration providing initialization code which allows extending the library. + /// + internal Image(int width, int height, ImageMetaData metadata, Configuration configuration) + : base(width, height, metadata, configuration) + { + } } } diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 82991948c..d063c3ff1 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -36,15 +36,8 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// public Image(int width, int height, Configuration configuration) - : base(width, height, configuration) + : this(width, height, new ImageMetaData(), configuration) { - if (!this.Configuration.ImageFormats.Any()) - { - throw new InvalidOperationException("No image formats have been configured."); - } - - this.MetaData = new ImageMetaData(); - this.CurrentImageFormat = this.Configuration.ImageFormats.First(); } /// @@ -87,12 +80,35 @@ namespace ImageSharp public Image(ImageBase other) : base(other) { + this.MetaData = new ImageMetaData(); + } + + /// + /// Initializes a new instance of the class + /// with the height and the width of the image. + /// + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The images metadata. + /// + /// The configuration providing initialization code which allows extending the library. + /// + internal Image(int width, int height, ImageMetaData metadata, Configuration configuration) + : base(width, height, configuration) + { + if (!this.Configuration.ImageFormats.Any()) + { + throw new InvalidOperationException("No image formats have been configured."); + } + + this.MetaData = metadata ?? new ImageMetaData(); + this.CurrentImageFormat = this.Configuration.ImageFormats.First(); } /// /// Gets the meta data of the image. /// - public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); + public ImageMetaData MetaData { get; private set; } /// /// Gets the width of the image in inches. It is calculated as the width of the image diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index d7d5e88b5..aed6efa2c 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -48,7 +48,25 @@ namespace ImageSharp { DebugGuard.NotNull(other, nameof(other)); - this.LoadFrom(other); + this.HorizontalResolution = other.HorizontalResolution; + this.VerticalResolution = other.VerticalResolution; + this.Quality = other.Quality; + this.FrameDelay = other.FrameDelay; + this.RepeatCount = other.RepeatCount; + + foreach (ImageProperty property in other.Properties) + { + this.Properties.Add(new ImageProperty(property)); + } + + if (other.ExifProfile != null) + { + this.ExifProfile = new ExifProfile(other.ExifProfile); + } + else + { + this.ExifProfile = null; + } } /// @@ -130,32 +148,5 @@ namespace ImageSharp { this.ExifProfile?.Sync(this); } - - /// - /// Sets the current metadata values based on a previous metadata object. - /// - /// Meta data object to copy values from. - internal void LoadFrom(ImageMetaData other) - { - this.HorizontalResolution = other.HorizontalResolution; - this.VerticalResolution = other.VerticalResolution; - this.Quality = other.Quality; - this.FrameDelay = other.FrameDelay; - this.RepeatCount = other.RepeatCount; - - foreach (ImageProperty property in other.Properties) - { - this.Properties.Add(new ImageProperty(property)); - } - - if (other.ExifProfile != null) - { - this.ExifProfile = new ExifProfile(other.ExifProfile); - } - else - { - this.ExifProfile = null; - } - } } } From c1a061194a72ae4dd76e4017ad9182aa6167875d Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 12:44:23 +0100 Subject: [PATCH 14/18] Move config argument to be first argument --- src/ImageSharp/Image.FromBytes.cs | 32 +++++++++---------- src/ImageSharp/Image.FromFile.cs | 32 +++++++++---------- src/ImageSharp/Image.FromStream.cs | 30 ++++++++--------- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 24 +++++++------- 4 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index d23d0baee..b2f9854f2 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -25,7 +25,7 @@ namespace ImageSharp /// The image public static Image Load(byte[] data) { - return Load(data, null, (Configuration)null); + return Load(null, data, null); } /// @@ -39,21 +39,21 @@ namespace ImageSharp /// The image public static Image Load(byte[] data, IDecoderOptions options) { - return Load(data, options, null); + return Load(null, data, options); } /// /// Loads the image from the given byte array. /// - /// The byte array containing image data. /// The config for the decoder. + /// The byte array containing image data. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] data, Configuration config) + public static Image Load(Configuration config, byte[] data) { - return Load(data, null, config); + return Load(config, data, null); } /// @@ -73,18 +73,18 @@ namespace ImageSharp /// /// Loads the image from the given byte array. /// + /// The configuration options. /// The byte array containing image data. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] data, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, byte[] data, IDecoderOptions options) { using (MemoryStream ms = new MemoryStream(data)) { - return Load(ms, options, config); + return Load(config, ms, options); } } @@ -118,7 +118,7 @@ namespace ImageSharp public static Image Load(byte[] data) where TColor : struct, IPixel { - return Load(data, null, (Configuration)null); + return Load(null, data, null); } /// @@ -134,23 +134,23 @@ namespace ImageSharp public static Image Load(byte[] data, IDecoderOptions options) where TColor : struct, IPixel { - return Load(data, options, null); + return Load(null, data, options); } /// /// Loads the image from the given byte array. /// /// The pixel format. - /// The byte array containing image data. /// The config for the decoder. + /// The byte array containing image data. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] data, Configuration config) + public static Image Load(Configuration config, byte[] data) where TColor : struct, IPixel { - return Load(data, null, config); + return Load(config, data, null); } /// @@ -173,19 +173,19 @@ namespace ImageSharp /// Loads the image from the given byte array. /// /// The pixel format. + /// The configuration options. /// The byte array containing image data. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(byte[] data, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, byte[] data, IDecoderOptions options) where TColor : struct, IPixel { using (MemoryStream ms = new MemoryStream(data)) { - return Load(ms, options, config); + return Load(config, ms, options); } } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 1710909e1..40cdfe3ef 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -26,7 +26,7 @@ namespace ImageSharp /// The image public static Image Load(string path) { - return Load(path, null, (Configuration)null); + return Load(null, path, null); } /// @@ -40,21 +40,21 @@ namespace ImageSharp /// The image public static Image Load(string path, IDecoderOptions options) { - return Load(path, options, null); + return Load(null, path, options); } /// /// Loads the image from the given file. /// - /// The file path to the image. /// The config for the decoder. + /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string path, Configuration config) + public static Image Load(Configuration config, string path) { - return Load(path, null, config); + return Load(config, path, null); } /// @@ -89,19 +89,19 @@ namespace ImageSharp /// /// Loads the image from the given file. /// + /// The configuration options. /// The file path to the image. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string path, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, string path, IDecoderOptions options) { config = config ?? Configuration.Default; using (Stream s = config.FileSystem.OpenRead(path)) { - return Load(s, options, config); + return Load(config, s, options); } } @@ -117,7 +117,7 @@ namespace ImageSharp public static Image Load(string path) where TColor : struct, IPixel { - return Load(path, null, (Configuration)null); + return Load(null, path, null); } /// @@ -133,23 +133,23 @@ namespace ImageSharp public static Image Load(string path, IDecoderOptions options) where TColor : struct, IPixel { - return Load(path, options, null); + return Load(null, path, options); } /// /// Loads the image from the given file. /// /// The pixel format. - /// The file path to the image. /// The config for the decoder. + /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string path, Configuration config) + public static Image Load(Configuration config, string path) where TColor : struct, IPixel { - return Load(path, null, config); + return Load(config, path, null); } /// @@ -172,20 +172,20 @@ namespace ImageSharp /// Loads the image from the given file. /// /// The pixel format. + /// The configuration options. /// The file path to the image. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(string path, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, string path, IDecoderOptions options) where TColor : struct, IPixel { config = config ?? Configuration.Default; using (Stream s = config.FileSystem.OpenRead(path)) { - return Load(s, options, config); + return Load(config, s, options); } } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 16793e16c..be807aef4 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -26,7 +26,7 @@ namespace ImageSharp /// The image public static Image Load(Stream stream) { - return Load(stream, null, (Configuration)null); + return Load(null, stream, null); } /// @@ -40,21 +40,21 @@ namespace ImageSharp /// The image public static Image Load(Stream stream, IDecoderOptions options) { - return Load(stream, options, null); + return Load(null, stream, options); } /// /// Loads the image from the given stream. /// - /// The stream containing image information. /// The config for the decoder. + /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(Stream stream, Configuration config) + public static Image Load(Configuration config, Stream stream) { - return Load(stream, null, config); + return Load(config, stream, null); } /// @@ -74,16 +74,16 @@ namespace ImageSharp /// /// Loads the image from the given stream. /// + /// The configuration options. /// The stream containing image information. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(Stream stream, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, Stream stream, IDecoderOptions options) { - Image image = Load(stream, options, config); + Image image = Load(config, stream, options); return image as Image ?? new Image(image); } @@ -117,7 +117,7 @@ namespace ImageSharp public static Image Load(Stream stream) where TColor : struct, IPixel { - return Load(stream, null, (Configuration)null); + return Load(null, stream, null); } /// @@ -133,23 +133,23 @@ namespace ImageSharp public static Image Load(Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - return Load(stream, options, null); + return Load(null, stream, options); } /// /// Loads the image from the given stream. /// /// The pixel format. - /// The stream containing image information. /// The config for the decoder. + /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(Stream stream, Configuration config) + public static Image Load(Configuration config, Stream stream) where TColor : struct, IPixel { - return Load(stream, null, config); + return Load(config, stream, null); } /// @@ -189,14 +189,14 @@ namespace ImageSharp /// Loads the image from the given stream. /// /// The pixel format. + /// The configuration options. /// The stream containing image information. /// The options for the decoder. - /// The configuration options. /// /// Thrown if the stream is not readable nor seekable. /// /// The image - public static Image Load(Stream stream, IDecoderOptions options, Configuration config) + public static Image Load(Configuration config, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { config = config ?? Configuration.Default; diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index 52b8f6ee2..91b907e19 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -128,7 +128,7 @@ namespace ImageSharp.Tests public void LoadFromStreamWithConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -141,7 +141,7 @@ namespace ImageSharp.Tests public void LoadFromStreamWithTypeAndConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -155,7 +155,7 @@ namespace ImageSharp.Tests public void LoadFromStreamWithConfigAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -168,7 +168,7 @@ namespace ImageSharp.Tests public void LoadFromStreamWithTypeAndConfigAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -276,7 +276,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithConfig() { - Image img = Image.Load(this.DataStream.ToArray(), this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -289,7 +289,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithTypeAndConfig() { - Image img = Image.Load(this.DataStream.ToArray(), this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -304,7 +304,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithConfigAndOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -317,7 +317,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithTypeAndConfigAndOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -425,7 +425,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithConfig() { - Image img = Image.Load(this.FilePath, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -437,7 +437,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithTypeAndConfig() { - Image img = Image.Load(this.FilePath, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); Assert.Equal(this.returnImage, img); @@ -450,7 +450,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithConfigAndOptions() { - Image img = Image.Load(this.FilePath, this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); @@ -462,7 +462,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithTypeAndConfigAndOptions() { - Image img = Image.Load(this.FilePath, this.decoderOptions, this.LocalConfiguration); + Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); From a8901f3c9cec097c850b7c8ff989ca07eb80318f Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 15:01:58 +0100 Subject: [PATCH 15/18] Update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 576ed76d0..86ce5ddd4 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Here's an example of the code required to resize an image using the default Bicu On platforms supporting netstandard 1.3+ ```csharp -using (Image image = new Image("foo.jpg")) +using (Image image = Image.Load("foo.jpg")) { image.Resize(image.Width / 2, image.Height / 2) .Grayscale() @@ -85,7 +85,7 @@ on netstandard 1.1 - 1.2 ```csharp using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) -using (Image image = new Image(stream)) +using (Image image = Image.Load(stream)) { image.Resize(image.Width / 2, image.Height / 2) .Grayscale() From 79ca3c430fde8387540888d0c4787848da748286 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 15:02:13 +0100 Subject: [PATCH 16/18] Increment version --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 2 +- src/ImageSharp/ImageSharp.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index e15bfe74b..351764bb9 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -2,7 +2,7 @@ An extension to ImageSharp that allows the drawing of images, paths, and text. ImageSharp.Drawing - 1.0.0-alpha4 + 1.0.0-alpha5 James Jackson-South and contributors netstandard1.1 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 63cb081c3..c51c0833a 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -2,7 +2,7 @@ A cross-platform library for the processing of image files; written in C# ImageSharp - 1.0.0-alpha4 + 1.0.0-alpha5 James Jackson-South and contributors netstandard1.3;netstandard1.1 true From da1dffd5cccfb634cdfec66518a07d1a48751d4e Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 15:12:14 +0100 Subject: [PATCH 17/18] decoder configuration argument order --- ImageSharp.sln | 3 ++ src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 8 +-- src/ImageSharp/Formats/IImageDecoder.cs | 4 +- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 2 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 8 +-- src/ImageSharp/Image.Decode.cs | 2 +- src/ImageSharp/Image.FromStream.cs | 2 +- src/README.md | 7 +++ .../ImageSharp.Tests/Image/ImageLoadTests.cs | 50 +++++++++---------- tests/ImageSharp.Tests/TestFormat.cs | 2 +- 11 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 src/README.md diff --git a/ImageSharp.sln b/ImageSharp.sln index 628fa7015..6e389421e 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -22,6 +22,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" + ProjectSection(SolutionItems) = preProject + src\README.md = src\README.md + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" EndProject diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 8414807e0..da5d24637 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -26,7 +26,7 @@ namespace ImageSharp.Formats public class BmpDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 2e56b0bab..2eb89de8f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -14,24 +14,24 @@ namespace ImageSharp.Formats public class GifDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); - return this.Decode(stream, gifOptions, configuration); + return this.Decode(configuration, stream, gifOptions); } /// /// Decodes the image from the specified stream to the . /// /// The pixel format. + /// The configuration. /// The containing image data. /// The options for the decoder. - /// The configuration. /// The image thats been decoded. - public Image Decode(Stream stream, IGifDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IGifDecoderOptions options) where TColor : struct, IPixel { return new GifDecoderCore(options, configuration).Decode(stream); diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 8da23d499..c85fbef10 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -17,11 +17,11 @@ namespace ImageSharp.Formats /// Decodes the image from the specified stream to the . /// /// The pixel format. + /// The configuration for the image. /// The containing image data. /// The options for the decoder. - /// The configuration for the image. /// The decoded image - Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 254506af0..0aac31603 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Formats public class JpegDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index bf17ad142..d0a820c17 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -31,24 +31,24 @@ namespace ImageSharp.Formats public class PngDecoder : IImageDecoder { /// - public Image Decode(Stream stream, IDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); - return this.Decode(stream, pngOptions, configuration); + return this.Decode(configuration, stream, pngOptions); } /// /// Decodes the image from the specified stream to the . /// /// The pixel format. + /// The configuration for the image. /// The containing image data. /// The options for the decoder. - /// The configuration for the image. /// The decoded image. - public Image Decode(Stream stream, IPngDecoderOptions options, Configuration configuration) + public Image Decode(Configuration configuration, Stream stream, IPngDecoderOptions options) where TColor : struct, IPixel { return new PngDecoderCore(options, configuration).Decode(stream); diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index a86a6e948..c1c137122 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -67,7 +67,7 @@ namespace ImageSharp return null; } - Image img = format.Decoder.Decode(stream, options, config); + Image img = format.Decoder.Decode(config, stream, options); img.CurrentImageFormat = format; return img; } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index be807aef4..41ac7757e 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -182,7 +182,7 @@ namespace ImageSharp public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) where TColor : struct, IPixel { - return WithSeekableStream(stream, s => decoder.Decode(s, options, Configuration.Default)); + return WithSeekableStream(stream, s => decoder.Decode(Configuration.Default, s, options)); } /// diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..3764e03cb --- /dev/null +++ b/src/README.md @@ -0,0 +1,7 @@ +# ImageSharp core libraries + +## Coding conventions + +### Argument ordering + +- When passing a `Configuration` object it should be the first argument \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index 91b907e19..a7f57ca1c 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -44,7 +44,7 @@ namespace ImageSharp.Tests this.localFormat.Setup(x => x.IsSupportedFileFormat(It.IsAny())).Returns(true); this.localFormat.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); - this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, o, c) => { using (var ms = new MemoryStream()) @@ -133,7 +133,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); } @@ -147,7 +147,7 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); } @@ -160,7 +160,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); } @@ -174,7 +174,7 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); } @@ -187,7 +187,7 @@ namespace ImageSharp.Tests Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(stream, null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); } [Fact] @@ -198,7 +198,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(stream, null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); } [Fact] @@ -208,7 +208,7 @@ namespace ImageSharp.Tests Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); } [Fact] @@ -219,7 +219,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(stream, this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); } [Fact] @@ -281,7 +281,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -296,7 +296,7 @@ namespace ImageSharp.Tests Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -309,7 +309,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -323,7 +323,7 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -335,7 +335,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -346,7 +346,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -356,7 +356,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -367,7 +367,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(It.IsAny(), this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -430,7 +430,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); } @@ -443,7 +443,7 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); } @@ -455,7 +455,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); } @@ -468,7 +468,7 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, this.LocalConfiguration)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); } @@ -479,7 +479,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); } [Fact] @@ -489,7 +489,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, null, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); } [Fact] @@ -498,7 +498,7 @@ namespace ImageSharp.Tests Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); } [Fact] @@ -508,7 +508,7 @@ namespace ImageSharp.Tests Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(this.DataStream, this.decoderOptions, Configuration.Default)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 5894151dc..084ad5993 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -149,7 +149,7 @@ namespace ImageSharp.Tests } - public Image Decode(Stream stream, IDecoderOptions options, Configuration config) where TColor : struct, IPixel + public Image Decode(Configuration config, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { var ms = new MemoryStream(); From c5c33a730e5b70dc78184e0328812ac8cf5b1e9b Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 17:55:17 +0100 Subject: [PATCH 18/18] fix broken tests --- tests/ImageSharp.Tests/Image/ImageLoadTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index a7f57ca1c..ddb9414cc 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -46,7 +46,7 @@ namespace ImageSharp.Tests this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, o, c) => { + .Callback((c, s, o) => { using (var ms = new MemoryStream()) { s.CopyTo(ms);