diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
index be0d057b2..00f7b1c3b 100644
--- a/src/ImageSharp/Advanced/AotCompilerTools.cs
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -248,7 +248,7 @@ internal static class AotCompilerTools
}
///
- /// This method pre-seeds the all in the AoT compiler.
+ /// This method pre-seeds the all in the AoT compiler.
///
/// The pixel format.
[Preserve]
@@ -280,15 +280,15 @@ internal static class AotCompilerTools
}
///
- /// This method pre-seeds the in the AoT compiler.
+ /// This method pre-seeds the in the AoT compiler.
///
/// The pixel format.
/// The decoder.
[Preserve]
private static void AotCompileImageDecoder()
where TPixel : unmanaged, IPixel
- where TDecoder : ImageDecoder
- => default(TDecoder).Decode(default, default, default);
+ where TDecoder : IImageDecoder
+ => default(TDecoder).Decode(default, default);
///
/// This method pre-seeds the all in the AoT compiler.
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index d56aaa1ac..8ef64b762 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
public sealed class BmpDecoder : SpecializedImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -20,7 +20,7 @@ public sealed class BmpDecoder : SpecializedImageDecoder
}
///
- protected internal override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -33,10 +33,10 @@ public sealed class BmpDecoder : SpecializedImageDecoder
}
///
- protected internal override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
///
- protected internal override BmpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
+ protected override BmpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index 6ae877516..fa35e339b 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
public sealed class GifDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -20,7 +20,7 @@ public sealed class GifDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -34,6 +34,6 @@ public sealed class GifDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs
new file mode 100644
index 000000000..86f4f3fa8
--- /dev/null
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Defines the contract for all image decoders.
+///
+public interface IImageDecoder
+{
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The general decoder options.
+ /// The containing image data.
+ /// The object.
+ /// Thrown if the encoded image contains errors.
+ public IImageInfo Identify(DecoderOptions options, Stream stream);
+
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The general decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// The object.
+ /// Thrown if the encoded image contains errors.
+ public Task IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The pixel format.
+ /// The general decoder options.
+ /// The containing image data.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ public Image Decode(DecoderOptions options, Stream stream)
+ where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The general decoder options.
+ /// The containing image data.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ public Image Decode(DecoderOptions options, Stream stream);
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The pixel format.
+ /// The general decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation.
+ /// Thrown if the encoded image contains errors.
+ public Task> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The general decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation.
+ /// Thrown if the encoded image contains errors.
+ public Task DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
+}
diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs
deleted file mode 100644
index ab5f536ff..000000000
--- a/src/ImageSharp/Formats/IImageInfoDetector.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats;
-
-///
-/// Encapsulates methods used for detecting the raw image information without fully decoding it.
-///
-public interface IImageInfoDetector
-{
- ///
- /// Reads the raw image information from the specified stream.
- ///
- ///
- /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
- ///
- /// The general decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// The object.
- /// Thrown if the encoded image contains errors.
- IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
-}
diff --git a/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs b/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs
new file mode 100644
index 000000000..2bda864bc
--- /dev/null
+++ b/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Defines the contract for an image decoder that supports specialized options.
+///
+/// The type of specialized options.
+public interface ISpecializedImageDecoder : IImageDecoder
+ where T : ISpecializedDecoderOptions
+{
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The pixel format.
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ public Image Decode(T options, Stream stream)
+ where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ public Image Decode(T options, Stream stream);
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The pixel format.
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation.
+ /// Thrown if the encoded image contains errors.
+ public Task> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation.
+ /// Thrown if the encoded image contains errors.
+ public Task DecodeAsync(T options, Stream stream, CancellationToken cancellationToken);
+}
diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs
index 000cb9923..42d476e92 100644
--- a/src/ImageSharp/Formats/ImageDecoder.cs
+++ b/src/ImageSharp/Formats/ImageDecoder.cs
@@ -1,15 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats;
///
-/// The base class for all image decoders.
+/// Acts as a base class for image decoders.
+/// Types that inherit this decoder are required to implement cancellable synchronous decoding operations only.
///
-public abstract class ImageDecoder
+public abstract class ImageDecoder : IImageDecoder
{
///
/// Decodes the image from the specified stream to an of a specific pixel type.
@@ -23,7 +25,7 @@ public abstract class ImageDecoder
/// The token to monitor for cancellation requests.
/// The .
/// Thrown if the encoded image contains errors.
- protected internal abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel;
///
@@ -37,7 +39,7 @@ public abstract class ImageDecoder
/// The token to monitor for cancellation requests.
/// The .
/// Thrown if the encoded image contains errors.
- protected internal abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
+ protected abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
///
/// Reads the raw image information from the specified stream.
@@ -50,7 +52,7 @@ public abstract class ImageDecoder
/// The token to monitor for cancellation requests.
/// The object.
/// Thrown if the encoded image contains errors.
- protected internal abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
+ protected abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
///
/// Performs a resize operation against the decoded image. If the target size is not set, or the image size
@@ -90,56 +92,132 @@ public abstract class ImageDecoder
Size currentSize = image.Size();
return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height;
}
-}
-///
-/// The base class for all specialized image decoders.
-/// Specialized decoders allow for additional options to be passed to the decoder.
-///
-/// The type of specialized options.
-public abstract class SpecializedImageDecoder : ImageDecoder
- where T : ISpecializedDecoderOptions
-{
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- ///
- /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
- ///
- /// The pixel format.
- /// The specialized decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// The .
- /// Thrown if the encoded image contains errors.
- protected internal abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel;
+ ///
+ public Image Decode(DecoderOptions options, Stream stream)
+ where TPixel : unmanaged, IPixel
+ => WithSeekableStream(
+ options,
+ stream,
+ s => this.Decode(options, s, default));
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- ///
- /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
- ///
- /// The specialized decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// The .
- /// Thrown if the encoded image contains errors.
- protected internal abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken);
+ ///
+ public Image Decode(DecoderOptions options, Stream stream)
+ => WithSeekableStream(
+ options,
+ stream,
+ s => this.Decode(options, s, default));
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
- => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
+ public Task> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel
+ => WithSeekableStreamAsync(
+ options,
+ stream,
+ (s, ct) => this.Decode(options, s, ct),
+ cancellationToken);
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
- => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
+ public Task DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ => WithSeekableStreamAsync(
+ options,
+ stream,
+ (s, ct) => this.Decode(options, s, ct),
+ cancellationToken);
- ///
- /// A factory method for creating the default specialized options.
- ///
- /// The general decoder options.
- /// The new .
- protected internal abstract T CreateDefaultSpecializedOptions(DecoderOptions options);
+ ///
+ public IImageInfo Identify(DecoderOptions options, Stream stream)
+ => WithSeekableStream(
+ options,
+ stream,
+ s => this.Identify(options, s, default));
+
+ ///
+ public Task IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ => WithSeekableStreamAsync(
+ options,
+ stream,
+ (s, ct) => this.Identify(options, s, ct),
+ cancellationToken);
+
+ internal static T WithSeekableStream(
+ DecoderOptions options,
+ Stream stream,
+ Func action)
+ {
+ Guard.NotNull(options, nameof(options));
+ Guard.NotNull(stream, nameof(stream));
+
+ if (!stream.CanRead)
+ {
+ throw new NotSupportedException("Cannot read from the stream.");
+ }
+
+ T Action(Stream s, long position)
+ {
+ T result = action(s);
+
+ // Our buffered reads may have left the stream in an incorrect non-zero position.
+ // Reset the position of the seekable stream if we did not read to the end to allow additional reads.
+ if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length)
+ {
+ stream.Position = position + s.Position;
+ }
+
+ return result;
+ }
+
+ if (stream.CanSeek)
+ {
+ return Action(stream, stream.Position);
+ }
+
+ Configuration configuration = options.Configuration;
+ using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
+ stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
+ memoryStream.Position = 0;
+
+ return Action(memoryStream, 0);
+ }
+
+ internal static async Task WithSeekableStreamAsync(
+ DecoderOptions options,
+ Stream stream,
+ Func action,
+ CancellationToken cancellationToken)
+ {
+ Guard.NotNull(options, nameof(options));
+ Guard.NotNull(stream, nameof(stream));
+
+ if (!stream.CanRead)
+ {
+ throw new NotSupportedException("Cannot read from the stream.");
+ }
+
+ T Action(Stream s, long position, CancellationToken ct)
+ {
+ T result = action(s, ct);
+
+ // Our buffered reads may have left the stream in an incorrect non-zero position.
+ // Reset the position of the seekable stream if we did not read to the end to allow additional reads.
+ if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length)
+ {
+ stream.Position = position + s.Position;
+ }
+
+ return result;
+ }
+
+ // NOTE: We are explicitly not executing the action against the stream here as we do in WithSeekableStream() because that
+ // would incur synchronous IO reads which must be avoided in this asynchronous method. Instead, we will *always* run the
+ // code below to copy the stream to an in-memory buffer before invoking the action.
+
+ // TODO: Avoid the existing double copy caused by calling IdentifyAsync followed by DecodeAsync.
+ // Perhaps we can make overloads accepting the chunked memorystream?
+ Configuration configuration = options.Configuration;
+ using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
+ await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
+ memoryStream.Position = 0;
+ return Action(memoryStream, 0, cancellationToken);
+ }
}
diff --git a/src/ImageSharp/Formats/ImageDecoderExtensions.cs b/src/ImageSharp/Formats/ImageDecoderExtensions.cs
deleted file mode 100644
index 26ceec3f6..000000000
--- a/src/ImageSharp/Formats/ImageDecoderExtensions.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Formats;
-
-///
-/// Extensions methods for and .
-///
-public static class ImageDecoderExtensions
-{
- ///
- /// Reads the raw image information from the specified stream.
- ///
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The object.
- /// Thrown if the encoded image contains errors.
- public static IImageInfo Identify(this ImageDecoder decoder, DecoderOptions options, Stream stream)
- => Image.WithSeekableStream(
- options,
- stream,
- s => decoder.Identify(options, s, default));
-
- ///
- /// Reads the raw image information from the specified stream.
- ///
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// The object.
- /// Thrown if the encoded image contains errors.
- public static Task IdentifyAsync(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
- => Image.WithSeekableStreamAsync(
- options,
- stream,
- (s, ct) => decoder.Identify(options, s, ct),
- cancellationToken);
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The pixel format.
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The .
- /// Thrown if the encoded image contains errors.
- public static Image Decode(this ImageDecoder decoder, DecoderOptions options, Stream stream)
- where TPixel : unmanaged, IPixel
- => Image.WithSeekableStream(
- options,
- stream,
- s => decoder.Decode(options, s, default));
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The .
- /// Thrown if the encoded image contains errors.
- public static Image Decode(this ImageDecoder decoder, DecoderOptions options, Stream stream)
- => Image.WithSeekableStream(
- options,
- stream,
- s => decoder.Decode(options, s, default));
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The pixel format.
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- /// Thrown if the encoded image contains errors.
- public static Task> DecodeAsync(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
- where TPixel : unmanaged, IPixel
- => Image.WithSeekableStreamAsync(
- options,
- stream,
- (s, ct) => decoder.Decode(options, s, ct),
- cancellationToken);
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The decoder.
- /// The general decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- /// Thrown if the encoded image contains errors.
- public static Task DecodeAsync(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
- => Image.WithSeekableStreamAsync(
- options,
- stream,
- (s, ct) => decoder.Decode(options, s, ct),
- cancellationToken);
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The type of specialized options.
- /// The pixel format.
- /// The decoder.
- /// The specialized decoder options.
- /// The containing image data.
- /// The .
- /// Thrown if the encoded image contains errors.
- public static Image Decode(this SpecializedImageDecoder decoder, T options, Stream stream)
- where T : ISpecializedDecoderOptions
- where TPixel : unmanaged, IPixel
- => Image.WithSeekableStream(
- options.GeneralOptions,
- stream,
- s => decoder.Decode(options, s, default));
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The type of specialized options.
- /// The decoder.
- /// The specialized decoder options.
- /// The containing image data.
- /// The .
- /// Thrown if the encoded image contains errors.
- public static Image Decode(this SpecializedImageDecoder decoder, T options, Stream stream)
- where T : ISpecializedDecoderOptions
- => Image.WithSeekableStream(
- options.GeneralOptions,
- stream,
- s => decoder.Decode(options, s, default));
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The type of specialized options.
- /// The pixel format.
- /// The decoder.
- /// The specialized decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- /// Thrown if the encoded image contains errors.
- public static Task> DecodeAsync(this SpecializedImageDecoder decoder, T options, Stream stream, CancellationToken cancellationToken = default)
- where T : ISpecializedDecoderOptions
- where TPixel : unmanaged, IPixel
- => Image.WithSeekableStreamAsync(
- options.GeneralOptions,
- stream,
- (s, ct) => decoder.Decode(options, s, ct),
- cancellationToken);
-
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The type of specialized options.
- /// The decoder.
- /// The specialized decoder options.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- /// Thrown if the encoded image contains errors.
- public static Task DecodeAsync(this SpecializedImageDecoder decoder, T options, Stream stream, CancellationToken cancellationToken = default)
- where T : ISpecializedDecoderOptions
- => Image.WithSeekableStreamAsync(
- options.GeneralOptions,
- stream,
- (s, ct) => decoder.Decode(options, s, ct),
- cancellationToken);
-}
diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs
index efa6c435a..23229703c 100644
--- a/src/ImageSharp/Formats/ImageFormatManager.cs
+++ b/src/ImageSharp/Formats/ImageFormatManager.cs
@@ -22,9 +22,9 @@ public class ImageFormatManager
private readonly ConcurrentDictionary mimeTypeEncoders = new();
///
- /// The list of supported keyed to mime types.
+ /// The list of supported keyed to mime types.
///
- private readonly ConcurrentDictionary mimeTypeDecoders = new();
+ private readonly ConcurrentDictionary mimeTypeDecoders = new();
///
/// The list of supported s.
@@ -59,9 +59,9 @@ public class ImageFormatManager
internal IEnumerable FormatDetectors => this.imageFormatDetectors;
///
- /// Gets the currently registered s.
+ /// Gets the currently registered s.
///
- internal IEnumerable> ImageDecoders => this.mimeTypeDecoders;
+ internal IEnumerable> ImageDecoders => this.mimeTypeDecoders;
///
/// Gets the currently registered s.
@@ -130,7 +130,7 @@ public class ImageFormatManager
///
/// The image format to register the encoder for.
/// The decoder to use,
- public void SetDecoder(IImageFormat imageFormat, ImageDecoder decoder)
+ public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
@@ -158,12 +158,12 @@ public class ImageFormatManager
/// For the specified mime type find the decoder.
///
/// The format to discover
- /// The if found otherwise null
- public ImageDecoder FindDecoder(IImageFormat format)
+ /// The if found otherwise null
+ public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
- return this.mimeTypeDecoders.TryGetValue(format, out ImageDecoder decoder)
+ return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)
? decoder
: null;
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
index 25ace3b8e..ac7187a3c 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
public sealed class JpegDecoder : SpecializedImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -21,7 +21,7 @@ public sealed class JpegDecoder : SpecializedImageDecoder
}
///
- protected internal override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -38,10 +38,10 @@ public sealed class JpegDecoder : SpecializedImageDecoder
}
///
- protected internal override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
///
- protected internal override JpegDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
+ protected override JpegDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
}
diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
index acfebdc84..e816dccb4 100644
--- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm;
public sealed class PbmDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -36,7 +36,7 @@ public sealed class PbmDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -50,6 +50,6 @@ public sealed class PbmDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs
index 617ca1c17..749b57720 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
public sealed class PngDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -20,7 +20,7 @@ public sealed class PngDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -34,7 +34,7 @@ public sealed class PngDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
diff --git a/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs b/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs
new file mode 100644
index 000000000..90442689b
--- /dev/null
+++ b/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Acts as a base class for specialized image decoders.
+/// Specialized decoders allow for additional options to be passed to the decoder.
+/// Types that inherit this decoder are required to implement cancellable synchronous decoding operations only.
+///
+/// The type of specialized options.
+public abstract class SpecializedImageDecoder : ImageDecoder, ISpecializedImageDecoder
+ where T : ISpecializedDecoderOptions
+{
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ ///
+ /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
+ ///
+ /// The pixel format.
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ protected abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Decodes the image from the specified stream to an of a specific pixel type.
+ ///
+ ///
+ /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
+ ///
+ /// The specialized decoder options.
+ /// The containing image data.
+ /// The token to monitor for cancellation requests.
+ /// The .
+ /// Thrown if the encoded image contains errors.
+ protected abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken);
+
+ ///
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
+
+ ///
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
+
+ ///
+ /// A factory method for creating the default specialized options.
+ ///
+ /// The general decoder options.
+ /// The new .
+ protected abstract T CreateDefaultSpecializedOptions(DecoderOptions options);
+
+ ///
+ public Image Decode(T options, Stream stream)
+ where TPixel : unmanaged, IPixel
+ => WithSeekableStream(
+ options.GeneralOptions,
+ stream,
+ s => this.Decode(options, s, default));
+
+ ///
+ public Image Decode(T options, Stream stream)
+ => WithSeekableStream(
+ options.GeneralOptions,
+ stream,
+ s => this.Decode(options, s, default));
+
+ ///
+ public Task> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel
+ => WithSeekableStreamAsync(
+ options.GeneralOptions,
+ stream,
+ (s, ct) => this.Decode(options, s, ct),
+ cancellationToken);
+
+ ///
+ public Task DecodeAsync(T options, Stream stream, CancellationToken cancellationToken)
+ => WithSeekableStreamAsync(
+ options.GeneralOptions,
+ stream,
+ (s, ct) => this.Decode(options, s, ct),
+ cancellationToken);
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
index b6ae9bd57..af58163c0 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tga;
public sealed class TgaDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -20,7 +20,7 @@ public sealed class TgaDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -34,6 +34,6 @@ public sealed class TgaDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
index 04b57e297..a5ce4f842 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
@@ -32,7 +32,8 @@ internal class WebpTiffCompression : TiffBaseDecompressor
///
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken)
{
- using Image image = new WebpDecoder().Decode(this.options, stream, cancellationToken);
+ using WebpDecoderCore decoder = new(this.options);
+ using Image image = decoder.Decode(stream, cancellationToken);
CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer);
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs
index a2d585a4c..f682daa66 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff;
public class TiffDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -20,7 +20,7 @@ public class TiffDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -34,6 +34,6 @@ public class TiffDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 76b486416..3a80d7533 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -113,7 +113,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
public FaxCompressionOptions FaxCompressionOptions { get; set; }
///
- /// Gets or sets the the logical order of bits within a byte.
+ /// Gets or sets the logical order of bits within a byte.
///
public TiffFillOrder FillOrder { get; set; }
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs
index aa62ee709..dc1a7f2ee 100644
--- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
public sealed class WebpDecoder : ImageDecoder
{
///
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -21,7 +21,7 @@ public sealed class WebpDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@@ -35,6 +35,6 @@ public sealed class WebpDecoder : ImageDecoder
}
///
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs
index ecb299ca3..55271afcb 100644
--- a/src/ImageSharp/IO/ChunkedMemoryStream.cs
+++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs
@@ -44,6 +44,7 @@ internal sealed class ChunkedMemoryStream : Stream
///
/// Initializes a new instance of the class.
///
+ /// The memory allocator.
public ChunkedMemoryStream(MemoryAllocator allocator)
{
Guard.NotNull(allocator, nameof(allocator));
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index 9e902c7bb..9616a8739 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -99,7 +99,7 @@ public abstract partial class Image
/// The image stream to read the header from.
/// The IImageFormat.
/// The image format or null if none found.
- private static ImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format)
+ private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format)
{
format = InternalDetectFormat(options.Configuration, stream);
@@ -113,36 +113,81 @@ public abstract partial class Image
///
/// The general decoder options.
/// The stream.
- /// The token to monitor for cancellation requests.
/// The pixel format.
///
/// A new .
///
- private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
+ private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel
{
- ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+ if (decoder is null)
+ {
+ return (null, null);
+ }
+
+ Image img = decoder.Decode(options, stream);
+ return (img, format);
+ }
+
+ private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(
+ DecoderOptions options,
+ Stream stream,
+ CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel
+ {
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+ if (decoder is null)
+ {
+ return (null, null);
+ }
+
+ Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
+ return (img, format);
+ }
+
+ private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream)
+ {
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
- Image img = decoder.Decode(options, stream, cancellationToken);
+ Image img = decoder.Decode(options, stream);
return (img, format);
}
- private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
+ private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(
+ DecoderOptions options,
+ Stream stream,
+ CancellationToken cancellationToken)
{
- ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
- Image img = decoder.Decode(options, stream, cancellationToken);
+ Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The general decoder options.
+ /// The stream.
+ ///
+ /// The or null if a suitable info detector is not found.
+ ///
+ private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentify(DecoderOptions options, Stream stream)
+ {
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+ IImageInfo info = decoder?.Identify(options, stream);
+ return (info, format);
+ }
+
///
/// Reads the raw image information from the specified stream.
///
@@ -152,10 +197,19 @@ public abstract partial class Image
///
/// The or null if a suitable info detector is not found.
///
- private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
+ private static async Task<(IImageInfo ImageInfo, IImageFormat Format)> InternalIdentifyAsync(
+ DecoderOptions options,
+ Stream stream,
+ CancellationToken cancellationToken)
{
- ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
- IImageInfo info = decoder?.Identify(options, stream, cancellationToken);
+ IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
+
+ if (decoder is null)
+ {
+ return (null, null);
+ }
+
+ IImageInfo info = await decoder.IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (info, format);
}
}
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index 8cf7e0fe2..19d85cafb 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -62,7 +62,7 @@ public abstract partial class Image
=> WithSeekableStreamAsync(
options,
stream,
- (s, _) => InternalDetectFormat(options.Configuration, s),
+ (s, _) => Task.FromResult(InternalDetectFormat(options.Configuration, s)),
cancellationToken);
///
@@ -160,7 +160,7 @@ public abstract partial class Image
///
public static IImageInfo Identify(DecoderOptions options, Stream stream, out IImageFormat format)
{
- (IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentity(options, s));
+ (IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentify(options, s));
format = data.Format;
return data.ImageInfo;
@@ -205,7 +205,7 @@ public abstract partial class Image
=> WithSeekableStreamAsync(
options,
stream,
- (s, ct) => InternalIdentity(options, s, ct),
+ (s, ct) => InternalIdentifyAsync(options, s, ct),
cancellationToken);
///
@@ -289,11 +289,7 @@ public abstract partial class Image
/// Image contains invalid content.
/// A representing the asynchronous operation.
public static async Task LoadAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
- {
- (Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(options, stream, cancellationToken)
- .ConfigureAwait(false);
- return fmt.Image;
- }
+ => (await LoadWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false)).Image;
///
/// Create a new instance of the class from the given stream.
@@ -416,7 +412,7 @@ public abstract partial class Image
CancellationToken cancellationToken = default)
{
(Image Image, IImageFormat Format) data =
- await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken)
+ await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
@@ -447,7 +443,7 @@ public abstract partial class Image
where TPixel : unmanaged, IPixel
{
(Image Image, IImageFormat Format) data =
- await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken)
+ await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
@@ -531,6 +527,20 @@ public abstract partial class Image
throw new NotSupportedException("Cannot read from the stream.");
}
+ T Action(Stream s, long position)
+ {
+ T result = action(s);
+
+ // Our buffered reads may have left the stream in an incorrect non-zero position.
+ // Reset the position of the seekable stream if we did not read to the end to allow additional reads.
+ if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length)
+ {
+ stream.Position = position + s.Position;
+ }
+
+ return result;
+ }
+
Configuration configuration = options.Configuration;
if (stream.CanSeek)
{
@@ -539,15 +549,14 @@ public abstract partial class Image
stream.Position = 0;
}
- return action(stream);
+ return Action(stream, stream.Position);
}
- // We want to be able to load images from things like HttpContext.Request.Body
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
memoryStream.Position = 0;
- return action(memoryStream);
+ return Action(memoryStream, 0);
}
///
@@ -563,7 +572,7 @@ public abstract partial class Image
internal static async Task WithSeekableStreamAsync(
DecoderOptions options,
Stream stream,
- Func action,
+ Func> action,
CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
@@ -574,34 +583,36 @@ public abstract partial class Image
throw new NotSupportedException("Cannot read from the stream.");
}
- Configuration configuration = options.Configuration;
- if (stream.CanSeek && configuration.ReadOrigin == ReadOrigin.Begin)
+ async Task Action(Stream s, long position, CancellationToken ct)
{
- stream.Position = 0;
+ T result = await action(s, ct).ConfigureAwait(false);
- // NOTE: We are explicitly not executing the action against the stream here as we do in WithSeekableStream() because that
- // would incur synchronous IO reads which must be avoided in this asynchronous method. Instead, we will *always* run the
- // code below to copy the stream to an in-memory buffer before invoking the action.
- }
+ // Our buffered reads may have left the stream in an incorrect non-zero position.
+ // Reset the position of the seekable stream if we did not read to the end to allow additional reads.
+ if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length)
+ {
+ stream.Position = position + s.Position;
+ }
- using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
- await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
- memoryStream.Position = 0;
+ return result;
+ }
- T Action(Stream ms, CancellationToken ct)
+ Configuration configuration = options.Configuration;
+ if (stream.CanSeek)
{
- // Reset the position of the seekable stream if we did not read to the end
- // to allow additional reads.
- T result = action(ms, ct);
- if (stream.CanSeek && ms.Position != ms.Length)
+ if (configuration.ReadOrigin == ReadOrigin.Begin)
{
- stream.Position = ms.Position;
+ stream.Position = 0;
}
- return result;
+ return await Action(stream, stream.Position, cancellationToken).ConfigureAwait(false);
}
- return Action(memoryStream, cancellationToken);
+ using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
+ await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
+ memoryStream.Position = 0;
+
+ return await Action(memoryStream, 0, cancellationToken).ConfigureAwait(false);
}
[DoesNotReturn]
@@ -610,7 +621,7 @@ public abstract partial class Image
StringBuilder sb = new();
sb.AppendLine("Image cannot be loaded. Available decoders:");
- foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders)
+ foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders)
{
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index f2a534b5d..7094bd047 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -41,6 +41,11 @@ public abstract partial class Image : IImage, IConfigurationProvider
///
/// Initializes a new instance of the class.
///
+ /// The configuration.
+ /// The .
+ /// The .
+ /// The width in px units.
+ /// The height in px units.
internal Image(
Configuration configuration,
PixelTypeInfo pixelType,
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
index fc8beb687..79ec5efab 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
@@ -26,6 +26,6 @@ public class IdentifyJpeg
{
using MemoryStream memoryStream = new(this.jpegBytes);
JpegDecoder decoder = new();
- return decoder.Identify(DecoderOptions.Default, memoryStream, default);
+ return decoder.Identify(DecoderOptions.Default, memoryStream);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
index 14eff7ba9..4ff85cb8f 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
@@ -278,9 +278,9 @@ public class BmpEncoderTests
// Use the default decoder to test our encoded image. This verifies the content.
// We do not verify the reference image though as some are invalid.
- ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
+ IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile);
- using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default);
+ using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
@@ -309,9 +309,9 @@ public class BmpEncoderTests
// Use the default decoder to test our encoded image. This verifies the content.
// We do not verify the reference image though as some are invalid.
- ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
+ IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile);
- using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default);
+ using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
@@ -378,7 +378,7 @@ public class BmpEncoderTests
bool supportTransparency = true, // if set to true, will write a V4 header, otherwise a V3 header.
IQuantizer quantizer = null,
ImageComparer customComparer = null,
- ImageDecoder referenceDecoder = null)
+ IImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage();
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
index 2d66842e8..4bd40dbcd 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
@@ -128,7 +128,7 @@ public class GifMetadataTests
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
- IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream);
+ IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream, default);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@@ -156,7 +156,7 @@ public class GifMetadataTests
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
- using Image image = await decoder.DecodeAsync(DecoderOptions.Default, stream);
+ using Image image = await decoder.DecodeAsync(DecoderOptions.Default, stream, default);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
index 277863a58..cb2611c40 100644
--- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
+++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
@@ -64,7 +64,7 @@ public class ImageFormatManagerTests
[Fact]
public void RegisterNullSetDecoder()
{
- Assert.Throws(() => this.DefaultFormatsManager.SetDecoder(null, new Mock().Object));
+ Assert.Throws(() => this.DefaultFormatsManager.SetDecoder(null, new Mock().Object));
Assert.Throws(() => this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null));
Assert.Throws(() => this.DefaultFormatsManager.SetDecoder(null, null));
}
@@ -87,14 +87,14 @@ public class ImageFormatManagerTests
[Fact]
public void RegisterMimeTypeDecoderReplacesLast()
{
- ImageDecoder decoder1 = new Mock().Object;
+ IImageDecoder decoder1 = new Mock().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
- ImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
+ IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
- ImageDecoder decoder2 = new Mock().Object;
+ IImageDecoder decoder2 = new Mock().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
- ImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
+ IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2);
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
index f88431be7..9f6b0d751 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
@@ -106,7 +106,7 @@ public partial class JpegDecoderTests
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder();
- IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream);
+ IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream, default);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@@ -142,7 +142,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
- using Image image = await JpegDecoder.DecodeAsync(DecoderOptions.Default, stream);
+ using Image image = await JpegDecoder.DecodeAsync(DecoderOptions.Default, stream, default);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
}
@@ -178,25 +178,25 @@ public partial class JpegDecoderTests
Assert.Equal(expectedColorType, meta.ColorType);
}
- private static void TestImageInfo(string imagePath, ImageDecoder decoder, bool useIdentify, Action test)
+ private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action test)
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
if (useIdentify)
{
- IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream, default);
+ IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream);
test(imageInfo);
}
else
{
- using Image img = decoder.Decode(DecoderOptions.Default, stream, default);
+ using Image img = decoder.Decode(DecoderOptions.Default, stream);
test(img);
}
}
private static void TestMetadataImpl(
bool useIdentify,
- ImageDecoder decoder,
+ IImageDecoder decoder,
string imagePath,
int expectedPixelSize,
bool exifProfilePresent,
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
index 0f33f2231..89e7e1159 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
@@ -588,7 +588,7 @@ public partial class PngEncoderTests
string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType);
// Compare to the Magick reference decoder.
- ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
+ IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
// We compare using both our decoder and the reference decoder as pixel transformation
// occurs within the encoder itself leaving the input image unaffected.
@@ -598,7 +598,7 @@ public partial class PngEncoderTests
fileStream.Position = 0;
- using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, fileStream, default);
+ using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, fileStream);
ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
index 456604d8b..95f37ba40 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
@@ -16,7 +16,7 @@ public abstract class TiffDecoderBaseTester
protected static MagickReferenceDecoder ReferenceDecoder => new();
- protected static void TestTiffDecoder(TestImageProvider provider, ImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
+ protected static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage(TiffDecoder);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs
index 0b4016dd5..6da0d4fd8 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs
@@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff;
[Trait("Format", "Tiff")]
public abstract class TiffEncoderBaseTester
{
- protected static readonly ImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
+ protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
protected static void TestStripLength(
TestImageProvider provider,
@@ -85,7 +85,7 @@ public abstract class TiffEncoderBaseTester
TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true,
float compareTolerance = 0.001f,
- ImageDecoder imageDecoder = null)
+ IImageDecoder imageDecoder = null)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage();
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
index 3f51f1967..7a3296120 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
@@ -19,7 +19,7 @@ public partial class ImageTests
protected Image localStreamReturnImageAgnostic;
- protected Mock localDecoder;
+ protected Mock localDecoder;
protected IImageFormatDetector localMimeTypeDetector;
@@ -59,13 +59,16 @@ public partial class ImageTests
this.localImageInfoMock = new Mock();
this.localImageFormatMock = new Mock();
- this.localDecoder = new Mock();
- this.localDecoder.Setup(x => x.Identify(It.IsAny(), It.IsAny(), It.IsAny()))
+ this.localDecoder = new Mock();
+ this.localDecoder.Setup(x => x.Identify(It.IsAny(), It.IsAny()))
.Returns(this.localImageInfoMock.Object);
+ this.localDecoder.Setup(x => x.IdentifyAsync(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(Task.FromResult(this.localImageInfoMock.Object));
+
this.localDecoder
- .Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny()))
- .Callback((c, s, ct) =>
+ .Setup(x => x.Decode(It.IsAny(), It.IsAny()))
+ .Callback((c, s) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
@@ -74,8 +77,8 @@ public partial class ImageTests
.Returns(this.localStreamReturnImageRgba32);
this.localDecoder
- .Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny()))
- .Callback((c, s, ct) =>
+ .Setup(x => x.Decode(It.IsAny(), It.IsAny()))
+ .Callback((c, s) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs
index fc95d5fa8..72298837c 100644
--- a/tests/ImageSharp.Tests/TestFile.cs
+++ b/tests/ImageSharp.Tests/TestFile.cs
@@ -147,7 +147,7 @@ public sealed class TestFile
///
/// The .
///
- public Image CreateRgba32Image(ImageDecoder decoder)
+ public Image CreateRgba32Image(IImageDecoder decoder)
=> this.CreateRgba32Image(decoder, new());
///
@@ -158,10 +158,10 @@ public sealed class TestFile
///
/// The .
///
- public Image CreateRgba32Image(ImageDecoder decoder, DecoderOptions options)
+ public Image CreateRgba32Image(IImageDecoder decoder, DecoderOptions options)
{
options.Configuration = this.Image.GetConfiguration();
using MemoryStream stream = new(this.Bytes);
- return decoder.Decode(options, stream, default);
+ return decoder.Decode(options, stream);
}
}
diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs
index e195bc660..eaf0a5df9 100644
--- a/tests/ImageSharp.Tests/TestFormat.cs
+++ b/tests/ImageSharp.Tests/TestFormat.cs
@@ -201,13 +201,13 @@ public class TestFormat : IConfigurationModule, IImageFormat
public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header);
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
- protected internal override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
+ protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
- protected internal override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Configuration configuration = options.GeneralOptions.Configuration;
var ms = new MemoryStream();
@@ -224,7 +224,7 @@ public class TestFormat : IConfigurationModule, IImageFormat
return this.testFormat.Sample();
}
- protected internal override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
index e9b7b8407..10126aab7 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
@@ -27,7 +27,7 @@ public abstract partial class TestImageProvider : IXunitSerializable
public Key(
PixelTypes pixelType,
string filePath,
- ImageDecoder customDecoder,
+ IImageDecoder customDecoder,
DecoderOptions options,
ISpecializedDecoderOptions specialized)
{
@@ -175,11 +175,11 @@ public abstract partial class TestImageProvider : IXunitSerializable
public override Image GetImage()
{
- ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath);
+ IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath);
return this.GetImage(decoder);
}
- public override Image GetImage(ImageDecoder decoder, DecoderOptions options)
+ public override Image GetImage(IImageDecoder decoder, DecoderOptions options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@@ -202,7 +202,7 @@ public abstract partial class TestImageProvider : IXunitSerializable
return cachedImage.Clone(this.Configuration);
}
- public override Task> GetImageAsync(ImageDecoder decoder, DecoderOptions options)
+ public override async Task> GetImageAsync(IImageDecoder decoder, DecoderOptions options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@@ -213,10 +213,10 @@ public abstract partial class TestImageProvider : IXunitSerializable
// TODO: Check Path here. Why combined?
string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath);
using Stream stream = System.IO.File.OpenRead(path);
- return Task.FromResult(decoder.Decode(options, stream, default));
+ return await decoder.DecodeAsync(options, stream, default);
}
- public override Image GetImage(SpecializedImageDecoder decoder, T options)
+ public override Image GetImage(ISpecializedImageDecoder decoder, T options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@@ -239,7 +239,7 @@ public abstract partial class TestImageProvider : IXunitSerializable
return cachedImage.Clone(this.Configuration);
}
- public override Task> GetImageAsync(SpecializedImageDecoder decoder, T options)
+ public override async Task> GetImageAsync(ISpecializedImageDecoder decoder, T options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@@ -250,7 +250,7 @@ public abstract partial class TestImageProvider : IXunitSerializable
// TODO: Check Path here. Why combined?
string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath);
using Stream stream = System.IO.File.OpenRead(path);
- return Task.FromResult(decoder.Decode(options, stream, default));
+ return await decoder.DecodeAsync(options, stream, default);
}
public override void Deserialize(IXunitSerializationInfo info)
@@ -266,23 +266,23 @@ public abstract partial class TestImageProvider : IXunitSerializable
info.AddValue("path", this.FilePath);
}
- private Image DecodeImage(ImageDecoder decoder, DecoderOptions options)
+ private Image DecodeImage(IImageDecoder decoder, DecoderOptions options)
{
options.Configuration = this.Configuration;
var testFile = TestFile.Create(this.FilePath);
using Stream stream = new MemoryStream(testFile.Bytes);
- return decoder.Decode(options, stream, default);
+ return decoder.Decode(options, stream);
}
- private Image DecodeImage(SpecializedImageDecoder decoder, T options)
+ private Image DecodeImage(ISpecializedImageDecoder decoder, T options)
where T : class, ISpecializedDecoderOptions, new()
{
options.GeneralOptions.Configuration = this.Configuration;
var testFile = TestFile.Create(this.FilePath);
using Stream stream = new MemoryStream(testFile.Bytes);
- return decoder.Decode(options, stream, default);
+ return decoder.Decode(options, stream);
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
index 48e63bdca..8f22fb2b2 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
@@ -87,23 +87,23 @@ public abstract partial class TestImageProvider : ITestImageProvider, IX
/// A test image.
public abstract Image GetImage();
- public Image GetImage(ImageDecoder decoder)
+ public Image GetImage(IImageDecoder decoder)
=> this.GetImage(decoder, new());
- public Task> GetImageAsync(ImageDecoder decoder)
+ public Task> GetImageAsync(IImageDecoder decoder)
=> this.GetImageAsync(decoder, new());
- public virtual Image GetImage(ImageDecoder decoder, DecoderOptions options)
+ public virtual Image GetImage(IImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!");
- public virtual Task> GetImageAsync(ImageDecoder decoder, DecoderOptions options)
+ public virtual Task> GetImageAsync(IImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
- public virtual Image GetImage(SpecializedImageDecoder decoder, T options)
+ public virtual Image GetImage(ISpecializedImageDecoder decoder, T options)
where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!");
- public virtual Task> GetImageAsync(SpecializedImageDecoder decoder, T options)
+ public virtual Task> GetImageAsync(ISpecializedImageDecoder decoder, T options)
where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs
index 1290fdea3..8d7b5042f 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs
@@ -45,6 +45,6 @@ public sealed class ImageSharpPngEncoderWithDefaultConfiguration : PngEncoder
// IDisposable means you must use async/await, where the compiler generates the
// state machine and a continuation.
using PngEncoderCore encoder = new(allocator, configuration, this);
- await encoder.EncodeAsync(image, stream, cancellationToken).ConfigureAwait(false);
+ await encoder.EncodeAsync(image, stream, cancellationToken);
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
index 0096a29d8..7203116c9 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
@@ -24,7 +24,7 @@ public class MagickReferenceDecoder : ImageDecoder
public static MagickReferenceDecoder Instance { get; } = new();
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Configuration configuration = options.Configuration;
BmpReadDefines bmpReadDefines = new()
@@ -32,8 +32,10 @@ public class MagickReferenceDecoder : ImageDecoder
IgnoreFileSize = !this.validate
};
- MagickReadSettings settings = new();
- settings.FrameCount = (int)options.MaxFrames;
+ MagickReadSettings settings = new()
+ {
+ FrameCount = (int)options.MaxFrames
+ };
settings.SetDefines(bmpReadDefines);
using MagickImageCollection magickImageCollection = new(stream, settings);
@@ -67,10 +69,10 @@ public class MagickReferenceDecoder : ImageDecoder
return new Image(configuration, new ImageMetadata(), framesList);
}
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup)
diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
index 6d8104059..503fd53ce 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
@@ -13,14 +13,14 @@ public class SystemDrawingReferenceDecoder : ImageDecoder
{
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();
- protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using SDBitmap sourceBitmap = new(stream);
PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
}
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using SDBitmap sourceBitmap = new(stream);
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
@@ -45,6 +45,6 @@ public class SystemDrawingReferenceDecoder : ImageDecoder
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap);
}
- protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(options, stream, cancellationToken);
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
index fb76d3b68..41b625959 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
@@ -20,7 +20,7 @@ public static partial class TestEnvironment
internal static Configuration Configuration => ConfigurationLazy.Value;
- internal static ImageDecoder GetReferenceDecoder(string filePath)
+ internal static IImageDecoder GetReferenceDecoder(string filePath)
{
IImageFormat format = GetImageFormat(filePath);
return Configuration.ImageFormatsManager.FindDecoder(format);
@@ -42,7 +42,7 @@ public static partial class TestEnvironment
private static void ConfigureCodecs(
this Configuration cfg,
IImageFormat imageFormat,
- ImageDecoder decoder,
+ IImageDecoder decoder,
ImageEncoder encoder,
IImageFormatDetector detector)
{
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
index d6a025fc4..884320476 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
@@ -214,7 +214,7 @@ public static class TestImageExtensions
bool grayscale = false,
bool appendPixelTypeToFileName = true,
bool appendSourceFileOrDescription = true,
- ImageDecoder decoder = null)
+ IImageDecoder decoder = null)
where TPixel : unmanaged, IPixel
{
using (Image referenceImage = GetReferenceOutputImage(
@@ -307,7 +307,7 @@ public static class TestImageExtensions
string extension = "png",
bool appendPixelTypeToFileName = true,
bool appendSourceFileOrDescription = true,
- ImageDecoder decoder = null)
+ IImageDecoder decoder = null)
where TPixel : unmanaged, IPixel
{
string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(
@@ -324,7 +324,7 @@ public static class TestImageExtensions
decoder ??= TestEnvironment.GetReferenceDecoder(referenceOutputFile);
using FileStream stream = File.OpenRead(referenceOutputFile);
- return decoder.Decode(DecoderOptions.Default, stream, default);
+ return decoder.Decode(DecoderOptions.Default, stream);
}
public static Image GetReferenceOutputImageMultiFrame(
@@ -343,17 +343,17 @@ public static class TestImageExtensions
var temporaryFrameImages = new List>();
- ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]);
+ IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]);
foreach (string path in frameFiles)
{
if (!File.Exists(path))
{
- throw new Exception("Reference output file missing: " + path);
+ throw new FileNotFoundException("Reference output file missing: " + path);
}
using FileStream stream = File.OpenRead(path);
- Image tempImage = decoder.Decode(DecoderOptions.Default, stream, default);
+ Image tempImage = decoder.Decode(DecoderOptions.Default, stream);
temporaryFrameImages.Add(tempImage);
}
@@ -511,7 +511,7 @@ public static class TestImageExtensions
public static Image CompareToOriginal