From f056e51cf7829990265658423c67fc6d3f3f29ca Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 22 Jul 2020 01:25:30 +0200 Subject: [PATCH] add cancellable overloads --- src/ImageSharp/Image.Decode.cs | 9 +- src/ImageSharp/Image.FromFile.cs | 48 +++++-- src/ImageSharp/Image.FromStream.cs | 194 ++++++++++++++++++++++++++--- 3 files changed, 222 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 683590fd1a..bc44cd8cab 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; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; @@ -156,9 +157,10 @@ namespace SixLabors.ImageSharp /// /// The stream. /// the configuration. + /// The token to monitor for cancellation requests. /// The pixel format. /// A representing the asynchronous operation. - private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config) + private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { (IImageDecoder decoder, IImageFormat format) = await DiscoverDecoderAsync(stream, config).ConfigureAwait(false); @@ -183,7 +185,7 @@ namespace SixLabors.ImageSharp return (img, format); } - private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config) + private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(Stream stream, Configuration config, CancellationToken cancellationToken) { (IImageDecoder decoder, IImageFormat format) = await DiscoverDecoderAsync(stream, config).ConfigureAwait(false); if (decoder is null) @@ -221,11 +223,12 @@ namespace SixLabors.ImageSharp /// /// The stream. /// the configuration. + /// The token to monitor for cancellation requests. /// /// 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) + private static async Task<(IImageInfo ImageInfo, IImageFormat Format)> InternalIdentityAsync(Stream stream, Configuration config, CancellationToken cancellationToken) { (IImageDecoder decoder, IImageFormat format) = await DiscoverDecoderAsync(stream, config).ConfigureAwait(false); diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index a078f2db98..7dc06b3ca9 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -136,13 +137,26 @@ namespace SixLabors.ImageSharp /// Image format not recognised. /// Image contains invalid content. /// A representing the asynchronous operation. - public static async Task LoadAsync(Configuration configuration, string path) + public static Task LoadAsync(Configuration configuration, string path) + => LoadAsync(configuration, path, default(CancellationToken)); + + /// + /// Create a new instance of the class from the given file. + /// + /// The configuration for the decoder. + /// The file path to the image. + /// The token to monitor for cancellation requests. + /// The configuration is null. + /// The path is null. + /// Image format not recognised. + /// Image contains invalid content. + /// A representing the asynchronous operation. + public static async Task LoadAsync(Configuration configuration, string path, CancellationToken cancellationToken) { - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - (Image img, _) = await LoadWithFormatAsync(configuration, stream).ConfigureAwait(false); - return img; - } + using Stream stream = configuration.FileSystem.OpenRead(path); + (Image img, _) = await LoadWithFormatAsync(configuration, stream, cancellationToken) + .ConfigureAwait(false); + return img; } /// @@ -181,14 +195,28 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// A representing the asynchronous operation. public static Task LoadAsync(Configuration configuration, string path, IImageDecoder decoder) + => LoadAsync(configuration, path, decoder, default); + + /// + /// Create a new instance of the class from the given file. + /// + /// The Configuration. + /// The file path to the image. + /// The decoder. + /// The token to monitor for cancellation requests. + /// The configuration is null. + /// The path is null. + /// The decoder is null. + /// Image format not recognised. + /// Image contains invalid content. + /// A representing the asynchronous operation. + public static Task LoadAsync(Configuration configuration, string path, IImageDecoder decoder, CancellationToken cancellationToken) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(path, nameof(path)); - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return LoadAsync(configuration, stream, decoder); - } + using Stream stream = configuration.FileSystem.OpenRead(path); + return LoadAsync(configuration, stream, decoder, cancellationToken); } /// diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index beec0b1880..d005873cad 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; @@ -62,7 +63,8 @@ namespace SixLabors.ImageSharp => WithSeekableStreamAsync( configuration, stream, - s => InternalDetectFormatAsync(s, configuration)); + (s, _) => InternalDetectFormatAsync(s, configuration), + default); /// /// Reads the raw image information from the specified stream without fully decoding it. @@ -192,7 +194,29 @@ namespace SixLabors.ImageSharp => WithSeekableStreamAsync( configuration, stream, - s => InternalIdentityAsync(s, configuration ?? Configuration.Default)); + (s, ct) => InternalIdentityAsync(s, configuration ?? Configuration.Default, ct), + default); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The configuration. + /// The image stream to read the information from. + /// The token to monitor for cancellation requests. + /// 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, CancellationToken cancellationToken) + => WithSeekableStreamAsync( + configuration, + stream, + (s, ct) => InternalIdentityAsync(s, configuration ?? Configuration.Default, ct), + cancellationToken); /// /// Decode a new instance of the class from the given stream. @@ -310,12 +334,31 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// A representing the asynchronous operation. public static Task LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder) + => LoadAsync(configuration, stream, decoder, default); + + /// + /// 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 token to monitor for cancellation requests. + /// 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, CancellationToken cancellationToken) { Guard.NotNull(decoder, nameof(decoder)); return WithSeekableStreamAsync( configuration, stream, - s => decoder.DecodeAsync(configuration, s)); + (s, ct) => decoder.DecodeAsync(configuration, s), + cancellationToken); } /// @@ -348,6 +391,25 @@ namespace SixLabors.ImageSharp return fmt.Image; } + /// + /// Decode a new instance of the class from the given stream. + /// + /// The configuration for the decoder. + /// The stream containing image information. + /// The token to monitor for cancellation requests. + /// 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, CancellationToken cancellationToken) + { + (Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(configuration, stream, cancellationToken) + .ConfigureAwait(false); + return fmt.Image; + } + /// /// Create a new instance of the class from the given stream. /// @@ -432,11 +494,28 @@ namespace SixLabors.ImageSharp /// The pixel format. /// A representing the asynchronous operation. public static Task> LoadAsync(Stream stream, IImageDecoder decoder) + where TPixel : unmanaged, IPixel + => LoadAsync(stream, decoder, default); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The stream containing image information. + /// The decoder. + /// The token to monitor for cancellation requests. + /// 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, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel => WithSeekableStreamAsync( Configuration.Default, stream, - s => decoder.DecodeAsync(Configuration.Default, s)); + (s, ct) => decoder.DecodeAsync(Configuration.Default, s), + cancellationToken); /// /// Create a new instance of the class from the given stream. @@ -468,12 +547,38 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// The pixel format. /// A representing the asynchronous operation. - public static Task> LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder) + public static Task> LoadAsync( + Configuration configuration, + Stream stream, + IImageDecoder decoder) + where TPixel : unmanaged, IPixel + => LoadAsync(configuration, stream, decoder, default); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The Configuration. + /// The stream containing image information. + /// The decoder. + /// The token to monitor for cancellation requests. + /// The configuration is null. + /// 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( + Configuration configuration, + Stream stream, + IImageDecoder decoder, + CancellationToken cancellationToken) where TPixel : unmanaged, IPixel => WithSeekableStreamAsync( configuration, stream, - s => decoder.DecodeAsync(configuration, s)); + (s, ct) => decoder.DecodeAsync(configuration, s), + cancellationToken); /// /// Create a new instance of the class from the given stream. @@ -538,12 +643,30 @@ namespace SixLabors.ImageSharp /// Image format not recognised. /// Image contains invalid content. /// A representing the asynchronous operation. - public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Configuration configuration, Stream stream) + public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync( + Configuration configuration, + Stream stream) + => LoadWithFormatAsync(configuration, stream, default); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The configuration options. + /// The stream containing image information. + /// The token to monitor for cancellation requests. + /// 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<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) { (Image Image, IImageFormat Format) data = await WithSeekableStreamAsync( configuration, stream, - async s => await DecodeAsync(s, configuration).ConfigureAwait(false)) + async (s, ct) => await DecodeAsync(s, configuration, ct).ConfigureAwait(false), + cancellationToken) .ConfigureAwait(false); if (data.Image != null) @@ -574,14 +697,33 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// The pixel format. /// A representing the asynchronous operation. - public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Configuration configuration, Stream stream) + public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync( + Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + => LoadWithFormatAsync(configuration, stream, default); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The configuration options. + /// The stream containing image information. + /// The token to monitor for cancellation requests. + /// The configuration is null. + /// 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(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { (Image Image, IImageFormat Format) data = await WithSeekableStreamAsync( configuration, stream, - s => DecodeAsync(s, configuration)) + (s, ct) => DecodeAsync(s, configuration, ct), + cancellationToken) .ConfigureAwait(false); if (data.Image != null) @@ -612,10 +754,28 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// The pixel format. /// A representing the asynchronous operation. - public static async Task> LoadAsync(Configuration configuration, Stream stream) + public static Task> LoadAsync(Configuration configuration, Stream stream) + where TPixel : unmanaged, IPixel + => LoadAsync(configuration, stream, default(CancellationToken)); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The configuration options. + /// The stream containing image information. + /// The token to monitor for cancellation requests. + /// The configuration is null. + /// 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> LoadAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - (Image img, _) = await LoadWithFormatAsync(configuration, stream).ConfigureAwait(false); + (Image img, _) = await LoadWithFormatAsync(configuration, stream, cancellationToken) + .ConfigureAwait(false); return img; } @@ -700,11 +860,13 @@ namespace SixLabors.ImageSharp /// The configuration. /// The input stream. /// The action to perform. + /// The cancellation token. /// The . private static async Task WithSeekableStreamAsync( Configuration configuration, Stream stream, - Func> action) + Func> action, + CancellationToken cancellationToken) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(stream, nameof(stream)); @@ -725,14 +887,14 @@ namespace SixLabors.ImageSharp stream.Position = 0; } - return await action(stream).ConfigureAwait(false); + return await action(stream, cancellationToken).ConfigureAwait(false); } using MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length); - await stream.CopyToAsync(memoryStream).ConfigureAwait(false); + await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; - return await action(memoryStream).ConfigureAwait(false); + return await action(memoryStream, cancellationToken).ConfigureAwait(false); } } }