diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 6547fe76a7..2699331cc9 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -27,6 +28,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black; + /// + public async Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + var decoder = new BmpDecoderCore(configuration, this); + + try + { + return await decoder.DecodeAsync(stream).ConfigureAwait(false); + } + catch (InvalidMemoryOperationException ex) + { + Size dims = decoder.Dimensions; + + throw new InvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}. This error can happen for very large RLE bitmaps, which are not supported.", ex); + } + } + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel @@ -50,6 +71,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + /// + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { @@ -57,5 +81,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new BmpDecoderCore(configuration, this).Identify(stream); } + + /// + public async Task IdentifyAsync(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + return await new BmpDecoderCore(configuration, this).IdentifyAsync(stream).ConfigureAwait(false); + } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 0661f31456..b5ae055a99 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -7,6 +7,7 @@ using System.Buffers.Binary; using System.IO; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -130,8 +131,32 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// is null. /// /// The decoded image. - public Image Decode(Stream stream) + public async Task> DecodeAsync(Stream stream) where TPixel : unmanaged, IPixel + { + // cheat for now do async copy of the stream into memory stream and use the sync version + // we should use an array pool backed memorystream implementation + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Decode(ms); + } + } + + /// + /// Decodes the image from the specified this._stream and sets + /// the data to image. + /// + /// The pixel format. + /// The stream, where the image should be + /// decoded from. Cannot be null (Nothing in Visual Basic). + /// + /// is null. + /// + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : unmanaged, IPixel { try { @@ -218,6 +243,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public async Task IdentifyAsync(Stream stream) + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Identify(ms); + } + } + /// /// Returns the y- value based on the given height. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index fbc94a73f5..52b87ef27f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -39,5 +40,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp var encoder = new BmpEncoderCore(this, image.GetMemoryAllocator()); encoder.Encode(image, stream); } + + /// + public Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + var encoder = new BmpEncoderCore(this, image.GetMemoryAllocator()); + return encoder.EncodeAsync(image, stream); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index cc07c4d871..3e10eedbbd 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -5,7 +5,7 @@ using System; using System.Buffers; using System.IO; using System.Runtime.InteropServices; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; @@ -97,8 +97,25 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) + public async Task EncodeAsync(Image image, Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + this.Encode(image, ms); + ms.Position = 0; + await ms.CopyToAsync(stream).ConfigureAwait(false); + } + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index faa2498d14..e5de1c028d 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -24,6 +25,27 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; + /// + public async Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + var decoder = new GifDecoderCore(configuration, this); + + try + { + return await decoder.DecodeAsync(stream).ConfigureAwait(false); + } + catch (InvalidMemoryOperationException ex) + { + Size dims = decoder.Dimensions; + + GifThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex); + + // Not reachable, as the previous statement will throw a exception. + return null; + } + } + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel @@ -54,7 +76,20 @@ namespace SixLabors.ImageSharp.Formats.Gif return decoder.Identify(stream); } + + /// + public async Task IdentifyAsync(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + var decoder = new GifDecoderCore(configuration, this); + return await decoder.IdentifyAsync(stream).ConfigureAwait(false); + } + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + /// + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 81aa556956..fdac0e2ae3 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -6,7 +6,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -103,8 +103,25 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The pixel format. /// The stream containing image data. /// The decoded image - public Image Decode(Stream stream) + public async Task> DecodeAsync(Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Decode(ms); + } + } + + /// + /// Decodes the stream to the image. + /// + /// The pixel format. + /// The stream containing image data. + /// The decoded image + public Image Decode(Stream stream) + where TPixel : unmanaged, IPixel { Image image = null; ImageFrame previousFrame = null; @@ -163,6 +180,20 @@ namespace SixLabors.ImageSharp.Formats.Gif return image; } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public async Task IdentifyAsync(Stream stream) + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Identify(ms); + } + } + /// /// Reads the raw image information from the specified stream. /// diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index 762fc03fce..8d4c33eff9 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -38,5 +39,13 @@ namespace SixLabors.ImageSharp.Formats.Gif var encoder = new GifEncoderCore(image.GetConfiguration(), this); encoder.Encode(image, stream); } + + /// + public Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + var encoder = new GifEncoderCore(image.GetConfiguration(), this); + return encoder.EncodeAsync(image, stream); + } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 34c92a180c..46fd7e16a9 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -74,8 +75,25 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) + public async Task EncodeAsync(Image image, Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + this.Encode(image, ms); + ms.Position = 0; + await ms.CopyToAsync(stream).ConfigureAwait(false); + } + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 4f17f67292..cc5a87f076 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats @@ -30,5 +31,25 @@ namespace SixLabors.ImageSharp.Formats /// The . // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110) Image Decode(Configuration configuration, Stream stream); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The configuration for the image. + /// The containing image data. + /// The . + // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110) + Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an . + /// + /// The configuration for the image. + /// The containing image data. + /// The . + // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110) + Task DecodeAsync(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 01478eb3e3..f4d9b27937 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats @@ -19,5 +20,14 @@ namespace SixLabors.ImageSharp.Formats /// The to encode the image data to. void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel; + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel; } } diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 2bd33c6164..b231e6777f 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; namespace SixLabors.ImageSharp.Formats { @@ -17,5 +18,13 @@ namespace SixLabors.ImageSharp.Formats /// The containing image data. /// The object IImageInfo Identify(Configuration configuration, Stream stream); + + /// + /// Reads the raw image information from the specified stream. + /// + /// The configuration for the image. + /// The containing image data. + /// The object + Task IdentifyAsync(Configuration configuration, Stream stream); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 97f455c6f9..09bf770224 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -17,6 +18,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public bool IgnoreMetadata { get; set; } + /// + public async Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + using var decoder = new JpegDecoderCore(configuration, this); + try + { + return await decoder.DecodeAsync(stream); + } + catch (InvalidMemoryOperationException ex) + { + (int w, int h) = (decoder.ImageWidth, decoder.ImageHeight); + + JpegThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {w}x{h}.", ex); + + // Not reachable, as the previous statement will throw a exception. + return null; + } + } + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel @@ -43,6 +66,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + /// + public async Task DecodeAsync(Configuration configuration, Stream stream) + => await this.DecodeAsync(configuration, stream).ConfigureAwait(false); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { @@ -53,5 +80,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } + + /// + public async Task IdentifyAsync(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + using (var decoder = new JpegDecoderCore(configuration, this)) + { + return await decoder.IdentifyAsync(stream).ConfigureAwait(false); + } + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index a754bfd2e7..2f9495267f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -218,8 +219,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The pixel format. /// The stream, where the image should be. /// The decoded image. - public Image Decode(Stream stream) + public async Task> DecodeAsync(Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Decode(ms); + } + } + + /// + /// Decodes the image from the specified and sets the data to image. + /// + /// The pixel format. + /// The stream, where the image should be. + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : unmanaged, IPixel { this.ParseStream(stream); this.InitExifProfile(); @@ -229,6 +247,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return this.PostProcessIntoImage(); } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public async Task IdentifyAsync(Stream stream) + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Identify(ms); + } + } + /// /// Reads the raw image information from the specified stream. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 9f3d04a8a2..e87f9ce757 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -35,5 +36,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg var encoder = new JpegEncoderCore(this); encoder.Encode(image, stream); } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public async Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + var encoder = new JpegEncoderCore(this); + + // this hack has to be be here because JpegEncoderCore is unsafe + using (var ms = new MemoryStream()) + { + encoder.Encode(image, ms); + ms.Position = 0; + await ms.CopyToAsync(stream).ConfigureAwait(false); + } + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 21bf538ece..44f9ba1dfd 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -194,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The image to write from. /// The stream to write to. public void Encode(Image image, Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index b2e243997d..b33a0b1ddb 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -34,6 +35,33 @@ namespace SixLabors.ImageSharp.Formats.Png /// public bool IgnoreMetadata { get; set; } + /// + /// Decodes the image from the specified stream to the . + /// + /// The pixel format. + /// The configuration for the image. + /// The containing image data. + /// The decoded image. + public async Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + var decoder = new PngDecoderCore(configuration, this); + + try + { + return await decoder.DecodeAsync(stream).ConfigureAwait(false); + } + catch (InvalidMemoryOperationException ex) + { + Size dims = decoder.Dimensions; + + PngThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex); + + // Not reachable, as the previous statement will throw a exception. + return null; + } + } + /// /// Decodes the image from the specified stream to the . /// @@ -68,7 +96,17 @@ namespace SixLabors.ImageSharp.Formats.Png return decoder.Identify(stream); } + /// + public async Task IdentifyAsync(Configuration configuration, Stream stream) + { + var decoder = new PngDecoderCore(configuration, this); + return await decoder.IdentifyAsync(stream).ConfigureAwait(false); + } + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + /// + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index d6ba088958..f610f57503 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -9,7 +9,7 @@ using System.IO.Compression; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Zlib; @@ -149,8 +149,31 @@ namespace SixLabors.ImageSharp.Formats.Png /// Thrown if the image is larger than the maximum allowable size. /// /// The decoded image. - public Image Decode(Stream stream) + public async Task> DecodeAsync(Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Decode(ms); + } + } + + /// + /// Decodes the stream to the image. + /// + /// The pixel format. + /// The stream containing image data. + /// + /// Thrown if the stream does not contain and end chunk. + /// + /// + /// Thrown if the image is larger than the maximum allowable size. + /// + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : unmanaged, IPixel { var metadata = new ImageMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata(); @@ -240,6 +263,20 @@ namespace SixLabors.ImageSharp.Formats.Png } } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public async Task IdentifyAsync(Stream stream) + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Identify(ms); + } + } + /// /// Reads the raw image information from the specified stream. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index bee0215506..a2c2ca1009 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -56,5 +57,20 @@ namespace SixLabors.ImageSharp.Formats.Png encoder.Encode(image, stream); } } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public async Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + using (var encoder = new PngEncoderCore(image.GetMemoryAllocator(), image.GetConfiguration(), new PngEncoderOptions(this))) + { + await encoder.EncodeAsync(image, stream); + } + } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index c150388477..1c8696fc12 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -7,6 +7,7 @@ using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; @@ -131,8 +132,25 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) + public async Task EncodeAsync(Image image, Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + this.Encode(image, ms); + ms.Position = 0; + await ms.CopyToAsync(stream).ConfigureAwait(false); + } + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : unmanaged, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index abfaba629e..5cd7ca7b0e 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,6 +13,29 @@ namespace SixLabors.ImageSharp.Formats.Tga /// public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector { + /// + public async Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + var decoder = new TgaDecoderCore(configuration, this); + + try + { + return await decoder.DecodeAsync(stream).ConfigureAwait(false); + } + catch (InvalidMemoryOperationException ex) + { + Size dims = decoder.Dimensions; + + TgaThrowHelper.ThrowInvalidImageContentException($"Can not decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex); + + // Not reachable, as the previous statement will throw a exception. + return null; + } + } + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel @@ -38,6 +62,9 @@ namespace SixLabors.ImageSharp.Formats.Tga /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + /// + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream).ConfigureAwait(false); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { @@ -45,5 +72,13 @@ namespace SixLabors.ImageSharp.Formats.Tga return new TgaDecoderCore(configuration, this).Identify(stream); } + + /// + public Task IdentifyAsync(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + return new TgaDecoderCore(configuration, this).IdentifyAsync(stream); + } } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index ead0535721..808139e597 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -5,7 +5,7 @@ using System; using System.Buffers; using System.IO; using System.Runtime.CompilerServices; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -88,8 +88,28 @@ namespace SixLabors.ImageSharp.Formats.Tga /// is null. /// /// The decoded image. - public Image Decode(Stream stream) + public async Task> DecodeAsync(Stream stream) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Decode(ms); + } + } + + /// + /// Decodes the image from the specified stream. + /// + /// The pixel format. + /// The stream, where the image should be decoded from. Cannot be null. + /// + /// is null. + /// + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : unmanaged, IPixel { try { @@ -650,6 +670,20 @@ namespace SixLabors.ImageSharp.Formats.Tga } } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public async Task IdentifyAsync(Stream stream) + { + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return this.Identify(ms); + } + } + /// /// Reads the raw image information from the specified stream. /// diff --git a/src/ImageSharp/Formats/Tga/TgaEncoder.cs b/src/ImageSharp/Formats/Tga/TgaEncoder.cs index 6280b2ae63..058dd3559c 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoder.cs @@ -2,7 +2,7 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -30,5 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Tga var encoder = new TgaEncoderCore(this, image.GetMemoryAllocator()); encoder.Encode(image, stream); } + + /// + public Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + var encoder = new TgaEncoderCore(this, image.GetMemoryAllocator()); + return encoder.EncodeAsync(image, stream); + } } } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index aee3a26ccc..3b16048f36 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -5,7 +5,7 @@ using System; using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -55,6 +55,23 @@ namespace SixLabors.ImageSharp.Formats.Tga this.compression = options.Compression; } + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public async Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + this.Encode(image, ms); + ms.Position = 0; + await ms.CopyToAsync(stream).ConfigureAwait(false); + } + } + /// /// Encodes the image to the specified stream from the . /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index f3f4daa55e..c5a80cfb86 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests } } - public class TestDecoder : ImageSharp.Formats.IImageDecoder + public class TestDecoder : IImageDecoder { private TestFormat testFormat; @@ -219,9 +219,15 @@ namespace SixLabors.ImageSharp.Tests return this.testFormat.Sample(); } + public Task> DecodeAsync(Configuration config, Stream stream) + where TPixel : unmanaged, IPixel + => Task.FromResult(this.Decode(config, stream)); + public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream); } public class TestEncoder : ImageSharp.Formats.IImageEncoder @@ -242,6 +248,13 @@ namespace SixLabors.ImageSharp.Tests { // TODO record this happened so we can verify it. } + + public Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + // TODO record this happened so we can verify it. + return Task.CompletedTask; + } } public struct TestPixelForAgnosticDecode : IPixel diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index a146e260c6..f013ccc912 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -5,7 +5,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - +using System.Threading.Tasks; using ImageMagick; using SixLabors.ImageSharp.Advanced; @@ -49,6 +49,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } } + public Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + => Task.FromResult(this.Decode(configuration, stream)); + public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel { @@ -80,5 +84,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 05a1fbf849..06035b59f2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -2,7 +2,8 @@ // Licensed under the GNU Affero General Public License, Version 3. using System.IO; - +using System.Threading.Tasks; +using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -13,6 +14,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); + public Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + => Task.FromResult(this.Decode(configuration, stream)); + public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel { @@ -43,6 +48,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } } + public Task IdentifyAsync(Configuration configuration, Stream stream) + => Task.FromResult(this.Identify(configuration, stream)); + public IImageInfo Identify(Configuration configuration, Stream stream) { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) @@ -53,5 +61,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs index 34bec526be..0f53008141 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the GNU Affero General Public License, Version 3. using System.Drawing.Imaging; using System.IO; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -30,5 +30,16 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs sdBitmap.Save(stream, this.imageFormat); } } + + public Task EncodeAsync(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image)) + { + sdBitmap.Save(stream, this.imageFormat); + } + + return Task.CompletedTask; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 0390457a0e..ff3dc160b9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Concurrent; using System.IO; - +using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; @@ -349,6 +349,9 @@ namespace SixLabors.ImageSharp.Tests private static readonly ConcurrentDictionary InvocationCounts = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InvocationCountsAsync = + new ConcurrentDictionary(); + private static readonly object Monitor = new object(); private string callerName; @@ -368,15 +371,27 @@ namespace SixLabors.ImageSharp.Tests return new Image(42, 42); } + public Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + InvocationCountsAsync[this.callerName]++; + return Task.FromResult(new Image(42, 42)); + } + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; + internal static int GetInvocationCountAsync(string callerName) => InvocationCountsAsync[callerName]; + internal void InitCaller(string name) { this.callerName = name; InvocationCounts[name] = 0; + InvocationCountsAsync[name] = 0; } public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream); } private class TestDecoderWithParameters : IImageDecoder @@ -384,6 +399,9 @@ namespace SixLabors.ImageSharp.Tests private static readonly ConcurrentDictionary InvocationCounts = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InvocationCountsAsync = + new ConcurrentDictionary(); + private static readonly object Monitor = new object(); private string callerName; @@ -407,15 +425,27 @@ namespace SixLabors.ImageSharp.Tests return new Image(42, 42); } + public Task> DecodeAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + { + InvocationCountsAsync[this.callerName]++; + return Task.FromResult(new Image(42, 42)); + } + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; + internal static int GetInvocationCountAsync(string callerName) => InvocationCountsAsync[callerName]; + internal void InitCaller(string name) { this.callerName = name; InvocationCounts[name] = 0; + InvocationCountsAsync[name] = 0; } public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + public async Task DecodeAsync(Configuration configuration, Stream stream) => await this.DecodeAsync(configuration, stream); } } }