diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index d0e4338b3f..5791c6e92b 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Linq;
-using System.Runtime.InteropServices;
-
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -15,15 +17,66 @@ namespace SixLabors.ImageSharp.Advanced
///
public static class AdvancedImageExtensions
{
+ ///
+ /// For a given file path find the best encoder to use via its extension.
+ ///
+ /// The source image.
+ /// The target file path to save the image to.
+ /// The matching encoder.
+ public static IImageEncoder DetectEncoder(this Image source, string filePath)
+ {
+ Guard.NotNull(filePath, nameof(filePath));
+
+ string ext = Path.GetExtension(filePath);
+ IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
+ if (format is null)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
+ foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
+ {
+ sb.AppendFormat(" - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
+ }
+
+ throw new NotSupportedException(sb.ToString());
+ }
+
+ IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
+
+ if (encoder is null)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
+ foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
+ {
+ sb.AppendFormat(" - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
+ }
+
+ throw new NotSupportedException(sb.ToString());
+ }
+
+ return encoder;
+ }
+
///
/// Accepts a to implement a double-dispatch pattern in order to
/// apply pixel-specific operations on non-generic instances
///
- /// The source.
- /// The visitor.
+ /// The source image.
+ /// The image visitor.
public static void AcceptVisitor(this Image source, IImageVisitor visitor)
=> source.Accept(visitor);
+ ///
+ /// Accepts a to implement a double-dispatch pattern in order to
+ /// apply pixel-specific operations on non-generic instances
+ ///
+ /// The source image.
+ /// The image visitor.
+ /// A representing the asynchronous operation.
+ public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor)
+ => source.AcceptAsync(visitor);
+
///
/// Gets the configuration for the image.
///
diff --git a/src/ImageSharp/Advanced/IImageVisitor.cs b/src/ImageSharp/Advanced/IImageVisitor.cs
index 2c54c8f1f8..fbfdafeb10 100644
--- a/src/ImageSharp/Advanced/IImageVisitor.cs
+++ b/src/ImageSharp/Advanced/IImageVisitor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Advanced
@@ -19,4 +20,20 @@ public interface IImageVisitor
void Visit(Image image)
where TPixel : unmanaged, IPixel;
}
+
+ ///
+ /// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations
+ /// on non-generic instances.
+ ///
+ public interface IImageVisitorAsync
+ {
+ ///
+ /// Provides a pixel-specific implementation for a given operation.
+ ///
+ /// The image.
+ /// The pixel type.
+ /// A representing the asynchronous operation.
+ Task VisitAsync(Image image)
+ where TPixel : unmanaged, IPixel;
+ }
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 7865c06f42..16da086c9e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -27,6 +28,26 @@ public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDe
///
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 @@ public Image Decode(Configuration configuration, Stream stream)
///
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 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
return new BmpDecoderCore(configuration, this).Identify(stream);
}
+
+ ///
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, nameof(stream));
+
+ return new BmpDecoderCore(configuration, this).IdentifyAsync(stream);
+ }
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index f97e810017..e37144bd58 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -7,7 +7,9 @@
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Common.Helpers;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -130,8 +132,40 @@ public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
/// is null.
///
/// The decoded image.
- public Image Decode(Stream stream)
+ public async Task> DecodeAsync(Stream stream)
where TPixel : unmanaged, IPixel
+ {
+ // if we can seek then we arn't in a context that errors on async operations
+ if (stream.CanSeek)
+ {
+ return this.Decode(stream);
+ }
+ else
+ {
+ // 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 +252,20 @@ public IImageInfo Identify(Stream stream)
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 FixedCapacityPooledMemoryStream(stream.Length))
+ {
+ 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 fa832b3a3a..08c9bde00d 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
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 @@ public void Encode(Image image, Stream stream)
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 f2c3791f39..b3f64eea6a 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -5,7 +5,7 @@
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,32 @@ public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocato
/// 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
+ {
+ if (stream.CanSeek)
+ {
+ this.Encode(image, stream);
+ }
+ else
+ {
+ 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 7e80859652..5f4fdd0fa6 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 @@ public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDe
///
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,19 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
return decoder.Identify(stream);
}
+ ///
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, nameof(stream));
+
+ var decoder = new GifDecoderCore(configuration, this);
+ return decoder.IdentifyAsync(stream);
+ }
+
///
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 649133085d..8f8426780d 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -6,7 +6,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
-
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -103,8 +104,32 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
/// 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
+ {
+ if (stream.CanSeek)
+ {
+ return this.Decode(stream);
+ }
+ else
+ {
+ 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 +188,27 @@ public Image Decode(Stream stream)
return image;
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public async Task IdentifyAsync(Stream stream)
+ {
+ if (stream.CanSeek)
+ {
+ return this.Identify(stream);
+ }
+ else
+ {
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
+ {
+ 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 5ded825e4d..539ab0fb38 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@@ -38,5 +39,13 @@ public void Encode(Image image, Stream stream)
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 fe77ba2896..556ace203a 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -6,6 +6,7 @@
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 @@ public GifEncoderCore(Configuration configuration, IGifEncoderOptions options)
/// 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 77d13842a8..97886e5269 100644
--- a/src/ImageSharp/Formats/IImageDecoder.cs
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
@@ -30,5 +31,25 @@ Image Decode(Configuration configuration, Stream stream)
/// 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 9e20944695..646e0ecc09 100644
--- a/src/ImageSharp/Formats/IImageEncoder.cs
+++ b/src/ImageSharp/Formats/IImageEncoder.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
@@ -19,5 +20,15 @@ public interface IImageEncoder
/// 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.
+ /// A representing the asynchronous operation.
+ 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 77abfe78f6..862c64999b 100644
--- a/src/ImageSharp/Formats/IImageInfoDetector.cs
+++ b/src/ImageSharp/Formats/IImageInfoDetector.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
namespace SixLabors.ImageSharp.Formats
{
@@ -17,5 +18,13 @@ public interface IImageInfoDetector
/// 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 a13d7562ed..2d2d7fb56e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -17,6 +18,28 @@ public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfo
///
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).ConfigureAwait(false);
+ }
+ 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 @@ public Image Decode(Configuration configuration, Stream stream)
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 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
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 15f9e36111..af1a705d44 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -6,6 +6,7 @@
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,32 @@ public static JpegFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStr
/// 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
+ {
+ if (stream.CanSeek)
+ {
+ return this.Decode(stream);
+ }
+ else
+ {
+ 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 +254,27 @@ public Image Decode(Stream stream)
return this.PostProcessIntoImage();
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public async Task IdentifyAsync(Stream stream)
+ {
+ if (stream.CanSeek)
+ {
+ return this.Identify(stream);
+ }
+ else
+ {
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
+ {
+ 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 2d6b0d49dd..08d793401e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
@@ -35,5 +36,33 @@ public void Encode(Image image, Stream stream)
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.
+ /// A representing the asynchronous operation.
+ public async Task EncodeAsync(Image image, Stream stream)
+ where TPixel : unmanaged, IPixel
+ {
+ var encoder = new JpegEncoderCore(this);
+
+ if (stream.CanSeek)
+ {
+ encoder.Encode(image, stream);
+ }
+ else
+ {
+ // 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 3acc8ecea2..f755028dbd 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -6,6 +6,7 @@
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 @@ public JpegEncoderCore(IJpegEncoderOptions options)
/// 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 9c728fc888..a6a040789a 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -2,31 +2,15 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png
{
///
- /// Encoder for generating an image out of a png encoded stream.
+ /// Decoder for generating an image out of a png encoded stream.
///
- ///
- /// At the moment the following features are supported:
- ///
- /// Filters: all filters are supported.
- ///
- ///
- /// Pixel formats:
- ///
- /// - RGBA (True color) with alpha (8 bit).
- /// - RGB (True color) without alpha (8 bit).
- /// - grayscale with alpha (8 bit).
- /// - grayscale without alpha (8 bit).
- /// - Palette Index with alpha (8 bit).
- /// - Palette Index without alpha (8 bit).
- ///
- ///
- ///
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{
///
@@ -34,6 +18,33 @@ public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDe
///
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 +79,17 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
return decoder.Identify(stream);
}
+ ///
+ public Task IdentifyAsync(Configuration configuration, Stream stream)
+ {
+ var decoder = new PngDecoderCore(configuration, this);
+ return decoder.IdentifyAsync(stream);
+ }
+
///
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 5919b7537b..bb8589de49 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -9,10 +9,11 @@
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;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -144,8 +145,38 @@ public PngDecoderCore(Configuration configuration, IPngDecoderOptions options)
/// 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
+ {
+ if (stream.CanSeek)
+ {
+ return this.Decode(stream);
+ }
+ else
+ {
+ 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();
@@ -235,6 +266,27 @@ public Image Decode(Stream stream)
}
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public async Task IdentifyAsync(Stream stream)
+ {
+ if (stream.CanSeek)
+ {
+ return this.Identify(stream);
+ }
+ else
+ {
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
+ {
+ 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 cd6287fb26..61ea7c4684 100644
--- a/src/ImageSharp/Formats/Png/PngEncoder.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -63,5 +64,21 @@ public void Encode(Image image, Stream stream)
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.
+ /// A representing the asynchronous operation.
+ 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).ConfigureAwait(false);
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 0ce516d2d1..a39fdd91f7 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -7,7 +7,8 @@
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;
using SixLabors.ImageSharp.Formats.Png.Zlib;
@@ -126,8 +127,32 @@ public PngEncoderCore(MemoryAllocator memoryAllocator, Configuration configurati
/// 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
+ {
+ if (stream.CanSeek)
+ {
+ this.Encode(image, stream);
+ }
+ else
+ {
+ 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 746e88f4d3..06b9ab6050 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
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 @@ public Image Decode(Configuration configuration, Stream stream)
///
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 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
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 e6648039e9..ae8946173b 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -5,7 +5,8 @@
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
-
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -88,8 +89,35 @@ public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options)
/// is null.
///
/// The decoded image.
- public Image Decode(Stream stream)
+ public async Task> DecodeAsync(Stream stream)
where TPixel : unmanaged, IPixel
+ {
+ if (stream.CanSeek)
+ {
+ return this.Decode(stream);
+ }
+ else
+ {
+ 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 +678,27 @@ private void ReadRle(int width, int height, Buffer2D pixels, int
}
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public async Task IdentifyAsync(Stream stream)
+ {
+ if (stream.CanSeek)
+ {
+ return this.Identify(stream);
+ }
+ else
+ {
+ using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
+ {
+ 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 2d944db6c1..c8f8fb1a51 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoder.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
-
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@@ -30,5 +30,13 @@ public void Encode(Image image, Stream stream)
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 317bec0809..d9e7c0e6bc 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
@@ -5,7 +5,7 @@
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,30 @@ public TgaEncoderCore(ITgaEncoderOptions options, MemoryAllocator memoryAllocato
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
+ {
+ if (stream.CanSeek)
+ {
+ this.Encode(image, stream);
+ }
+ else
+ {
+ 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/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs b/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
new file mode 100644
index 0000000000..878fcc53a7
--- /dev/null
+++ b/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Buffers;
+using System.IO;
+
+namespace SixLabors.ImageSharp.IO
+{
+ ///
+ /// A memory stream constructed from a pooled buffer of known length.
+ ///
+ internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
+ {
+ private readonly byte[] buffer;
+ private bool isDisposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The length of the stream buffer to rent.
+ public FixedCapacityPooledMemoryStream(long length)
+ : this(RentBuffer(length)) => this.Length = length;
+
+ private FixedCapacityPooledMemoryStream(byte[] buffer)
+ : base(buffer) => this.buffer = buffer;
+
+ ///
+ public override long Length { get; }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
+ {
+ this.isDisposed = true;
+
+ if (disposing)
+ {
+ ArrayPool.Shared.Return(this.buffer);
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+
+ // In the extrememly unlikely event someone ever gives us a stream
+ // with length longer than int.MaxValue then we'll use something else.
+ private static byte[] RentBuffer(long length) => ArrayPool.Shared.Rent((int)length);
+ }
+}
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index fca36b33c7..6b6dc6e21f 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@@ -70,6 +71,20 @@ private static IImageFormat InternalDetectFormat(Stream stream, Configuration co
}
}
+ ///
+ /// By reading the header on the provided stream this calculates the images format.
+ ///
+ /// The image stream to read the header from.
+ /// The configuration.
+ /// The mime type or null if none found.
+ private static Task InternalDetectFormatAsync(Stream stream, Configuration config)
+ {
+ // We are going to cheat here because we know that by this point we have been wrapped in a
+ // seekable stream then we are free to use sync APIs this is potentially brittle and may
+ // need a better fix in the future.
+ return Task.FromResult(InternalDetectFormat(stream, config));
+ }
+
///
/// By reading the header on the provided stream this calculates the images format.
///
@@ -86,7 +101,6 @@ private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config
: null;
}
-#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly
///
/// Decodes the image stream to the current image.
///
@@ -96,8 +110,7 @@ private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config
///
/// A new .
///
- private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config)
-#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly
+ private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config)
where TPixel : unmanaged, IPixel
{
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
@@ -110,7 +123,27 @@ private static (Image img, IImageFormat format) Decode(Stream st
return (img, format);
}
- private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config)
+ ///
+ /// Decodes the image stream to the current image.
+ ///
+ /// The stream.
+ /// the configuration.
+ /// The pixel format.
+ /// A representing the asynchronous operation.
+ private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config)
+ where TPixel : unmanaged, IPixel
+ {
+ IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
+ if (decoder is null)
+ {
+ return (null, null);
+ }
+
+ Image img = await decoder.DecodeAsync(config, stream).ConfigureAwait(false);
+ return (img, format);
+ }
+
+ private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config)
{
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
if (decoder is null)
@@ -122,22 +155,60 @@ private static (Image img, IImageFormat format) Decode(Stream stream, Configurat
return (img, format);
}
+ private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config)
+ {
+ IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
+ if (decoder is null)
+ {
+ return (null, null);
+ }
+
+ Image img = await decoder.DecodeAsync(config, stream).ConfigureAwait(false);
+ return (img, format);
+ }
+
///
/// Reads the raw image information from the specified stream.
///
/// The stream.
/// the configuration.
///
- /// The or null if suitable info detector not found.
+ /// The or null if a suitable info detector is not found.
///
- private static (IImageInfo info, IImageFormat format) InternalIdentity(Stream stream, Configuration config)
+ private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(Stream stream, Configuration config)
+ {
+ if (!(DiscoverDecoder(stream, config, out IImageFormat format) is IImageInfoDetector detector))
+ {
+ return (null, null);
+ }
+
+ IImageInfo info = detector?.Identify(config, stream);
+ return (info, format);
+ }
+
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The stream.
+ /// the configuration.
+ ///
+ /// A representing the asynchronous operation with the
+ /// property of the returned type set to null if a suitable detector
+ /// is not found.
+ private static async Task<(IImageInfo ImageInfo, IImageFormat Format)> InternalIdentityAsync(Stream stream, Configuration config)
{
if (!(DiscoverDecoder(stream, config, out IImageFormat format) is IImageInfoDetector detector))
{
return (null, null);
}
- return (detector?.Identify(config, stream), format);
+ if (detector is null)
+ {
+ return (null, format);
+ }
+
+ IImageInfo info = await detector.IdentifyAsync(config, stream).ConfigureAwait(false);
+ return (info, format);
}
}
}
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index 6e22ee8a3d..499f5ac195 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -5,7 +5,9 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -37,6 +39,31 @@ public static IImageFormat DetectFormat(Stream stream)
public static IImageFormat DetectFormat(Configuration configuration, Stream stream)
=> WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration));
+ ///
+ /// By reading the header on the provided stream this calculates the images format type.
+ ///
+ /// The image stream to read the header from.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// A representing the asynchronous operation or null if none is found.
+ public static Task DetectFormatAsync(Stream stream)
+ => DetectFormatAsync(Configuration.Default, stream);
+
+ ///
+ /// By reading the header on the provided stream this calculates the images format type.
+ ///
+ /// The configuration.
+ /// The image stream to read the header from.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// A representing the asynchronous operation.
+ public static Task DetectFormatAsync(Configuration configuration, Stream stream)
+ => WithSeekableStreamAsync(
+ configuration,
+ stream,
+ s => InternalDetectFormatAsync(s, configuration));
+
///
/// Reads the raw image information from the specified stream without fully decoding it.
///
@@ -45,11 +72,25 @@ public static IImageFormat DetectFormat(Configuration configuration, Stream stre
/// The stream is not readable.
/// Image contains invalid content.
///
- /// The or null if suitable info detector not found.
+ /// The or null if a suitable info detector is not found.
///
public static IImageInfo Identify(Stream stream)
=> Identify(stream, out IImageFormat _);
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The image stream to read the header from.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
+ ///
+ /// A representing the asynchronous operation or null if
+ /// a suitable detector is not found.
+ ///
+ public static Task IdentifyAsync(Stream stream)
+ => IdentifyAsync(Configuration.Default, stream);
+
///
/// Reads the raw image information from the specified stream without fully decoding it.
///
@@ -59,11 +100,45 @@ public static IImageInfo Identify(Stream stream)
/// The stream is not readable.
/// Image contains invalid content.
///
- /// The or null if suitable info detector not found.
+ /// The or null if a suitable info detector is not found.
///
public static IImageInfo Identify(Stream stream, out IImageFormat format)
=> Identify(Configuration.Default, stream, out format);
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The configuration.
+ /// The image stream to read the information from.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
+ ///
+ /// The or null if a suitable info detector is not found.
+ ///
+ public static IImageInfo Identify(Configuration configuration, Stream stream)
+ => Identify(configuration, stream, out _);
+
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The configuration.
+ /// The image stream to read the information from.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
+ ///
+ /// A representing the asynchronous operation or null if
+ /// a suitable detector is not found.
+ ///
+ public static async Task IdentifyAsync(Configuration configuration, Stream stream)
+ {
+ (IImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(configuration, stream).ConfigureAwait(false);
+ return res.ImageInfo;
+ }
+
///
/// Reads the raw image information from the specified stream without fully decoding it.
///
@@ -75,16 +150,50 @@ public static IImageInfo Identify(Stream stream, out IImageFormat format)
/// The stream is not readable.
/// Image contains invalid content.
///
- /// The or null if suitable info detector is not found.
+ /// The or null if a suitable info detector is not found.
///
public static IImageInfo Identify(Configuration configuration, Stream stream, out IImageFormat format)
{
- (IImageInfo info, IImageFormat format) data = WithSeekableStream(configuration, stream, s => InternalIdentity(s, configuration ?? Configuration.Default));
+ (IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(configuration, stream, s => InternalIdentity(s, configuration ?? Configuration.Default));
- format = data.format;
- return data.info;
+ format = data.Format;
+ return data.ImageInfo;
}
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The image stream to read the information from.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
+ ///
+ /// A representing the asynchronous operation or null if
+ /// a suitable detector is not found.
+ ///
+ public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(Stream stream)
+ => IdentifyWithFormatAsync(Configuration.Default, stream);
+
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The configuration.
+ /// The image stream to read the information from.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
+ ///
+ /// The representing the asyncronous operation with the parameter type
+ /// property set to null if suitable info detector is not found.
+ ///
+ public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(Configuration configuration, Stream stream)
+ => WithSeekableStreamAsync(
+ configuration,
+ stream,
+ s => InternalIdentityAsync(s, configuration ?? Configuration.Default));
+
///
/// Decode a new instance of the class from the given stream.
/// The pixel format is selected by the decoder.
@@ -99,6 +208,19 @@ public static IImageInfo Identify(Configuration configuration, Stream stream, ou
public static Image Load(Stream stream, out IImageFormat format)
=> Load(Configuration.Default, stream, out format);
+ ///
+ /// Decode a new instance of the class from the given stream.
+ /// The pixel format is selected by the decoder.
+ ///
+ /// The stream containing image information.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// A representing the asynchronous operation.
+ public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream)
+ => LoadWithFormatAsync(Configuration.Default, stream);
+
///
/// Decode a new instance of the class from the given stream.
/// The pixel format is selected by the decoder.
@@ -111,6 +233,18 @@ public static Image Load(Stream stream, out IImageFormat format)
/// The .
public static Image Load(Stream stream) => Load(Configuration.Default, stream);
+ ///
+ /// Decode a new instance of the class from the given stream.
+ /// The pixel format is selected by the decoder.
+ ///
+ /// The stream containing image information.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// A representing the asynchronous operation.
+ public static Task LoadAsync(Stream stream) => LoadAsync(Configuration.Default, stream);
+
///
/// Decode a new instance of the class from the given stream.
/// The pixel format is selected by the decoder.
@@ -126,6 +260,21 @@ public static Image Load(Stream stream, out IImageFormat format)
public static Image Load(Stream stream, IImageDecoder decoder)
=> Load(Configuration.Default, stream, decoder);
+ ///
+ /// Decode a new instance of the class from the given stream.
+ /// The pixel format is selected by the decoder.
+ ///
+ /// The stream containing image information.
+ /// The decoder.
+ /// The stream is null.
+ /// The decoder is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// A representing the asynchronous operation.
+ public static Task LoadAsync(Stream stream, IImageDecoder decoder)
+ => LoadAsync(Configuration.Default, stream, decoder);
+
///
/// Decode a new instance of the class from the given stream.
/// The pixel format is selected by the decoder.
@@ -139,13 +288,36 @@ public static Image Load(Stream stream, IImageDecoder decoder)
/// The stream is not readable.
/// Image format not recognised.
/// Image contains invalid content.
- /// A new .>
+ /// A new .
public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder)
{
Guard.NotNull(decoder, nameof(decoder));
return WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s));
}
+ ///
+ /// Decode a new instance of the class from the given stream.
+ /// The pixel format is selected by the decoder.
+ ///
+ /// The configuration for the decoder.
+ /// The stream containing image information.
+ /// The decoder.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The decoder is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// A representing the asynchronous operation.
+ public static Task LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder)
+ {
+ Guard.NotNull(decoder, nameof(decoder));
+ return WithSeekableStreamAsync(
+ configuration,
+ stream,
+ s => decoder.DecodeAsync(configuration, s));
+ }
+
///
/// Decode a new instance of the class from the given stream.
///
@@ -156,9 +328,26 @@ public static Image Load(Configuration configuration, Stream stream, IImageDecod
/// The stream is not readable.
/// Image format not recognised.
/// Image contains invalid content.
- /// A new .>
+ /// A new .
public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _);
+ ///
+ /// Decode a new instance of the class from the given stream.
+ ///
+ /// The configuration for the decoder.
+ /// The stream containing image information.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// A representing the asynchronous operation.
+ public static async Task LoadAsync(Configuration configuration, Stream stream)
+ {
+ (Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(configuration, stream).ConfigureAwait(false);
+ return fmt.Image;
+ }
+
///
/// Create a new instance of the class from the given stream.
///
@@ -168,11 +357,25 @@ public static Image Load(Configuration configuration, Stream stream, IImageDecod
/// Image format not recognised.
/// Image contains invalid content.
/// The pixel format.
- /// A new .>
+ /// A new .
public static Image Load(Stream stream)
where TPixel : unmanaged, IPixel
=> Load(Configuration.Default, stream);
+ ///
+ /// Create a new instance of the class from the given stream.
+ ///
+ /// The stream containing image information.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// The pixel format.
+ /// A representing the asynchronous operation.
+ public static Task> LoadAsync(Stream stream)
+ where TPixel : unmanaged, IPixel
+ => LoadAsync(Configuration.Default, stream);
+
///
/// Create a new instance of the class from the given stream.
///
@@ -183,11 +386,25 @@ public static Image Load(Stream stream)
/// Image format not recognised.
/// Image contains invalid content.
/// The pixel format.
- /// A new .>
+ /// A new .
public static Image Load(Stream stream, out IImageFormat format)
where TPixel : unmanaged, IPixel
=> Load(Configuration.Default, stream, out format);
+ ///
+ /// Create a new instance of the class from the given stream.
+ ///
+ /// The stream containing image information.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// The pixel format.
+ /// A representing the asynchronous operation.
+ public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream)
+ where TPixel : unmanaged, IPixel
+ => await LoadWithFormatAsync(Configuration.Default, stream).ConfigureAwait(false);
+
///
/// Create a new instance of the class from the given stream.
///
@@ -198,11 +415,29 @@ public static Image Load(Stream stream, out IImageFormat format)
/// Image format not recognised.
/// Image contains invalid content.
/// The pixel format.
- /// A new .>
+ /// A new .
public static Image Load(Stream stream, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
=> WithSeekableStream(Configuration.Default, stream, s => decoder.Decode(Configuration.Default, s));
+ ///
+ /// Create a new instance of the class from the given stream.
+ ///
+ /// The stream containing image information.
+ /// The decoder.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
+ /// The pixel format.
+ /// A representing the asynchronous operation.
+ public static Task> LoadAsync(Stream stream, IImageDecoder decoder)
+ where TPixel : unmanaged, IPixel
+ => WithSeekableStreamAsync(
+ Configuration.Default,
+ stream,
+ s => decoder.DecodeAsync