From 177064f05a1eb8b9a6175722b933b297cd97b5df 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 9c729493b2..628fa70153 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 9f490a3a9b..2bc1c8cc30 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 a75031ea19..adfa4b6ace 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 b1e8ba928d..16b036e684 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 ab1edc2c76..22a26345fe 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 df98870ddd..c4a9cf8c3c 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 eeb371d1e7..3a91f8010e 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 f1b85fa0bf..fa656e71e8 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 d527e1654d..5b7d97fc70 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 fd03ed39b8..dadf7ab7d6 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 0000000000..f31f2c0a55 --- /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 0000000000..e68a57fe70 --- /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 0000000000..b0adb1f96f --- /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 0000000000..36309db5b5 --- /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 8bfd8ee1a3..352107021a 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 707fea235d..393d83077a 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 e894fba4a8..14bdffc672 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 878ba09b39..cfce7184b6 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 34724cc977..82991948cb 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 de1e424769..d7d5e88b5f 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 b363286b00..c4a94c5ff1 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 431bbeb079..acde8e0dbe 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 517915bacb..6786cfdc0f 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 cb70213dac..a9bb4c7b3a 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 cbbe9c9f23..6ce2303703 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 c79d615384..5c3c1e115b 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 79c8dbc23e..620a48a3b1 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 b0a3b44999..6ed5773388 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 0810f3fe17..fabeba1bde 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 ba383873c2..1318c1674a 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 f835f9666f..7649812ecc 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 0bb0e922c5..4c1feb6c2a 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 4fee634f5f..a084ca025c 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 28bec5124d..28661b9d63 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 ae795c2ecc..1ecd04690a 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 a140b7a3cf..897778bc3a 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 5723f9b234..416c88a500 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 c97eb14619..0833cb8680 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 50e678bf08..28a64a765c 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 0000000000..2c97ea06fd --- /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 b0a031a780..02b0e5ad9b 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 785d9dcfc2..1bc31286da 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 701025e871..eedc0d306a 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 0000000000..3a40ed4201 --- /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 87b7ace6ae..c2fe0dc5c2 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 a8d398c1e8..2361bc01ce 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 1f9f0c9f82fa68a1d00db4bf4ff56fc3215b56af 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 e68a57fe70..3f1a3031c0 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 b0adb1f96f..9b8ba83ded 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 78334dd86fb764309bce6bf9d8f8b36c25724b7c 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 f31f2c0a55..c95b71b1e8 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 3f1a3031c0..8e0ac04a5a 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 9b8ba83ded..e9015eaa9d 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 36309db5b5..b40aa62cd9 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 2c97ea06fd..da3d2fb069 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 0000000000..78499ac8ee --- /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 23f2e79ea340185279bd1a8df047c9617529a4cc 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 fa656e71e8..3f946fb790 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 cc32d2d3984b48d8f317badf33583db43aaed12e 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 2bc1c8cc30..8414807e0c 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 adfa4b6ace..18e4e858b8 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 16b036e684..2e56b0baba 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 22a26345fe..79348a7aba 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 c4a9cf8c3c..8da23d499f 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 3a91f8010e..254506af05 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 3f946fb790..fc9506b546 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 5b7d97fc70..bf17ad1428 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 dadf7ab7d6..7705e95492 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 0000000000..bc430724a6 --- /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 c95b71b1e8..32943ead58 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 e9015eaa9d..b7fb0eef59 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 b40aa62cd9..e995c47997 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 416c88a500..cdd892dcee 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 da3d2fb069..52b8f6ee28 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 3a40ed4201..5894151dca 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 acd51cc64fb287564711f11770871e6bf84f3f38 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 78499ac8ee..d43b989f10 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 9b33d8b7157c8cf8c11816111f1b9a9d0a21c90f 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 66e1f4380c..3c07d41a86 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 b028126fe333f195939b6a7d4b8dbe84ae45d5ad 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 3c07d41a86..060ead6e4d 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 122ebccdc2dd57c5966a10b06160a8a01c6494bc 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 060ead6e4d..9a616d408b 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 8847f12913466e10b2852a33c94810e256da4d86 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 28781fab22..1f5f4cdb12 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 253bb2aaca..b58a40b34d 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 68db4d9a27..52b7fcbb65 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 385926067d2b723ebb1a93f545d8e4c617a57dab 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 352107021a..195af60563 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 b1d76dddc8a90c57f9ba5f9c75fd1dc30fb2506e 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 32943ead58..a86a6e948d 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 8e0ac04a5a..d23d0baeed 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 b7fb0eef59..1710909e18 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 e995c47997..16793e16c7 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 69100f4f0c65eb8facd5f21d2f3616a61554dcdf 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 79348a7aba..4c119ca737 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 fc9506b546..33533aa12b 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 7705e95492..d6529940e2 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 bc430724a6..fcecefd7b7 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 195af60563..00688afc96 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 82991948cb..d063c3ff16 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 d7d5e88b5f..aed6efa2cb 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 726ffe5d2e88b1b36e0dba7ffd00c3a805f541e7 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 d23d0baeed..b2f9854f22 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 1710909e18..40cdfe3eff 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 16793e16c7..be807aef44 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 52b8f6ee28..91b907e192 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 4590402ce1185df5cc488e3a8b052309516c2226 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 576ed76d07..86ce5ddd4d 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 b3810f549d6edd4b20bd74196a1c35d8e9b5377e 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 e15bfe74ba..351764bb95 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 63cb081c33..c51c0833a0 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 15fc34126f56664b0bb29ae48a6bcad017e43a55 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 628fa70153..6e389421e7 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 8414807e0c..da5d246372 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 2e56b0baba..2eb89de8ff 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 8da23d499f..c85fbef101 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 254506af05..0aac316035 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 bf17ad1428..d0a820c17f 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 a86a6e948d..c1c1371220 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 be807aef44..41ac7757e7 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 0000000000..3764e03cb8 --- /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 91b907e192..a7f57ca1c3 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 5894151dca..084ad59938 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 f23eac7e1479ff859ff4a587689aff119ffa0bb1 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 a7f57ca1c3..ddb9414cc4 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);