diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 467e52862..f36f3d09b 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -289,7 +289,7 @@ internal static class AotCompilerTools private static void AotCompileImageDecoder() where TPixel : unmanaged, IPixel where TDecoder : class, IImageDecoder - => default(TDecoder).Decode(default, default, default); + => default(TDecoder).Decode(default, default); /// /// This method pre-seeds the all in the AoT compiler. diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index b29940575..d4d055823 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -42,11 +42,11 @@ public sealed class Configuration /// Initializes a new instance of the class. /// /// A collection of configuration modules to register. - public Configuration(params IConfigurationModule[] configurationModules) + public Configuration(params IImageFormatConfigurationModule[] configurationModules) { if (configurationModules != null) { - foreach (IConfigurationModule p in configurationModules) + foreach (IImageFormatConfigurationModule p in configurationModules) { p.Configure(this); } @@ -180,7 +180,7 @@ public sealed class Configuration /// Registers a new format provider. /// /// The configuration provider to call configure on. - public void Configure(IConfigurationModule configuration) + public void Configure(IImageFormatConfigurationModule configuration) { Guard.NotNull(configuration, nameof(configuration)); configuration.Configure(this); @@ -203,7 +203,7 @@ public sealed class Configuration }; /// - /// Creates the default instance with the following s preregistered: + /// Creates the default instance with the following s preregistered: /// /// /// diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs index 05303058e..38e6e6ea6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp; @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Registers the image encoders, decoders and mime type detectors for the bmp format. /// -public sealed class BmpConfigurationModule : IConfigurationModule +public sealed class BmpConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(BmpFormat.Instance, new BmpEncoder()); - configuration.ImageFormatsManager.SetDecoder(BmpFormat.Instance, new BmpDecoder()); + configuration.ImageFormatsManager.SetDecoder(BmpFormat.Instance, BmpDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 35d5690c7..15c213f88 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Image decoder for generating an image out of a Windows bitmap stream. /// -public class BmpDecoder : IImageDecoderSpecialized +public sealed class BmpDecoder : SpecializedImageDecoder { + private BmpDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static BmpDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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,27 +29,23 @@ public class BmpDecoder : IImageDecoderSpecialized } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoderSpecialized.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)); Image image = new BmpDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options.GeneralOptions, image); + ScaleToTargetSize(options.GeneralOptions, image); return image; } /// - Image IImageDecoderSpecialized.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); + protected override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override BmpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index 26c4e7ec5..b3387ce80 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp; public sealed class BmpDecoderOptions : ISpecializedDecoderOptions { /// - public DecoderOptions GeneralOptions { get; set; } = new(); + public DecoderOptions GeneralOptions { get; init; } = new(); /// - /// Gets or sets the value indicating how to deal with skipped pixels, + /// Gets the value indicating how to deal with skipped pixels, /// which can occur during decoding run length encoded bitmaps. /// - public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } + public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; } } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 9e20da170..156e2f961 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -24,16 +24,9 @@ public sealed class BmpEncoder : QuantizingImageEncoder public bool SupportTransparency { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { BmpEncoderCore encoder = new(this, image.GetMemoryAllocator()); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - BmpEncoderCore encoder = new(this, image.GetMemoryAllocator()); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs index b0d8453a7..a67b06cb8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp; @@ -13,7 +13,7 @@ public sealed class BmpFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static BmpFormat Instance { get; } = new BmpFormat(); @@ -30,5 +30,5 @@ public sealed class BmpFormat : IImageFormat public IEnumerable FileExtensions => BmpConstants.FileExtensions; /// - public BmpMetadata CreateDefaultFormatMetadata() => new BmpMetadata(); + public BmpMetadata CreateDefaultFormatMetadata() => new(); } diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 3e3f1aa50..989fc49fc 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -21,27 +21,27 @@ public sealed class DecoderOptions internal static DecoderOptions Default { get; } = LazyOptions.Value; /// - /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. + /// Gets a custom configuration instance to be used by the image processing pipeline. /// - public Configuration Configuration { get; set; } = Configuration.Default; + public Configuration Configuration { get; internal set; } = Configuration.Default; /// - /// Gets or sets the target size to decode the image into. + /// Gets the target size to decode the image into. Scaling should use an operation equivalent to . /// - public Size? TargetSize { get; set; } + public Size? TargetSize { get; init; } /// - /// Gets or sets the sampler to use when resizing during decoding. + /// Gets the sampler to use when resizing during decoding. /// - public IResampler Sampler { get; set; } = KnownResamplers.Box; + public IResampler Sampler { get; init; } = KnownResamplers.Box; /// - /// Gets or sets a value indicating whether to ignore encoded metadata when decoding. + /// Gets a value indicating whether to ignore encoded metadata when decoding. /// - public bool SkipMetadata { get; set; } + public bool SkipMetadata { get; init; } /// - /// Gets or sets the maximum number of image frames to decode, inclusive. + /// Gets the maximum number of image frames to decode, inclusive. /// - public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } + public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } } diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs index 8c8067ada..31116d661 100644 --- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs +++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Gif; @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Registers the image encoders, decoders and mime type detectors for the gif format. /// -public sealed class GifConfigurationModule : IConfigurationModule +public sealed class GifConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(GifFormat.Instance, new GifEncoder()); - configuration.ImageFormatsManager.SetDecoder(GifFormat.Instance, new GifDecoder()); + configuration.ImageFormatsManager.SetDecoder(GifFormat.Instance, GifDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index cf8f4637e..e08fc6230 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Decoder for generating an image out of a gif encoded stream. /// -public sealed class GifDecoder : IImageDecoder +public sealed class GifDecoder : ImageDecoder { + private GifDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static GifDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +29,7 @@ public sealed class GifDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -28,12 +37,12 @@ public sealed class GifDecoder : IImageDecoder GifDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoder)this).Decode(options, stream, cancellationToken); + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index 351554eb0..386b1bd1c 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -16,16 +16,9 @@ public sealed class GifEncoder : QuantizingImageEncoder public GifColorTableMode? ColorTableMode { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { GifEncoderCore encoder = new(image.GetConfiguration(), this); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - GifEncoderCore encoder = new(image.GetConfiguration(), this); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs index df302c7ea..463642071 100644 --- a/src/ImageSharp/Formats/Gif/GifFormat.cs +++ b/src/ImageSharp/Formats/Gif/GifFormat.cs @@ -13,7 +13,7 @@ public sealed class GifFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static GifFormat Instance { get; } = new(); diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 7052bc49f..3729c98d9 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -6,35 +6,68 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats; /// -/// Encapsulates properties and methods required for decoding an image from a stream. +/// Defines the contract for all image decoders. /// -public interface IImageDecoder : IImageInfoDetector +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 = 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 pixel format. /// The general decoder options. /// The containing image data. - /// The token to monitor for cancellation requests. /// The . /// Thrown if the encoded image contains errors. - Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + 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 = default) where TPixel : unmanaged, IPixel; /// - /// Decodes the image from the specified stream to an . + /// 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 general decoder options. /// The containing image data. /// The token to monitor for cancellation requests. - /// The . + /// A representing the asynchronous operation. /// Thrown if the encoded image contains errors. - Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + public Task DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default); } diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 112c38bd5..42d04a54d 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -6,27 +6,32 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats; /// -/// Encapsulates properties and methods required for encoding an image to a stream. +/// Defines the contract for all image encoders. /// public interface IImageEncoder { /// - /// Encodes the image to the specified stream from the . + /// Gets a value indicating whether to ignore decoded metadata when encoding. + /// + public bool SkipMetadata { get; init; } + + /// + /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. - /// The to encode the image data to. - void Encode(Image image, Stream stream) + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel; /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. - /// The to encode the image data to. + /// The to encode from. + /// The to encode the image data to. /// The token to monitor for cancellation requests. - /// A representing the asynchronous operation. - Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) + /// A representing the asynchronous operation. + public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; } diff --git a/src/ImageSharp/Formats/IImageEncoderInternals.cs b/src/ImageSharp/Formats/IImageEncoderInternals.cs index ca9c474e1..131949c96 100644 --- a/src/ImageSharp/Formats/IImageEncoderInternals.cs +++ b/src/ImageSharp/Formats/IImageEncoderInternals.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.PixelFormats; @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats; /// -/// Abstraction for shared internals for ***DecoderCore implementations to be used with . +/// Abstraction for shared internals for ***DecoderCore implementations. /// internal interface IImageEncoderInternals { diff --git a/src/ImageSharp/IConfigurationModule.cs b/src/ImageSharp/Formats/IImageFormatConfigurationModule.cs similarity index 81% rename from src/ImageSharp/IConfigurationModule.cs rename to src/ImageSharp/Formats/IImageFormatConfigurationModule.cs index e2b221a08..b05a8c037 100644 --- a/src/ImageSharp/IConfigurationModule.cs +++ b/src/ImageSharp/Formats/IImageFormatConfigurationModule.cs @@ -1,12 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp; +namespace SixLabors.ImageSharp.Formats; /// /// Represents an interface that can register image encoders, decoders and image format detectors. /// -public interface IConfigurationModule +public interface IImageFormatConfigurationModule { /// /// Called when loaded into a configuration object so the module can register items into the configuration. 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/ISpecializedDecoderOptions.cs b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs index 75f643d0c..e0a4c9b62 100644 --- a/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs +++ b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats; public interface ISpecializedDecoderOptions { /// - /// Gets or sets the general decoder options. + /// Gets the general decoder options. /// - DecoderOptions GeneralOptions { get; set; } + DecoderOptions GeneralOptions { get; init; } } diff --git a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs b/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs similarity index 50% rename from src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs rename to src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs index 77cffe34c..e9506795c 100644 --- a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs +++ b/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs @@ -6,37 +6,51 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats; /// -/// The base class for all specialized image decoders. +/// Defines the contract for an image decoder that supports specialized options. /// /// The type of specialized options. -public interface IImageDecoderSpecialized : IImageDecoder +public interface ISpecializedImageDecoder : IImageDecoder 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. - public Image Decode(T options, Stream stream, CancellationToken cancellationToken) + 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. /// - /// - /// 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. - public Image Decode(T options, Stream stream, CancellationToken cancellationToken); + 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 = default) + 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 = default); } diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs new file mode 100644 index 000000000..695e9c844 --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -0,0 +1,254 @@ +// 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; + +/// +/// 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 : IImageDecoder +{ + /// + public Image Decode(DecoderOptions options, Stream stream) + where TPixel : unmanaged, IPixel + => WithSeekableStream( + options, + stream, + s => this.Decode(options, s, default)); + + /// + public Image Decode(DecoderOptions options, Stream stream) + => WithSeekableStream( + options, + stream, + s => this.Decode(options, s, default)); + + /// + public Task> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) + where TPixel : unmanaged, IPixel + => WithSeekableMemoryStreamAsync( + options, + stream, + (s, ct) => this.Decode(options, s, ct), + cancellationToken); + + /// + public Task DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) + => WithSeekableMemoryStreamAsync( + options, + stream, + (s, ct) => this.Decode(options, s, ct), + cancellationToken); + + /// + 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 = default) + => WithSeekableMemoryStreamAsync( + options, + stream, + (s, ct) => this.Identify(options, s, ct), + cancellationToken); + + /// + /// 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 general 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(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an . + /// + /// + /// 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 . + /// Thrown if the encoded image contains errors. + protected abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + /// 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. + protected abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + /// Performs a scaling operation against the decoded image. If the target size is not set, or the image size + /// already matches the target size, the image is untouched. + /// + /// The decoder options. + /// The decoded image. + protected static void ScaleToTargetSize(DecoderOptions options, Image image) + { + if (ShouldResize(options, image)) + { + ResizeOptions resizeOptions = new() + { + Size = options.TargetSize.Value, + Sampler = options.Sampler, + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(resizeOptions)); + } + } + + /// + /// Determines whether the decoded image should be resized. + /// + /// The decoder options. + /// The decoded image. + /// if the image should be resized, otherwise; . + private static bool ShouldResize(DecoderOptions options, Image image) + { + if (options.TargetSize is null) + { + return false; + } + + Size targetSize = options.TargetSize.Value; + Size currentSize = image.Size(); + return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; + } + + 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 PeformActionAndResetPosition(Stream s, long position) + { + T result = action(s); + + // Issue #2259. 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. + // The stream is always seekable in this scenario. + if (stream.Position != s.Position && s.Position != s.Length) + { + stream.Position = position + s.Position; + } + + return result; + } + + if (stream.CanSeek) + { + return PeformActionAndResetPosition(stream, stream.Position); + } + + Configuration configuration = options.Configuration; + using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); + stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize); + memoryStream.Position = 0; + + return action(memoryStream); + } + + internal static Task WithSeekableMemoryStreamAsync( + 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."); + } + + Task PeformActionAndResetPosition(Stream s, long position, CancellationToken ct) + { + try + { + T result = action(s, ct); + + // Issue #2259. 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. + // We check here that the input stream is seekable because it is not guaranteed to be so since + // we always copy input streams of unknown type. + if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length) + { + stream.Position = position + s.Position; + } + + return Task.FromResult(result); + } + catch (OperationCanceledException) + { + return Task.FromCanceled(cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + // 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. + if (stream is MemoryStream ms) + { + return PeformActionAndResetPosition(ms, ms.Position, cancellationToken); + } + + if (stream is ChunkedMemoryStream cms) + { + return PeformActionAndResetPosition(cms, cms.Position, cancellationToken); + } + + return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken); + } + + private static async Task CopyToMemoryStreamAndActionAsync( + DecoderOptions options, + Stream stream, + Func> action, + CancellationToken cancellationToken) + { + long position = stream.CanSeek ? stream.Position : 0; + Configuration configuration = options.Configuration; + using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); + await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); + memoryStream.Position = 0; + return await action(memoryStream, position, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderExtensions.cs b/src/ImageSharp/Formats/ImageDecoderExtensions.cs deleted file mode 100644 index a18974bbd..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 IImageDecoder 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 IImageDecoder 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 IImageDecoder 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 IImageDecoder 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 IImageDecoder 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 IImageDecoder 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 IImageDecoderSpecialized 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 IImageDecoderSpecialized 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 IImageDecoderSpecialized 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 IImageDecoderSpecialized 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/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index cedd95f6d..50635dbef 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -5,61 +5,21 @@ using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Formats; /// -/// Utility methods for . +/// Utility methods for . /// internal static class ImageDecoderUtilities { - /// - /// Performs a resize operation against the decoded image. If the target size is not set, or the image size - /// already matches the target size, the image is untouched. - /// - /// The decoder options. - /// The decoded image. - public static void Resize(DecoderOptions options, Image image) - { - if (ShouldResize(options, image)) - { - ResizeOptions resizeOptions = new() - { - Size = options.TargetSize.Value, - Sampler = options.Sampler, - Mode = ResizeMode.Max - }; - - image.Mutate(x => x.Resize(resizeOptions)); - } - } - - /// - /// Determines whether the decoded image should be resized. - /// - /// The decoder options. - /// The decoded image. - /// if the image should be resized, otherwise; . - private static bool ShouldResize(DecoderOptions options, Image image) - { - if (options.TargetSize is null) - { - return false; - } - - Size targetSize = options.TargetSize.Value; - Size currentSize = image.Size(); - return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; - } - internal static IImageInfo Identify( this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) { - using BufferedReadStream bufferedReadStream = new(configuration, stream); + using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken); try { @@ -91,7 +51,7 @@ internal static class ImageDecoderUtilities CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - using BufferedReadStream bufferedReadStream = new(configuration, stream); + using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken); try { diff --git a/src/ImageSharp/Formats/ImageEncoder.cs b/src/ImageSharp/Formats/ImageEncoder.cs index a0c087e64..d6870f716 100644 --- a/src/ImageSharp/Formats/ImageEncoder.cs +++ b/src/ImageSharp/Formats/ImageEncoder.cs @@ -1,43 +1,94 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats; /// -/// The base class for all image encoders. +/// Acts as a base class for image encoders. +/// Types that inherit this encoder are required to implement cancellable synchronous encoding operations only. /// public abstract class ImageEncoder : IImageEncoder { - /// - /// Gets a value indicating whether to ignore decoded metadata when encoding. - /// + /// public bool SkipMetadata { get; init; } /// - public abstract void Encode(Image image, Stream stream) - where TPixel : unmanaged, IPixel; + public void Encode(Image image, Stream stream) + where TPixel : unmanaged, IPixel + => this.EncodeWithSeekableStream(image, stream, default); /// - public abstract Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; -} + public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken = default) + where TPixel : unmanaged, IPixel + => this.EncodeWithSeekableStreamAsync(image, stream, cancellationToken); -/// -/// The base class for all image encoders that allow color palette generation via quantization. -/// -public abstract class QuantizingImageEncoder : ImageEncoder -{ /// - /// Gets the quantizer used to generate the color palette. + /// Encodes the image to the specified stream from the . /// - public IQuantizer Quantizer { get; init; } = KnownQuantizers.Octree; + /// + /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use. + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The token to monitor for cancellation requests. + protected abstract void Encode(Image image, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; - /// - /// Gets the used for quantization when building color palettes. - /// - public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy(); + private void EncodeWithSeekableStream(Image image, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + Configuration configuration = image.GetConfiguration(); + if (stream.CanSeek) + { + this.Encode(image, stream, cancellationToken); + } + else + { + using ChunkedMemoryStream ms = new(configuration.MemoryAllocator); + this.Encode(image, stream, cancellationToken); + ms.Position = 0; + ms.CopyTo(stream, configuration.StreamProcessingBufferSize); + } + } + + private async Task EncodeWithSeekableStreamAsync(Image image, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + Configuration configuration = image.GetConfiguration(); + if (stream.CanSeek) + { + await DoEncodeAsync(stream).ConfigureAwait(false); + } + else + { + using ChunkedMemoryStream ms = new(configuration.MemoryAllocator); + await DoEncodeAsync(ms); + ms.Position = 0; + await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken) + .ConfigureAwait(false); + } + + Task DoEncodeAsync(Stream innerStream) + { + try + { + // TODO: Are synchronous IO writes OK? We avoid reads. + this.Encode(image, innerStream, cancellationToken); + return Task.CompletedTask; + } + catch (OperationCanceledException) + { + return Task.FromCanceled(cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + } } diff --git a/src/ImageSharp/Formats/ImageEncoderUtilities.cs b/src/ImageSharp/Formats/ImageEncoderUtilities.cs deleted file mode 100644 index 665431c95..000000000 --- a/src/ImageSharp/Formats/ImageEncoderUtilities.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats; - -internal static class ImageEncoderUtilities -{ - public static async Task EncodeAsync( - this IImageEncoderInternals encoder, - Image image, - Stream stream, - CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - { - Configuration configuration = image.GetConfiguration(); - if (stream.CanSeek) - { - await DoEncodeAsync(stream).ConfigureAwait(false); - } - else - { - using MemoryStream ms = new(); - await DoEncodeAsync(ms); - ms.Position = 0; - await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken) - .ConfigureAwait(false); - } - - Task DoEncodeAsync(Stream innerStream) - { - try - { - encoder.Encode(image, innerStream, cancellationToken); - return Task.CompletedTask; - } - catch (OperationCanceledException) - { - return Task.FromCanceled(cancellationToken); - } - catch (Exception ex) - { - return Task.FromException(ex); - } - } - } - - public static void Encode( - this IImageEncoderInternals encoder, - Image image, - Stream stream) - where TPixel : unmanaged, IPixel - => encoder.Encode(image, stream, default); -} diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index f8763b72f..6249f8dc7 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -26,7 +26,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsBmp(this Image source, string path) => SaveAsBmp(source, path, null); + public static void SaveAsBmp(this Image source, string path) => SaveAsBmp(source, path, default); /// /// Saves the image to the given stream with the Bmp format. @@ -35,7 +35,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsBmpAsync(this Image source, string path) => SaveAsBmpAsync(source, path, null); + public static Task SaveAsBmpAsync(this Image source, string path) => SaveAsBmpAsync(source, path, default); /// /// Saves the image to the given stream with the Bmp format. @@ -46,7 +46,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsBmpAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsBmpAsync(source, path, null, cancellationToken); + => SaveAsBmpAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Bmp format. @@ -69,11 +69,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), - cancellationToken); + public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Bmp format. @@ -82,7 +82,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsBmp(this Image source, Stream stream) - => SaveAsBmp(source, stream, null); + => SaveAsBmp(source, stream, default); /// /// Saves the image to the given stream with the Bmp format. @@ -93,7 +93,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsBmpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsBmpAsync(source, stream, null, cancellationToken); + => SaveAsBmpAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Bmp format. @@ -104,8 +104,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); /// /// Saves the image to the given stream with the Bmp format. @@ -116,11 +116,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), - cancellationToken); + public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Gif format. @@ -128,7 +128,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsGif(this Image source, string path) => SaveAsGif(source, path, null); + public static void SaveAsGif(this Image source, string path) => SaveAsGif(source, path, default); /// /// Saves the image to the given stream with the Gif format. @@ -137,7 +137,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsGifAsync(this Image source, string path) => SaveAsGifAsync(source, path, null); + public static Task SaveAsGifAsync(this Image source, string path) => SaveAsGifAsync(source, path, default); /// /// Saves the image to the given stream with the Gif format. @@ -148,7 +148,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsGifAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsGifAsync(source, path, null, cancellationToken); + => SaveAsGifAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Gif format. @@ -171,11 +171,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), - cancellationToken); + public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Gif format. @@ -184,7 +184,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsGif(this Image source, Stream stream) - => SaveAsGif(source, stream, null); + => SaveAsGif(source, stream, default); /// /// Saves the image to the given stream with the Gif format. @@ -195,7 +195,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsGifAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsGifAsync(source, stream, null, cancellationToken); + => SaveAsGifAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Gif format. @@ -206,8 +206,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); /// /// Saves the image to the given stream with the Gif format. @@ -218,11 +218,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), - cancellationToken); + public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Jpeg format. @@ -230,7 +230,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsJpeg(this Image source, string path) => SaveAsJpeg(source, path, null); + public static void SaveAsJpeg(this Image source, string path) => SaveAsJpeg(source, path, default); /// /// Saves the image to the given stream with the Jpeg format. @@ -239,7 +239,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsJpegAsync(this Image source, string path) => SaveAsJpegAsync(source, path, null); + public static Task SaveAsJpegAsync(this Image source, string path) => SaveAsJpegAsync(source, path, default); /// /// Saves the image to the given stream with the Jpeg format. @@ -250,7 +250,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsJpegAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsJpegAsync(source, path, null, cancellationToken); + => SaveAsJpegAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Jpeg format. @@ -273,11 +273,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), - cancellationToken); + public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Jpeg format. @@ -286,7 +286,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsJpeg(this Image source, Stream stream) - => SaveAsJpeg(source, stream, null); + => SaveAsJpeg(source, stream, default); /// /// Saves the image to the given stream with the Jpeg format. @@ -297,7 +297,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsJpegAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsJpegAsync(source, stream, null, cancellationToken); + => SaveAsJpegAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Jpeg format. @@ -308,8 +308,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); /// /// Saves the image to the given stream with the Jpeg format. @@ -320,11 +320,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), - cancellationToken); + public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Pbm format. @@ -332,7 +332,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsPbm(this Image source, string path) => SaveAsPbm(source, path, null); + public static void SaveAsPbm(this Image source, string path) => SaveAsPbm(source, path, default); /// /// Saves the image to the given stream with the Pbm format. @@ -341,7 +341,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsPbmAsync(this Image source, string path) => SaveAsPbmAsync(source, path, null); + public static Task SaveAsPbmAsync(this Image source, string path) => SaveAsPbmAsync(source, path, default); /// /// Saves the image to the given stream with the Pbm format. @@ -352,7 +352,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsPbmAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsPbmAsync(source, path, null, cancellationToken); + => SaveAsPbmAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Pbm format. @@ -375,11 +375,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), - cancellationToken); + public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Pbm format. @@ -388,7 +388,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsPbm(this Image source, Stream stream) - => SaveAsPbm(source, stream, null); + => SaveAsPbm(source, stream, default); /// /// Saves the image to the given stream with the Pbm format. @@ -399,7 +399,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsPbmAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsPbmAsync(source, stream, null, cancellationToken); + => SaveAsPbmAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Pbm format. @@ -410,8 +410,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance)); /// /// Saves the image to the given stream with the Pbm format. @@ -422,11 +422,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), - cancellationToken); + public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Png format. @@ -434,7 +434,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsPng(this Image source, string path) => SaveAsPng(source, path, null); + public static void SaveAsPng(this Image source, string path) => SaveAsPng(source, path, default); /// /// Saves the image to the given stream with the Png format. @@ -443,7 +443,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsPngAsync(this Image source, string path) => SaveAsPngAsync(source, path, null); + public static Task SaveAsPngAsync(this Image source, string path) => SaveAsPngAsync(source, path, default); /// /// Saves the image to the given stream with the Png format. @@ -454,7 +454,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsPngAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsPngAsync(source, path, null, cancellationToken); + => SaveAsPngAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Png format. @@ -477,11 +477,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), - cancellationToken); + public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Png format. @@ -490,7 +490,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsPng(this Image source, Stream stream) - => SaveAsPng(source, stream, null); + => SaveAsPng(source, stream, default); /// /// Saves the image to the given stream with the Png format. @@ -501,7 +501,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsPngAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsPngAsync(source, stream, null, cancellationToken); + => SaveAsPngAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Png format. @@ -512,8 +512,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); /// /// Saves the image to the given stream with the Png format. @@ -524,11 +524,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), - cancellationToken); + public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Tga format. @@ -536,7 +536,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsTga(this Image source, string path) => SaveAsTga(source, path, null); + public static void SaveAsTga(this Image source, string path) => SaveAsTga(source, path, default); /// /// Saves the image to the given stream with the Tga format. @@ -545,7 +545,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsTgaAsync(this Image source, string path) => SaveAsTgaAsync(source, path, null); + public static Task SaveAsTgaAsync(this Image source, string path) => SaveAsTgaAsync(source, path, default); /// /// Saves the image to the given stream with the Tga format. @@ -556,7 +556,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsTgaAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsTgaAsync(source, path, null, cancellationToken); + => SaveAsTgaAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Tga format. @@ -579,11 +579,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), - cancellationToken); + public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Tga format. @@ -592,7 +592,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsTga(this Image source, Stream stream) - => SaveAsTga(source, stream, null); + => SaveAsTga(source, stream, default); /// /// Saves the image to the given stream with the Tga format. @@ -603,7 +603,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsTgaAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsTgaAsync(source, stream, null, cancellationToken); + => SaveAsTgaAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Tga format. @@ -614,8 +614,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsTga(this Image source, Stream stream, TgaEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance)); /// /// Saves the image to the given stream with the Tga format. @@ -626,11 +626,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), - cancellationToken); + public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Webp format. @@ -638,7 +638,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsWebp(this Image source, string path) => SaveAsWebp(source, path, null); + public static void SaveAsWebp(this Image source, string path) => SaveAsWebp(source, path, default); /// /// Saves the image to the given stream with the Webp format. @@ -647,7 +647,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsWebpAsync(this Image source, string path) => SaveAsWebpAsync(source, path, null); + public static Task SaveAsWebpAsync(this Image source, string path) => SaveAsWebpAsync(source, path, default); /// /// Saves the image to the given stream with the Webp format. @@ -658,7 +658,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsWebpAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsWebpAsync(source, path, null, cancellationToken); + => SaveAsWebpAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Webp format. @@ -681,11 +681,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), - cancellationToken); + public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Webp format. @@ -694,7 +694,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsWebp(this Image source, Stream stream) - => SaveAsWebp(source, stream, null); + => SaveAsWebp(source, stream, default); /// /// Saves the image to the given stream with the Webp format. @@ -705,7 +705,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsWebpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsWebpAsync(source, stream, null, cancellationToken); + => SaveAsWebpAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Webp format. @@ -716,8 +716,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance)); /// /// Saves the image to the given stream with the Webp format. @@ -728,11 +728,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), - cancellationToken); + public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Tiff format. @@ -740,7 +740,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null); + public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, default); /// /// Saves the image to the given stream with the Tiff format. @@ -749,7 +749,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null); + public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, default); /// /// Saves the image to the given stream with the Tiff format. @@ -760,7 +760,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAsTiffAsync(this Image source, string path, CancellationToken cancellationToken) - => SaveAsTiffAsync(source, path, null, cancellationToken); + => SaveAsTiffAsync(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the Tiff format. @@ -783,11 +783,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), - cancellationToken); + public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), + cancellationToken); /// /// Saves the image to the given stream with the Tiff format. @@ -796,7 +796,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAsTiff(this Image source, Stream stream) - => SaveAsTiff(source, stream, null); + => SaveAsTiff(source, stream, default); /// /// Saves the image to the given stream with the Tiff format. @@ -807,7 +807,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAsTiffAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAsTiffAsync(source, stream, null, cancellationToken); + => SaveAsTiffAsync(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the Tiff format. @@ -818,8 +818,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); /// /// Saves the image to the given stream with the Tiff format. @@ -830,10 +830,10 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), - cancellationToken); + public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), + cancellationToken); } diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index 9ca3a0f22..7498aa7c3 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -45,7 +45,7 @@ public static partial class ImageExtensions /// The image this method extends. /// The file path to save the image to. /// Thrown if the path is null. - public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, null); + public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, default); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -54,7 +54,7 @@ public static partial class ImageExtensions /// The file path to save the image to. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, null); + public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, default); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -65,7 +65,7 @@ public static partial class ImageExtensions /// Thrown if the path is null. /// A representing the asynchronous operation. public static Task SaveAs<#= fmt #>Async(this Image source, string path, CancellationToken cancellationToken) - => SaveAs<#= fmt #>Async(source, path, null, cancellationToken); + => SaveAs<#= fmt #>Async(source, path, default, cancellationToken); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -88,11 +88,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the path is null. /// A representing the asynchronous operation. - public static Task SaveAs<#= fmt #>Async(this Image source, string path, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), - cancellationToken); + public static Task SaveAs<#= fmt #>Async(this Image source, string path, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + path, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), + cancellationToken); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -101,7 +101,7 @@ public static partial class ImageExtensions /// The stream to save the image to. /// Thrown if the stream is null. public static void SaveAs<#= fmt #>(this Image source, Stream stream) - => SaveAs<#= fmt #>(source, stream, null); + => SaveAs<#= fmt #>(source, stream, default); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -112,7 +112,7 @@ public static partial class ImageExtensions /// Thrown if the stream is null. /// A representing the asynchronous operation. public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, CancellationToken cancellationToken = default) - => SaveAs<#= fmt #>Async(source, stream, null, cancellationToken); + => SaveAs<#= fmt #>Async(source, stream, default, cancellationToken); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -123,8 +123,8 @@ public static partial class ImageExtensions /// Thrown if the stream is null. public static void SaveAs<#= fmt #>(this Image source, Stream stream, <#= fmt #>Encoder encoder) => source.Save( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -135,11 +135,11 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) => - source.SaveAsync( - stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), - cancellationToken); + public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) + => source.SaveAsync( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), + cancellationToken); <# } diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs index be7230870..dc09bb6a1 100644 --- a/src/ImageSharp/Formats/ImageFormatManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -23,7 +23,7 @@ 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(); @@ -123,7 +123,7 @@ public class ImageFormatManager Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(encoder, nameof(encoder)); this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (_, _) => encoder); } /// @@ -136,7 +136,7 @@ public class ImageFormatManager Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(decoder, nameof(decoder)); this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (_, _) => decoder); } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs index 963b8eb29..3f1b7bc37 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Jpeg; @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// -public sealed class JpegConfigurationModule : IConfigurationModule +public sealed class JpegConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()); - configuration.ImageFormatsManager.SetDecoder(JpegFormat.Instance, new JpegDecoder()); + configuration.ImageFormatsManager.SetDecoder(JpegFormat.Instance, JpegDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 7e85ddad5..b8a142d28 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Decoder for generating an image out of a jpeg encoded stream. /// -public sealed class JpegDecoder : IImageDecoderSpecialized +public sealed class JpegDecoder : SpecializedImageDecoder { + private JpegDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static JpegDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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,15 +30,7 @@ public sealed class JpegDecoder : IImageDecoderSpecialized } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoderSpecialized.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)); @@ -39,13 +40,17 @@ public sealed class JpegDecoder : IImageDecoderSpecialized if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) { - ImageDecoderUtilities.Resize(options.GeneralOptions, image); + ScaleToTargetSize(options.GeneralOptions, image); } return image; } /// - Image IImageDecoderSpecialized.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); + protected override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override JpegDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs index 193b2d3a8..78ee31741 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs @@ -8,11 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// public sealed class JpegDecoderOptions : ISpecializedDecoderOptions { + /// + public DecoderOptions GeneralOptions { get; init; } = new(); + /// - /// Gets or sets the resize mode. + /// Gets the resize mode. /// - public JpegDecoderResizeMode ResizeMode { get; set; } - - /// - public DecoderOptions GeneralOptions { get; set; } = new(); + public JpegDecoderResizeMode ResizeMode { get; init; } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 2ddd829f8..5ff4b1694 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -48,16 +48,9 @@ public sealed class JpegEncoder : ImageEncoder public JpegEncodingColor? ColorType { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { JpegEncoderCore encoder = new(this); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - JpegEncoderCore encoder = new(this); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs index b9c126e29..a07be33fc 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Jpeg; @@ -13,7 +13,7 @@ public sealed class JpegFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static JpegFormat Instance { get; } = new JpegFormat(); @@ -30,5 +30,5 @@ public sealed class JpegFormat : IImageFormat public IEnumerable FileExtensions => JpegConstants.FileExtensions; /// - public JpegMetadata CreateDefaultFormatMetadata() => new JpegMetadata(); + public JpegMetadata CreateDefaultFormatMetadata() => new(); } diff --git a/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs b/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs index cd411a2d7..9e5d6d3bc 100644 --- a/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs +++ b/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Pbm; /// /// Registers the image encoders, decoders and mime type detectors for the Pbm format. /// -public sealed class PbmConfigurationModule : IConfigurationModule +public sealed class PbmConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(PbmFormat.Instance, new PbmEncoder()); - configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, new PbmDecoder()); + configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, PbmDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new PbmImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs index 2caf8ecc1..f7b32b5fc 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs @@ -24,10 +24,19 @@ namespace SixLabors.ImageSharp.Formats.Pbm; /// /// The specification of these images is found at . /// -public sealed class PbmDecoder : IImageDecoder +public sealed class PbmDecoder : ImageDecoder { + private PbmDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static PbmDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +45,7 @@ public sealed class PbmDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -44,12 +53,12 @@ public sealed class PbmDecoder : IImageDecoder PbmDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoder)this).Decode(options, stream, cancellationToken); + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs index 6b25347c0..0f492fae7 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs @@ -42,21 +42,14 @@ public sealed class PbmEncoder : ImageEncoder public PbmColorType? ColorType { get; init; } /// - /// Gets the Data Type of the pixel components. + /// Gets the data type of the pixel components. /// public PbmComponentType? ComponentType { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { PbmEncoderCore encoder = new(image.GetConfiguration(), this); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - PbmEncoderCore encoder = new(image.GetConfiguration(), this); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmFormat.cs b/src/ImageSharp/Formats/Pbm/PbmFormat.cs index bdf5e785e..f21728b07 100644 --- a/src/ImageSharp/Formats/Pbm/PbmFormat.cs +++ b/src/ImageSharp/Formats/Pbm/PbmFormat.cs @@ -13,7 +13,7 @@ public sealed class PbmFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static PbmFormat Instance { get; } = new(); diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index caee81719..42f7f6e8e 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Png; @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Png; /// /// Registers the image encoders, decoders and mime type detectors for the png format. /// -public sealed class PngConfigurationModule : IConfigurationModule +public sealed class PngConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(PngFormat.Instance, new PngEncoder()); - configuration.ImageFormatsManager.SetDecoder(PngFormat.Instance, new PngDecoder()); + configuration.ImageFormatsManager.SetDecoder(PngFormat.Instance, PngDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index bcc193f0b..56e76da10 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Png; /// /// Decoder for generating an image out of a png encoded stream. /// -public sealed class PngDecoder : IImageDecoder +public sealed class PngDecoder : ImageDecoder { + private PngDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static PngDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +29,7 @@ public sealed class PngDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -28,13 +37,13 @@ public sealed class PngDecoder : IImageDecoder PngDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.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)); @@ -47,48 +56,47 @@ public sealed class PngDecoder : IImageDecoder PngColorType color = meta.ColorType.GetValueOrDefault(); PngBitDepth bits = meta.BitDepth.GetValueOrDefault(); - var imageDecoder = (IImageDecoder)this; switch (color) { case PngColorType.Grayscale: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Palette: - return imageDecoder.Decode(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? imageDecoder.Decode(options, stream, cancellationToken) - : imageDecoder.Decode(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); default: - return imageDecoder.Decode(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 45a97580e..37cc8b2f6 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -177,7 +177,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.InitializeImage(metadata, out image); } - this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); + this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata, cancellationToken); break; case PngChunkType.Palette: @@ -556,7 +556,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The png chunk containing the compressed scanline data. /// The pixel data. /// The png metadata - private void ReadScanlines(PngChunk chunk, ImageFrame image, PngMetadata pngMetadata) + /// The cancellation token. + private void ReadScanlines(PngChunk chunk, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using ZlibInflateStream deframeStream = new(this.currentStream, this.ReadNextDataChunk); @@ -565,11 +566,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, image, pngMetadata); + this.DecodeInterlacedPixelData(dataStream, image, pngMetadata, cancellationToken); } else { - this.DecodePixelData(dataStream, image, pngMetadata); + this.DecodePixelData(dataStream, image, pngMetadata, cancellationToken); } } @@ -580,11 +581,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The compressed pixel data stream. /// The image to decode to. /// The png metadata - private void DecodePixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata) + /// The CancellationToken + private void DecodePixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { while (this.currentRow < this.header.Height) { + cancellationToken.ThrowIfCancellationRequested(); Span scanlineSpan = this.scanline.GetSpan(); while (this.currentRowBytesRead < this.bytesPerScanline) { @@ -640,7 +643,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// The compressed pixel data stream. /// The current image. /// The png metadata. - private void DecodeInterlacedPixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata) + /// The cancellation token. + private void DecodeInterlacedPixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int pass = 0; @@ -662,6 +666,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals while (this.currentRow < this.header.Height) { + cancellationToken.ThrowIfCancellationRequested(); while (this.currentRowBytesRead < bytesPerInterlaceScanline) { int bytesRead = compressedStream.Read(this.scanline.GetSpan(), this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead); diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index ea99a2a5d..1d068303b 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -75,19 +75,9 @@ public class PngEncoder : QuantizingImageEncoder public PngTransparentColorMode TransparentColorMode { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { using PngEncoderCore encoder = new(image.GetMemoryAllocator(), image.GetConfiguration(), this); - encoder.Encode(image, stream); - } - - /// - public override async Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - // The introduction of a local variable that refers to an object the implements - // IDisposable means you must use async/await, where the compiler generates the - // state machine and a continuation. - using PngEncoderCore encoder = new(image.GetMemoryAllocator(), image.GetConfiguration(), this); - await encoder.EncodeAsync(image, stream, cancellationToken).ConfigureAwait(false); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs index a0cc06500..2d1f2dcc7 100644 --- a/src/ImageSharp/Formats/Png/PngFormat.cs +++ b/src/ImageSharp/Formats/Png/PngFormat.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Png; @@ -13,7 +13,7 @@ public sealed class PngFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static PngFormat Instance { get; } = new PngFormat(); @@ -30,5 +30,5 @@ public sealed class PngFormat : IImageFormat public IEnumerable FileExtensions => PngConstants.FileExtensions; /// - public PngMetadata CreateDefaultFormatMetadata() => new PngMetadata(); + public PngMetadata CreateDefaultFormatMetadata() => new(); } diff --git a/src/ImageSharp/Formats/QuantizingImageEncoder.cs b/src/ImageSharp/Formats/QuantizingImageEncoder.cs new file mode 100644 index 000000000..b7eb86afb --- /dev/null +++ b/src/ImageSharp/Formats/QuantizingImageEncoder.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Acts as a base class for all image encoders that allow color palette generation via quantization. +/// +public abstract class QuantizingImageEncoder : ImageEncoder +{ + /// + /// Gets the quantizer used to generate the color palette. + /// + public IQuantizer Quantizer { get; init; } = KnownQuantizers.Octree; + + /// + /// Gets the used for quantization when building color palettes. + /// + public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy(); +} diff --git a/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs b/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs new file mode 100644 index 000000000..fa6461464 --- /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 +{ + /// + 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 = default) + where TPixel : unmanaged, IPixel + => WithSeekableMemoryStreamAsync( + options.GeneralOptions, + stream, + (s, ct) => this.Decode(options, s, ct), + cancellationToken); + + /// + public Task DecodeAsync(T options, Stream stream, CancellationToken cancellationToken = default) + => WithSeekableMemoryStreamAsync( + options.GeneralOptions, + stream, + (s, ct) => this.Decode(options, s, ct), + cancellationToken); + + /// + /// 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); +} diff --git a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs index 06f93d1ce..45847b0a5 100644 --- a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Tga; /// /// Registers the image encoders, decoders and mime type detectors for the tga format. /// -public sealed class TgaConfigurationModule : IConfigurationModule +public sealed class TgaConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(TgaFormat.Instance, new TgaEncoder()); - configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder()); + configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, TgaDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index 362daa77d..f6f1e6762 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Tga; /// /// Image decoder for Truevision TGA images. /// -public sealed class TgaDecoder : IImageDecoder +public sealed class TgaDecoder : ImageDecoder { + private TgaDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static TgaDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +29,7 @@ public sealed class TgaDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -28,12 +37,12 @@ public sealed class TgaDecoder : IImageDecoder TgaDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoder)this).Decode(options, stream, cancellationToken); + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoder.cs b/src/ImageSharp/Formats/Tga/TgaEncoder.cs index e0a393235..71acf3ae8 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoder.cs @@ -21,16 +21,9 @@ public sealed class TgaEncoder : ImageEncoder public TgaCompression Compression { get; init; } = TgaCompression.RunLength; /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { TgaEncoderCore encoder = new(this, image.GetMemoryAllocator()); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - TgaEncoderCore encoder = new(this, image.GetMemoryAllocator()); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tga/TgaFormat.cs b/src/ImageSharp/Formats/Tga/TgaFormat.cs index 886d4eea3..e024dfc62 100644 --- a/src/ImageSharp/Formats/Tga/TgaFormat.cs +++ b/src/ImageSharp/Formats/Tga/TgaFormat.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tga; public sealed class TgaFormat : IImageFormat { /// - /// Gets the current instance. + /// Gets the shared instance. /// public static TgaFormat Instance { get; } = new TgaFormat(); @@ -26,5 +26,5 @@ public sealed class TgaFormat : IImageFormat public IEnumerable FileExtensions => TgaConstants.FileExtensions; /// - public TgaMetadata CreateDefaultFormatMetadata() => new TgaMetadata(); + public TgaMetadata CreateDefaultFormatMetadata() => new(); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index 4e1c9c2f8..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 = ((IImageDecoder)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/TiffConfigurationModule.cs b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs index 67b651723..d63ea2158 100644 --- a/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs +++ b/src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Registers the image encoders, decoders and mime type detectors for the TIFF format. /// -public sealed class TiffConfigurationModule : IConfigurationModule +public sealed class TiffConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); - configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); + configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, TiffDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index b433222a0..3ec9b3d68 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Image decoder for generating an image out of a TIFF stream. /// -public class TiffDecoder : IImageDecoder +public class TiffDecoder : ImageDecoder { + private TiffDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static TiffDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +29,7 @@ public class TiffDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -28,12 +37,12 @@ public class TiffDecoder : IImageDecoder TiffDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoder)this).Decode(options, stream, 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 c9002e131..887d4eda4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -114,7 +114,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/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index e7bb08cdc..24cca41dc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -40,16 +40,9 @@ public class TiffEncoder : QuantizingImageEncoder public TiffPredictor? HorizontalPredictor { get; init; } /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { TiffEncoderCore encode = new(this, image.GetMemoryAllocator()); - encode.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - TiffEncoderCore encoder = new(this, image.GetMemoryAllocator()); - return encoder.EncodeAsync(image, stream, cancellationToken); + encode.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormat.cs b/src/ImageSharp/Formats/Tiff/TiffFormat.cs index 62ad93c84..76a06d013 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -15,7 +15,7 @@ public sealed class TiffFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static TiffFormat Instance { get; } = new TiffFormat(); diff --git a/src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs b/src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs index f266cde32..571749957 100644 --- a/src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs @@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Registers the image encoders, decoders and mime type detectors for the webp format. /// -public sealed class WebpConfigurationModule : IConfigurationModule +public sealed class WebpConfigurationModule : IImageFormatConfigurationModule { /// public void Configure(Configuration configuration) { - configuration.ImageFormatsManager.SetDecoder(WebpFormat.Instance, new WebpDecoder()); + configuration.ImageFormatsManager.SetDecoder(WebpFormat.Instance, WebpDecoder.Instance); configuration.ImageFormatsManager.SetEncoder(WebpFormat.Instance, new WebpEncoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new WebpImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index 86c868d7e..7a97b86a7 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -8,10 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Image decoder for generating an image out of a webp stream. /// -public sealed class WebpDecoder : IImageDecoder +public sealed class WebpDecoder : ImageDecoder { + private WebpDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static WebpDecoder Instance { get; } = new(); + /// - IImageInfo IImageInfoDetector.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 +30,7 @@ public sealed class WebpDecoder : IImageDecoder } /// - Image IImageDecoder.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)); @@ -29,12 +38,12 @@ public sealed class WebpDecoder : IImageDecoder using WebpDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoder)this).Decode(options, stream, cancellationToken); + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } diff --git a/src/ImageSharp/Formats/Webp/WebpEncoder.cs b/src/ImageSharp/Formats/Webp/WebpEncoder.cs index 359128254..e314d3801 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoder.cs @@ -80,16 +80,9 @@ public sealed class WebpEncoder : ImageEncoder public int NearLosslessQuality { get; init; } = 100; /// - public override void Encode(Image image, Stream stream) + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { WebpEncoderCore encoder = new(this, image.GetMemoryAllocator()); - encoder.Encode(image, stream); - } - - /// - public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - WebpEncoderCore encoder = new(this, image.GetMemoryAllocator()); - return encoder.EncodeAsync(image, stream, cancellationToken); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpFormat.cs b/src/ImageSharp/Formats/Webp/WebpFormat.cs index cc2073bc0..29c74b11b 100644 --- a/src/ImageSharp/Formats/Webp/WebpFormat.cs +++ b/src/ImageSharp/Formats/Webp/WebpFormat.cs @@ -13,7 +13,7 @@ public sealed class WebpFormat : IImageFormat } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static WebpFormat Instance { get; } = new(); diff --git a/src/ImageSharp/IO/BufferedReadStream.cs b/src/ImageSharp/IO/BufferedReadStream.cs index 291a31886..efa8f6f4b 100644 --- a/src/ImageSharp/IO/BufferedReadStream.cs +++ b/src/ImageSharp/IO/BufferedReadStream.cs @@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.IO; /// internal sealed class BufferedReadStream : Stream { + private readonly CancellationToken cancellationToken; + private readonly int maxBufferIndex; private readonly byte[] readBuffer; @@ -33,12 +35,15 @@ internal sealed class BufferedReadStream : Stream /// /// The configuration which allows altering default behaviour or extending the library. /// The input stream. - public BufferedReadStream(Configuration configuration, Stream stream) + /// The optional stream-level cancellation token to detect cancellation in synchronous methods. + public BufferedReadStream(Configuration configuration, Stream stream, CancellationToken cancellationToken = default) { Guard.NotNull(configuration, nameof(configuration)); Guard.IsTrue(stream.CanRead, nameof(stream), "Stream must be readable."); Guard.IsTrue(stream.CanSeek, nameof(stream), "Stream must be seekable."); + this.cancellationToken = cancellationToken; + // Ensure all underlying buffers have been flushed before we attempt to read the stream. // User streams may have opted to throw from Flush if CanWrite is false // (although the abstract Stream does not do so). @@ -85,6 +90,7 @@ internal sealed class BufferedReadStream : Stream set { Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.Position)); + this.cancellationToken.ThrowIfCancellationRequested(); // Only reset readBufferIndex if we are out of bounds of our working buffer // otherwise we should simply move the value by the diff. @@ -163,6 +169,8 @@ internal sealed class BufferedReadStream : Stream [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Read(Span buffer) { + this.cancellationToken.ThrowIfCancellationRequested(); + // Too big for our buffer. Read directly from the stream. int count = buffer.Length; if (count > this.BufferSize) @@ -255,6 +263,7 @@ internal sealed class BufferedReadStream : Stream [MethodImpl(MethodImplOptions.NoInlining)] private void FillReadBuffer() { + this.cancellationToken.ThrowIfCancellationRequested(); Stream baseStream = this.BaseStream; if (this.readerPosition != baseStream.Position) { diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index ea5bd5972..5f388d24f 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -45,6 +45,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 572856d99..806a0f471 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -114,12 +114,27 @@ 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 + { + 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); @@ -128,11 +143,26 @@ public abstract partial class Image return (null, null); } - Image img = decoder.Decode(options, stream, cancellationToken); + 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); 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) { IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); if (decoder is null) @@ -140,10 +170,25 @@ public abstract partial class Image 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. /// @@ -153,16 +198,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) { IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); - if (decoder is not IImageInfoDetector detector) + if (decoder is null) { return (null, null); } - IImageInfo info = detector?.Identify(options, stream, cancellationToken); + 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 eefb08776..33653434d 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) @@ -542,7 +538,6 @@ public abstract partial class Image return action(stream); } - // 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; @@ -563,7 +558,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)); @@ -575,33 +570,21 @@ public abstract partial class Image } Configuration configuration = options.Configuration; - if (stream.CanSeek && configuration.ReadOrigin == ReadOrigin.Begin) + if (stream.CanSeek) { - stream.Position = 0; + if (configuration.ReadOrigin == ReadOrigin.Begin) + { + stream.Position = 0; + } - // 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. + return await action(stream, cancellationToken).ConfigureAwait(false); } using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; - T Action(Stream ms, CancellationToken ct) - { - // 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) - { - stream.Position = ms.Position; - } - - return result; - } - - return Action(memoryStream, cancellationToken); + return await action(memoryStream, cancellationToken).ConfigureAwait(false); } [DoesNotReturn] diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 91c96b55f..2df86b109 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/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index fb2207c9e..7cd8a7b2f 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -48,10 +48,8 @@ public static partial class ImageExtensions { Guard.NotNull(path, nameof(path)); Guard.NotNull(encoder, nameof(encoder)); - using (Stream fs = source.GetConfiguration().FileSystem.Create(path)) - { - source.Save(fs, encoder); - } + using Stream fs = source.GetConfiguration().FileSystem.Create(path); + source.Save(fs, encoder); } /// @@ -73,10 +71,8 @@ public static partial class ImageExtensions Guard.NotNull(path, nameof(path)); Guard.NotNull(encoder, nameof(encoder)); - using (Stream fs = source.GetConfiguration().FileSystem.Create(path)) - { - await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false); - } + using Stream fs = source.GetConfiguration().FileSystem.Create(path); + await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false); } /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 53d602829..0dc6d26bc 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -16,7 +16,7 @@ public class DecodeJpeg private void GenericSetup(string imageSubpath) { - this.decoder = new JpegDecoder(); + this.decoder = JpegDecoder.Instance; byte[] bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, imageSubpath)); this.preloadedImageStream = new MemoryStream(bytes); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 3a3c81b52..aa88242ce 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -37,8 +37,7 @@ public class DecodeJpegParseStreamOnly { using var memoryStream = new MemoryStream(this.jpegBytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); - var options = new JpegDecoderOptions(); - options.GeneralOptions.SkipMetadata = true; + var options = new JpegDecoderOptions() { GeneralOptions = new() { SkipMetadata = true } }; using var decoder = new JpegDecoderCore(options); var spectralConverter = new NoopSpectralConverter(); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index 0b977bfbc..aed3e4c03 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -19,19 +19,12 @@ public class IdentifyJpeg public string TestImage { get; set; } [GlobalSetup] - public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + public void ReadImages() => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath); [Benchmark] public IImageInfo Identify() { - using var memoryStream = new MemoryStream(this.jpegBytes); - IImageDecoder decoder = new JpegDecoder(); - return decoder.Identify(DecoderOptions.Default, memoryStream, default); + using MemoryStream memoryStream = new(this.jpegBytes); + return JpegDecoder.Instance.Identify(DecoderOptions.Default, memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index ce2d1625f..dd9b55e58 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -208,8 +208,7 @@ public class LoadResizeSaveStressRunner TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) }; - var decoder = new JpegDecoder(); - using ImageSharpImage image = decoder.Decode(options, inputStream); + using ImageSharpImage image = JpegDecoder.Instance.Decode(options, inputStream); this.LogImageProcessed(image.Width, image.Height); // Reduce the size of the file diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index a3419eb27..3853c445f 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -3,6 +3,7 @@ using Microsoft.DotNet.RemoteExecutor; using Moq; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -86,7 +87,7 @@ public class ConfigurationTests [Fact] public void ConstructorCallConfigureOnFormatProvider() { - var provider = new Mock(); + var provider = new Mock(); var config = new Configuration(provider.Object); provider.Verify(x => x.Configure(config)); @@ -95,7 +96,7 @@ public class ConfigurationTests [Fact] public void AddFormatCallsConfig() { - var provider = new Mock(); + var provider = new Mock(); var config = new Configuration(); config.Configure(provider.Object); @@ -178,7 +179,7 @@ public class ConfigurationTests } } - private class MockConfigurationModule : IConfigurationModule + private class MockConfigurationModule : IImageFormatConfigurationModule { public void Configure(Configuration configuration) { diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 4bc59a1e1..e6eb389be 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -25,8 +25,6 @@ public class BmpDecoderTests public static readonly string[] BitfieldsBmpFiles = BitFields; - private static BmpDecoder BmpDecoder => new(); - public static readonly TheoryData RatioFiles = new() { @@ -40,7 +38,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_MiscellaneousBitmaps(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); if (TestEnvironment.IsWindows) @@ -61,7 +59,7 @@ public class BmpDecoderTests provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100); - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider, nonContiguousBuffersStr); if (TestEnvironment.IsWindows) @@ -81,7 +79,7 @@ public class BmpDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10); - InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(BmpDecoder)); + InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(BmpDecoder.Instance)); Assert.IsType(ex.InnerException); } @@ -90,7 +88,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBitfields(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -101,7 +99,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_Inverted(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -112,7 +110,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } @@ -123,7 +121,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_2Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Reference decoder cant decode 2-bit, compare to reference output instead. @@ -135,7 +133,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -145,7 +143,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -155,7 +153,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -165,7 +163,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -175,7 +173,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_32BitV4Header_Fast(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -190,7 +188,7 @@ public class BmpDecoderTests RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette; BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -203,7 +201,7 @@ public class BmpDecoderTests RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette; BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -217,7 +215,7 @@ public class BmpDecoderTests where TPixel : unmanaged, IPixel { BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); if (TestEnvironment.IsWindows) { @@ -232,7 +230,7 @@ public class BmpDecoderTests where TPixel : unmanaged, IPixel { BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -251,7 +249,7 @@ public class BmpDecoderTests } BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -272,7 +270,7 @@ public class BmpDecoderTests } BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black }; - using Image image = provider.GetImage(BmpDecoder, options); + using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); // Neither System.Drawing nor MagickReferenceDecoder decode this file. @@ -285,7 +283,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Neither System.Drawing nor MagickReferenceDecoder decode this file. @@ -298,7 +296,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -308,7 +306,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel @@ -325,7 +323,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Do not validate. Reference files will fail validation. @@ -337,7 +335,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -347,7 +345,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -358,7 +356,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); if (TestEnvironment.IsWindows) { @@ -372,7 +370,7 @@ public class BmpDecoderTests where TPixel : unmanaged, IPixel => Assert.Throws(() => { - using (provider.GetImage(BmpDecoder)) + using (provider.GetImage(BmpDecoder.Instance)) { } }); @@ -384,7 +382,7 @@ public class BmpDecoderTests where TPixel : unmanaged, IPixel => Assert.Throws(() => { - using (provider.GetImage(BmpDecoder)) + using (provider.GetImage(BmpDecoder.Instance)) { } }); @@ -394,7 +392,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -404,7 +402,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, new MagickReferenceDecoder()); } @@ -414,7 +412,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -425,7 +423,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -435,7 +433,7 @@ public class BmpDecoderTests public void BmpDecoder_RespectsFileHeaderOffset(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -445,7 +443,7 @@ public class BmpDecoderTests public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -455,7 +453,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode4BytePerEntryPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -507,8 +505,7 @@ public class BmpDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new BmpDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = BmpDecoder.Instance.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -520,7 +517,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. @@ -533,7 +530,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // System.Drawing can not decode this image. MagickReferenceDecoder can decode it, @@ -554,7 +551,7 @@ public class BmpDecoderTests public void BmpDecoder_CanDecode_Os2BitmapArray(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(BmpDecoder); + using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 8743e77bd..f486310b7 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp; [Trait("Format", "Bmp")] public class BmpEncoderTests { - private static BmpDecoder BmpDecoder => new(); - private static BmpEncoder BmpEncoder => new(); public static readonly TheoryData BitsPerPixel = @@ -200,7 +198,7 @@ public class BmpEncoderTests // arrange var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; using var memoryStream = new MemoryStream(); - using Image input = provider.GetImage(BmpDecoder); + using Image input = provider.GetImage(BmpDecoder.Instance); // act encoder.Encode(input, memoryStream); @@ -222,7 +220,7 @@ public class BmpEncoderTests // arrange var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; using var memoryStream = new MemoryStream(); - using Image input = provider.GetImage(BmpDecoder); + using Image input = provider.GetImage(BmpDecoder.Instance); // act encoder.Encode(input, memoryStream); @@ -280,7 +278,7 @@ public class BmpEncoderTests // We do not verify the reference image though as some are invalid. 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, @@ -311,7 +309,7 @@ public class BmpEncoderTests // We do not verify the reference image though as some are invalid. 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, @@ -331,7 +329,7 @@ public class BmpEncoderTests public void Encode_PreservesColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image input = provider.GetImage(new BmpDecoder(), new()); + using Image input = provider.GetImage(BmpDecoder.Instance, new()); ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; byte[] expectedProfileBytes = expectedProfile.ToByteArray(); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index adfd926b7..f4ce54670 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -51,7 +51,7 @@ public class BmpMetadataTests public void Decoder_CanReadColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new BmpDecoder())) + using (Image image = provider.GetImage(BmpDecoder.Instance)) { ImageSharp.Metadata.ImageMetadata metaData = image.Metadata; Assert.NotNull(metaData); diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 172821acc..c9e50337e 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Formats; @@ -47,7 +48,7 @@ public class GeneralFormatTests } [Fact] - public void ReadOriginIsRespectedOnLoad() + public void ChainedReadOriginIsRespectedForSeekableStreamsOnLoad() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using Image i = Image.Load(stream); @@ -62,7 +63,18 @@ public class GeneralFormatTests } [Fact] - public async Task ReadOriginIsRespectedOnLoadAsync() + public void ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException() + { + using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); + using NonSeekableStream wrapper = new(stream); + using Image i = Image.Load(wrapper); + + Assert.Equal(stream.Length, stream.Position); + Assert.Throws(() => { using Image j = Image.Load(wrapper); }); + } + + [Fact] + public async Task ChainedReadOriginIsRespectedForSeekableStreamsOnLoadAsync() { using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); using Image i = await Image.LoadAsync(stream); @@ -76,6 +88,17 @@ public class GeneralFormatTests Assert.NotEqual(i[5, 5], j[5, 5]); } + [Fact] + public async Task ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException_Async() + { + using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259)); + using NonSeekableStream wrapper = new(stream); + using Image i = await Image.LoadAsync(wrapper); + + Assert.Equal(stream.Length, stream.Position); + await Assert.ThrowsAsync(async () => { using Image j = await Image.LoadAsync(wrapper); }); + } + [Fact] public void ImageCanEncodeToString() { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 2a9d2b791..9ddae6645 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -18,8 +18,6 @@ public class GifDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; - private static GifDecoder GifDecoder => new(); - public static readonly string[] MultiFrameTestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Kumin @@ -46,7 +44,7 @@ public class GifDecoderTests MaxFrames = 1 }; - using Image image = provider.GetImage(GifDecoder, options); + using Image image = provider.GetImage(GifDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -68,7 +66,7 @@ public class GifDecoderTests fixed (byte* data = testFile.Bytes.AsSpan(0, length)) { using var stream = new UnmanagedMemoryStream(data, length); - using Image image = GifDecoder.Decode(DecoderOptions.Default, stream); + using Image image = GifDecoder.Instance.Decode(DecoderOptions.Default, stream); Assert.Equal((200, 200), (image.Width, image.Height)); } } @@ -102,7 +100,7 @@ public class GifDecoderTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { MaxFrames = 1 }; - using Image image = provider.GetImage(new GifDecoder(), options); + using Image image = provider.GetImage(GifDecoder.Instance, options); Assert.Equal(1, image.Frames.Count); } @@ -111,7 +109,7 @@ public class GifDecoderTests public void CanDecodeAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(new GifDecoder()); + using Image image = provider.GetImage(GifDecoder.Instance); Assert.True(image.Frames.Count > 1); } @@ -137,7 +135,7 @@ public class GifDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(GifDecoder); + using Image image = provider.GetImage(GifDecoder.Instance); }); Assert.NotNull(ex); Assert.Contains("Width or height should not be 0", ex.Message); @@ -149,7 +147,7 @@ public class GifDecoderTests public void Decode_WithMaxDimensions_Works(TestImageProvider provider, int expectedWidth, int expectedHeight) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(GifDecoder); + using Image image = provider.GetImage(GifDecoder.Instance); Assert.Equal(expectedWidth, image.Width); Assert.Equal(expectedHeight, image.Height); } @@ -216,7 +214,7 @@ public class GifDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10); - InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(GifDecoder)); + InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(GifDecoder.Instance)); Assert.IsType(ex.InnerException); } @@ -233,7 +231,7 @@ public class GifDecoderTests provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100); - using Image image = provider.GetImage(GifDecoder); + using Image image = provider.GetImage(GifDecoder.Instance); image.DebugSave(provider, nonContiguousBuffersStr); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 2d66842e8..24eaa9607 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -56,7 +56,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(TestImages.Gif.Rings); - using Image image = testFile.CreateRgba32Image(new GifDecoder()); + using Image image = testFile.CreateRgba32Image(GifDecoder.Instance); GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(1, metadata.Comments.Count); Assert.Equal("ImageSharp", metadata.Comments[0]); @@ -72,7 +72,7 @@ public class GifMetadataTests var testFile = TestFile.Create(TestImages.Gif.Rings); - using Image image = testFile.CreateRgba32Image(new GifDecoder(), options); + using Image image = testFile.CreateRgba32Image(GifDecoder.Instance, options); GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(0, metadata.Comments.Count); } @@ -82,7 +82,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(TestImages.Gif.LargeComment); - using Image image = testFile.CreateRgba32Image(new GifDecoder()); + using Image image = testFile.CreateRgba32Image(GifDecoder.Instance); GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(2, metadata.Comments.Count); Assert.Equal(new string('c', 349), metadata.Comments[0]); @@ -92,7 +92,7 @@ public class GifMetadataTests [Fact] public void Encode_PreservesTextData() { - var decoder = new GifDecoder(); + var decoder = GifDecoder.Instance; var testFile = TestFile.Create(TestImages.Gif.LargeComment); using Image input = testFile.CreateRgba32Image(decoder); @@ -113,8 +113,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new GifDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -127,8 +126,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 GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -141,8 +139,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new GifDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = GifDecoder.Instance.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -155,8 +152,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 GifDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -169,8 +165,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new GifDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream); GifMetadata meta = image.Metadata.GetGifMetadata(); Assert.Equal(repeatCount, meta.RepeatCount); } @@ -181,8 +176,7 @@ public class GifMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new GifDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = GifDecoder.Instance.Decode(DecoderOptions.Default, stream); GifMetadata meta = image.Metadata.GetGifMetadata(); Assert.Equal(repeatCount, meta.RepeatCount); } diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index 06fe2601c..d276f7eb6 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -102,8 +102,8 @@ public class ImageFormatManagerTests [Fact] public void AddFormatCallsConfig() { - var provider = new Mock(); - var config = new Configuration(); + Mock provider = new(); + Configuration config = new(); config.Configure(provider.Object); provider.Verify(x => x.Configure(config)); @@ -113,9 +113,9 @@ public class ImageFormatManagerTests public void DetectFormatAllocatesCleanBuffer() { byte[] jpegImage; - using (var buffer = new MemoryStream()) + using (MemoryStream buffer = new()) { - using var image = new Image(100, 100); + using Image image = new(100, 100); image.SaveAsJpeg(buffer); jpegImage = buffer.ToArray(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index f8a562c4c..f147e4132 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -28,7 +29,7 @@ public partial class JpegDecoderTests provider.LimitAllocatorBufferCapacity().InPixels(16_000); } - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr); provider.Utility.TestName = DecodeBaselineJpegOutputName; @@ -57,7 +58,7 @@ public partial class JpegDecoderTests public void DecodeJpeg_WithArithmeticCoding(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Tolerant(0.002f), ReferenceDecoder); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 3603000f6..d972f539e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -65,7 +65,7 @@ public partial class JpegDecoderTests bool exifProfilePresent, bool iccProfilePresent) => TestMetadataImpl( useIdentify, - JpegDecoder, + JpegDecoder.Instance, imagePath, expectedPixelSize, exifProfilePresent, @@ -77,8 +77,7 @@ public partial class JpegDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new JpegDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -91,8 +90,7 @@ public partial class JpegDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new JpegDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -105,8 +103,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 JpegDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -119,8 +116,7 @@ public partial class JpegDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new JpegDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } @@ -131,7 +127,7 @@ public partial class JpegDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream); + using Image image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } @@ -142,7 +138,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.Instance.DecodeAsync(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } @@ -159,7 +155,7 @@ public partial class JpegDecoderTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(expectedColorType, meta.ColorType); } @@ -173,7 +169,7 @@ public partial class JpegDecoderTests public void Decode_DetectsCorrectColorType(TestImageProvider provider, JpegEncodingColor expectedColorType) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(expectedColorType, meta.ColorType); } @@ -184,12 +180,12 @@ public partial class JpegDecoderTests 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); } } @@ -253,9 +249,9 @@ public partial class JpegDecoderTests DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; // Snake.jpg has both Exif and ICC profiles defined: - var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); - using Image image = testFile.CreateRgba32Image(JpegDecoder, options); + using Image image = testFile.CreateRgba32Image(JpegDecoder.Instance, options); if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); @@ -273,7 +269,7 @@ public partial class JpegDecoderTests [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) => TestImageInfo( TestImages.Jpeg.Baseline.Floorplan, - JpegDecoder, + JpegDecoder.Instance, useIdentify, imageInfo => { @@ -286,7 +282,7 @@ public partial class JpegDecoderTests [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) => TestImageInfo( TestImages.Jpeg.Baseline.Jpeg420Exif, - JpegDecoder, + JpegDecoder.Instance, useIdentify, imageInfo => { @@ -301,7 +297,7 @@ public partial class JpegDecoderTests { Exception ex = Record.Exception(() => { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); }); Assert.Null(ex); } @@ -313,7 +309,7 @@ public partial class JpegDecoderTests { Exception ex = Record.Exception(() => { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); ExifProfile clone = image.Metadata.ExifProfile.DeepClone(); }); Assert.Null(ex); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index 8e07d9ac9..a5472e1ae 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using Microsoft.DotNet.RemoteExecutor; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -19,7 +20,7 @@ public partial class JpegDecoderTests public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); provider.Utility.TestName = DecodeProgressiveJpegOutputName; @@ -35,7 +36,7 @@ public partial class JpegDecoderTests public void DecodeProgressiveJpeg_WithArithmeticCoding(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Tolerant(0.004f), ReferenceDecoder); } @@ -51,7 +52,7 @@ public partial class JpegDecoderTests provider.LimitAllocatorBufferCapacity().InBytesSqrt(200); - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider, nonContiguousBuffersStr); provider.Utility.TestName = DecodeProgressiveJpegOutputName; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index deca7f69f..18b4e1fba 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -8,7 +8,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; -using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit.Abstractions; @@ -64,8 +63,6 @@ public partial class JpegDecoderTests private ITestOutputHelper Output { get; } - private static JpegDecoder JpegDecoder => new(); - [Fact] public void ParseStream_BasicPropertiesAreCorrect() { @@ -105,7 +102,7 @@ public partial class JpegDecoderTests public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); provider.Utility.TestName = DecodeBaselineJpegOutputName; @@ -121,7 +118,7 @@ public partial class JpegDecoderTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(JpegDecoder, options); + using Image image = provider.GetImage(JpegDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -143,7 +140,7 @@ public partial class JpegDecoderTests TargetSize = new() { Width = 150, Height = 150 }, Sampler = KnownResamplers.Bicubic }; - using Image image = provider.GetImage(JpegDecoder, options); + using Image image = provider.GetImage(JpegDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -167,7 +164,7 @@ public partial class JpegDecoderTests ResizeMode = JpegDecoderResizeMode.IdctOnly }; - using Image image = provider.GetImage(JpegDecoder, specializedOptions); + using Image image = provider.GetImage(JpegDecoder.Instance, specializedOptions); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -191,7 +188,7 @@ public partial class JpegDecoderTests ResizeMode = JpegDecoderResizeMode.ScaleOnly }; - using Image image = provider.GetImage(JpegDecoder, specializedOptions); + using Image image = provider.GetImage(JpegDecoder.Instance, specializedOptions); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -215,7 +212,7 @@ public partial class JpegDecoderTests ResizeMode = JpegDecoderResizeMode.Combined }; - using Image image = provider.GetImage(JpegDecoder, specializedOptions); + using Image image = provider.GetImage(JpegDecoder.Instance, specializedOptions); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -234,7 +231,7 @@ public partial class JpegDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InBytesSqrt(10); - InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(JpegDecoder)); + InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(JpegDecoder.Instance)); this.Output.WriteLine(ex.Message); Assert.IsType(ex.InnerException); } @@ -246,66 +243,18 @@ public partial class JpegDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InBytesSqrt(10); - InvalidImageContentException ex = await Assert.ThrowsAsync(() => provider.GetImageAsync(JpegDecoder)); + InvalidImageContentException ex = await Assert.ThrowsAsync(() => provider.GetImageAsync(JpegDecoder.Instance)); this.Output.WriteLine(ex.Message); Assert.IsType(ex.InnerException); } - [Fact] - public async Task DecodeAsync_IsCancellable() - { - var cts = new CancellationTokenSource(); - string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); - using var pausedStream = new PausedStream(file); - pausedStream.OnWaiting(_ => - { - cts.Cancel(); - pausedStream.Release(); - }); - - var configuration = Configuration.CreateDefaultInstance(); - configuration.FileSystem = new SingleStreamFileSystem(pausedStream); - DecoderOptions options = new() - { - Configuration = configuration - }; - - await Assert.ThrowsAsync(async () => - { - using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token); - }); - } - - [Fact] - public async Task Identify_IsCancellable() - { - var cts = new CancellationTokenSource(); - - string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); - using var pausedStream = new PausedStream(file); - pausedStream.OnWaiting(_ => - { - cts.Cancel(); - pausedStream.Release(); - }); - - var configuration = Configuration.CreateDefaultInstance(); - configuration.FileSystem = new SingleStreamFileSystem(pausedStream); - DecoderOptions options = new() - { - Configuration = configuration - }; - - await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(options, "someFakeFile", cts.Token)); - } - [Theory] [WithFileCollection(nameof(UnsupportedTestJpegs), PixelTypes.Rgba32)] public void ThrowsNotSupported_WithUnsupportedJpegs(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); }); // https://github.com/SixLabors/ImageSharp/pull/1732 @@ -314,7 +263,7 @@ public partial class JpegDecoderTests public void Issue1732_DecodesWithRgbColorSpace(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -325,7 +274,7 @@ public partial class JpegDecoderTests public void Issue2057_DecodeWorks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -336,7 +285,7 @@ public partial class JpegDecoderTests public void Issue2133_DeduceColorSpace(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -347,7 +296,7 @@ public partial class JpegDecoderTests public void Issue2136_DecodeWorks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 7e8f13ed3..2b721b9b5 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -100,10 +100,11 @@ public partial class JpegEncoderTests Exception ex = Record.Exception(() => { var encoder = new JpegEncoder(); - var stream = new MemoryStream(); - - using Image image = provider.GetImage(JpegDecoder); - image.Save(stream, encoder); + using (var stream = new MemoryStream()) + { + using Image image = provider.GetImage(JpegDecoder.Instance); + image.Save(stream, encoder); + } }); Assert.Null(ex); @@ -162,7 +163,7 @@ public partial class JpegEncoderTests where TPixel : unmanaged, IPixel { // arrange - using Image input = provider.GetImage(JpegDecoder); + using Image input = provider.GetImage(JpegDecoder.Instance); using var memoryStream = new MemoryStream(); // act diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 0e92be428..61c01b15c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -13,8 +13,6 @@ public partial class JpegEncoderTests { private static JpegEncoder JpegEncoder => new(); - private static JpegDecoder JpegDecoder => new(); - private static readonly TheoryData TestQualities = new() { 40, diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs index 2e00ea8eb..5bdab7b37 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs @@ -108,7 +108,7 @@ public class PbmDecoderTests TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(new PbmDecoder(), options); + using Image image = provider.GetImage(PbmDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 5a23c6037..aff8bc12a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -70,10 +70,8 @@ public partial class PngDecoderTests WriteChunk(memStream, chunkName); WriteDataChunk(memStream); - var decoder = new PngDecoder(); - ImageFormatException exception = - Assert.Throws(() => decoder.Decode(DecoderOptions.Default, memStream)); + Assert.Throws(() => PngDecoder.Instance.Decode(DecoderOptions.Default, memStream)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index c83b97a88..2e1785cbb 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -19,8 +19,6 @@ public partial class PngDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; - private static PngDecoder PngDecoder => new(); - public static readonly string[] CommonTestImages = { TestImages.Png.Splash, @@ -102,7 +100,7 @@ public partial class PngDecoderTests public void Decode(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -117,7 +115,7 @@ public partial class PngDecoderTests TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(PngDecoder, options); + using Image image = provider.GetImage(PngDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -135,7 +133,7 @@ public partial class PngDecoderTests public void Decode_WithAverageFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -146,7 +144,7 @@ public partial class PngDecoderTests public void Decode_WithSubFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -156,7 +154,7 @@ public partial class PngDecoderTests public void Decode_WithUpFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -167,7 +165,7 @@ public partial class PngDecoderTests public void Decode_WithPaethFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -178,7 +176,7 @@ public partial class PngDecoderTests public void Decode_GrayWithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -190,7 +188,7 @@ public partial class PngDecoderTests public void Decode_Interlaced(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -205,7 +203,7 @@ public partial class PngDecoderTests public void Decode_Indexed(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -216,7 +214,7 @@ public partial class PngDecoderTests public void Decode_48Bpp(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -227,7 +225,7 @@ public partial class PngDecoderTests public void Decode_64Bpp(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -240,7 +238,7 @@ public partial class PngDecoderTests public void Decoder_L8bitInterlaced(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -250,7 +248,7 @@ public partial class PngDecoderTests public void Decode_L16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -261,7 +259,7 @@ public partial class PngDecoderTests public void Decode_GrayAlpha16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -271,7 +269,7 @@ public partial class PngDecoderTests public void Decoder_CanDecode_Grey8bitInterlaced_WithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -281,7 +279,7 @@ public partial class PngDecoderTests public void Decoder_CanDecode_CorruptedImages(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -291,7 +289,7 @@ public partial class PngDecoderTests public void Decoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); } @@ -319,7 +317,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -335,7 +333,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -350,7 +348,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.Null(ex); @@ -365,7 +363,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -381,7 +379,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -396,7 +394,7 @@ public partial class PngDecoderTests InvalidImageContentException ex = Assert.Throws( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); }); Assert.NotNull(ex); Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message); @@ -411,7 +409,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); }); @@ -427,7 +425,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); }); @@ -443,7 +441,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); }); @@ -459,7 +457,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); // We don't have another x-plat reference decoder that can be compared for this image. @@ -480,7 +478,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ImageComparer.Exact); }); @@ -493,7 +491,7 @@ public partial class PngDecoderTests public void Issue2209_Decode_HasTransparencyIsTrue(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); PngMetadata metadata = image.Metadata.GetPngMetadata(); Assert.True(metadata.HasTransparency); @@ -520,7 +518,7 @@ public partial class PngDecoderTests Exception ex = Record.Exception( () => { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); // We don't have another x-plat reference decoder that can be compared for this image. @@ -540,7 +538,7 @@ public partial class PngDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10); - InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(PngDecoder)); + InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(PngDecoder.Instance)); Assert.IsType(ex.InnerException); } @@ -555,7 +553,7 @@ public partial class PngDecoderTests provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100); - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 2656144d3..74038c761 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -284,9 +284,7 @@ public partial class PngEncoderTests stream.Seek(0, SeekOrigin.Begin); - var decoder = new PngDecoder(); - - Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream); PngMetadata metadata = image.Metadata.GetPngMetadata(); Assert.Equal(pngColorType, metadata.ColorType); @@ -542,7 +540,7 @@ public partial class PngEncoderTests // https://github.com/SixLabors/ImageSharp/issues/935 using var ms = new MemoryStream(); var testFile = TestFile.Create(TestImages.Png.Issue935); - using Image image = testFile.CreateRgba32Image(new PngDecoder()); + using Image image = testFile.CreateRgba32Image(PngDecoder.Instance); image.Save(ms, new PngEncoder { ColorType = PngColorType.RgbWithAlpha }); } @@ -594,11 +592,11 @@ public partial class PngEncoderTests // occurs within the encoder itself leaving the input image unaffected. // This means we are benefiting from testing our decoder also. using FileStream fileStream = File.OpenRead(actualOutputFile); - using Image imageSharpImage = new PngDecoder().Decode(DecoderOptions.Default, fileStream); + using Image imageSharpImage = PngDecoder.Instance.Decode(DecoderOptions.Default, fileStream); 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/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index a0d2423aa..d283efc00 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -52,7 +52,7 @@ public class PngMetadataTests public void Decoder_CanReadTextData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(new PngDecoder()); + using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); VerifyTextDataIsPresent(meta); } @@ -62,13 +62,12 @@ public class PngMetadataTests public void Encoder_PreservesTextData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var decoder = new PngDecoder(); - using Image input = provider.GetImage(decoder); + using Image input = provider.GetImage(PngDecoder.Instance); using var memoryStream = new MemoryStream(); input.Save(memoryStream, new PngEncoder()); memoryStream.Position = 0; - using Image image = decoder.Decode(DecoderOptions.Default, memoryStream); + using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, memoryStream); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); VerifyTextDataIsPresent(meta); } @@ -78,7 +77,7 @@ public class PngMetadataTests public void Decoder_IgnoresInvalidTextData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(new PngDecoder()); + using Image image = provider.GetImage(PngDecoder.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.DoesNotContain(meta.TextData, m => m.Value is "leading space"); Assert.DoesNotContain(meta.TextData, m => m.Value is "trailing space"); @@ -93,8 +92,7 @@ public class PngMetadataTests public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var decoder = new PngDecoder(); - using Image input = provider.GetImage(decoder); + using Image input = provider.GetImage(PngDecoder.Instance); using var memoryStream = new MemoryStream(); // This will be a zTXt chunk. @@ -111,7 +109,7 @@ public class PngMetadataTests }); memoryStream.Position = 0; - using Image image = decoder.Decode(DecoderOptions.Default, memoryStream); + using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, memoryStream); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.Contains(meta.TextData, m => m.Equals(expectedText)); Assert.Contains(meta.TextData, m => m.Equals(expectedTextNoneLatin)); @@ -127,7 +125,7 @@ public class PngMetadataTests SkipMetadata = false }; - using Image image = provider.GetImage(new PngDecoder(), options); + using Image image = provider.GetImage(PngDecoder.Instance, options); Assert.NotNull(image.Metadata.ExifProfile); ExifProfile exif = image.Metadata.ExifProfile; VerifyExifDataIsPresent(exif); @@ -143,9 +141,7 @@ public class PngMetadataTests SkipMetadata = true }; - PngDecoder decoder = new(); - - using Image image = provider.GetImage(decoder, options); + using Image image = provider.GetImage(PngDecoder.Instance, options); Assert.Null(image.Metadata.ExifProfile); } @@ -159,7 +155,7 @@ public class PngMetadataTests var testFile = TestFile.Create(TestImages.Png.Blur); - using Image image = testFile.CreateRgba32Image(new PngDecoder(), options); + using Image image = testFile.CreateRgba32Image(PngDecoder.Instance, options); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.Equal(1, meta.TextData.Count); @@ -178,7 +174,7 @@ public class PngMetadataTests var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); - using Image image = testFile.CreateRgba32Image(new PngDecoder(), options); + using Image image = testFile.CreateRgba32Image(PngDecoder.Instance, options); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.Equal(0, meta.TextData.Count); } @@ -189,8 +185,7 @@ public class PngMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new PngDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream); + using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -202,7 +197,7 @@ public class PngMetadataTests public void Encode_PreservesColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image input = provider.GetImage(new PngDecoder()); + using Image input = provider.GetImage(PngDecoder.Instance); ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; byte[] expectedProfileBytes = expectedProfile.ToByteArray(); @@ -224,8 +219,7 @@ public class PngMetadataTests { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - var decoder = new PngDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + IImageInfo image = PngDecoder.Instance.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 8bc64362d..a4288a3d8 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -24,7 +24,7 @@ public class PngSmokeTests // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.Save(ms, new PngEncoder()); ms.Position = 0; - using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms); + using Image img2 = PngDecoder.Instance.Decode(DecoderOptions.Default, ms); ImageComparer.Tolerant().VerifySimilarity(image, img2); // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); @@ -45,7 +45,7 @@ public class PngSmokeTests // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); image.Save(ms, new PngEncoder()); ms.Position = 0; - using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms); + using Image img2 = PngDecoder.Instance.Decode(DecoderOptions.Default, ms); ImageComparer.Tolerant().VerifySimilarity(image, img2); } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index b96e284b9..3c9a2f826 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -17,14 +17,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga; [ValidateDisposedMemoryAllocations] public class TgaDecoderTests { - private static TgaDecoder TgaDecoder => new(); - [Theory] [WithFile(Gray8BitTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -36,7 +34,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -48,7 +46,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -60,7 +58,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -72,7 +70,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -84,7 +82,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -96,7 +94,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -108,7 +106,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -120,7 +118,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -135,7 +133,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -150,7 +148,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -165,7 +163,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -180,7 +178,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -195,7 +193,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -210,7 +208,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -225,7 +223,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); @@ -240,7 +238,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_15Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -252,7 +250,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_15Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -264,7 +262,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -276,7 +274,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithPalette_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -288,7 +286,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -300,7 +298,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -312,7 +310,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -324,7 +322,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithBottomRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -336,7 +334,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -348,7 +346,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -360,7 +358,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -372,7 +370,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_Palette_WithTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -384,7 +382,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithTopLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -396,7 +394,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -408,7 +406,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -420,7 +418,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithBottomRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -432,7 +430,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -444,7 +442,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -456,7 +454,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -468,7 +466,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -480,7 +478,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -492,7 +490,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -504,7 +502,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_Paletted_WithTopLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -516,7 +514,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -528,7 +526,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -540,7 +538,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -552,7 +550,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -564,7 +562,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPaletteTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -576,7 +574,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPaletteTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -588,7 +586,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -600,7 +598,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPaletteBottomRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -612,7 +610,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_WithPaletteTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -624,7 +622,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_WithPaletteTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -636,7 +634,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_WithPaletteBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -648,7 +646,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_RLE_WithPaletteBottomRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -660,7 +658,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -672,7 +670,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPalette_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -684,7 +682,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPalette_WithBottomRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -696,7 +694,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WithPalette_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -709,7 +707,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -722,7 +720,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { // Using here the reference output instead of the the reference decoder, // because the reference decoder does not ignore the alpha data here. @@ -738,7 +736,7 @@ public class TgaDecoderTests public void TgaDecoder_CanDecode_LegacyFormat(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TgaDecoder)) + using (Image image = provider.GetImage(TgaDecoder.Instance)) { image.DebugSave(provider); ImageComparingUtils.CompareWithReferenceDecoder(provider, image); @@ -755,7 +753,7 @@ public class TgaDecoderTests TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(TgaDecoder, options); + using Image image = provider.GetImage(TgaDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -775,7 +773,7 @@ public class TgaDecoderTests where TPixel : unmanaged, IPixel { provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10); - InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(TgaDecoder)); + InvalidImageContentException ex = Assert.Throws(() => provider.GetImage(TgaDecoder.Instance)); Assert.IsType(ex.InnerException); } @@ -790,7 +788,7 @@ public class TgaDecoderTests provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100); - using Image image = provider.GetImage(TgaDecoder); + using Image image = provider.GetImage(TgaDecoder.Instance); image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr); if (TestEnvironment.IsWindows) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 62ca6da3d..29017a674 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -39,7 +39,7 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester { Assert.Throws(() => TestTiffDecoder(provider)); - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); ExifProfile exif = image.Frames.RootFrame.Metadata.ExifProfile; // PhotometricInterpretation is required tag: https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html @@ -104,7 +104,7 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_SubIfd8(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); ExifProfile meta = image.Frames.RootFrame.Metadata.ExifProfile; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs index 95f37ba40..4acdf3e50 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs @@ -12,14 +12,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff; public abstract class TiffDecoderBaseTester { - protected static TiffDecoder TiffDecoder => new(); - protected static MagickReferenceDecoder ReferenceDecoder => new(); 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); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal( provider, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ba5b77baf..ced8ed6dd 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -21,7 +21,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) - where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder.Instance)); [Theory] [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] @@ -201,7 +201,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -258,7 +258,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -285,7 +285,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -387,7 +387,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -427,7 +427,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -466,7 +466,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -496,7 +496,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester if (TestEnvironment.IsMacOS) { // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); return; } @@ -667,7 +667,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester where TPixel : unmanaged, IPixel { DecoderOptions options = new() { MaxFrames = 1 }; - using Image image = provider.GetImage(new TiffDecoder(), options); + using Image image = provider.GetImage(TiffDecoder.Instance, options); Assert.Equal(1, image.Frames.Count); } @@ -696,7 +696,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester { MaxFrames = 1 }; - using Image image = provider.GetImage(TiffDecoder, decoderOptions); + using Image image = provider.GetImage(TiffDecoder.Instance, decoderOptions); image.DebugSave(provider); image.CompareToOriginal( provider, @@ -723,7 +723,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester where TPixel : unmanaged, IPixel => Assert.Throws( () => { - using (provider.GetImage(TiffDecoder)) + using (provider.GetImage(TiffDecoder.Instance)) { } }); @@ -739,7 +739,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void DecodeMultiframe(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); Assert.True(image.Frames.Count > 1); image.DebugSave(provider); @@ -759,7 +759,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(TiffDecoder, options); + using Image image = provider.GetImage(TiffDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 2c30cc3d0..97445ad6c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -295,7 +295,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester [Theory] [WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)] public void TiffEncoder_EncodePlanar_AndReload_Works(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, imageDecoder: new TiffDecoder()); + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, imageDecoder: TiffDecoder.Instance); [Theory] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index ffe4c573b..f882bf415 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff; [Trait("Format", "Tiff")] public class TiffMetadataTests { - private static TiffDecoder TiffDecoder => new(); - private class NumberComparer : IEqualityComparer { public bool Equals(Number x, Number y) => x.Equals(y); @@ -46,7 +44,7 @@ public class TiffMetadataTests public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); @@ -113,7 +111,7 @@ public class TiffMetadataTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; - using Image image = provider.GetImage(new TiffDecoder(), options); + using Image image = provider.GetImage(TiffDecoder.Instance, options); TiffMetadata meta = image.Metadata.GetTiffMetadata(); ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; @@ -137,7 +135,7 @@ public class TiffMetadataTests public void CanDecodeImage_WithIptcDataAsLong(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); IptcProfile iptcProfile = image.Frames.RootFrame.Metadata.IptcProfile; Assert.NotNull(iptcProfile); @@ -151,7 +149,7 @@ public class TiffMetadataTests public void BaselineTags(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); ImageFrame rootFrame = image.Frames.RootFrame; Assert.Equal(32, rootFrame.Width); Assert.Equal(32, rootFrame.Height); @@ -208,7 +206,7 @@ public class TiffMetadataTests public void SubfileType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(TiffDecoder); + using Image image = provider.GetImage(TiffDecoder.Instance); TiffMetadata meta = image.Metadata.GetTiffMetadata(); Assert.NotNull(meta); @@ -232,7 +230,7 @@ public class TiffMetadataTests { // Load Tiff image DecoderOptions options = new() { SkipMetadata = false }; - using Image image = provider.GetImage(TiffDecoder, options); + using Image image = provider.GetImage(TiffDecoder.Instance, options); ImageMetadata inputMetaData = image.Metadata; ImageFrame rootFrameInput = image.Frames.RootFrame; diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 55c623365..694b8c23e 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [ValidateDisposedMemoryAllocations] public class WebpDecoderTests { - private static WebpDecoder WebpDecoder => new(); - private static MagickReferenceDecoder ReferenceDecoder => new(); private static string TestImageLossyHorizontalFilterPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.AlphaCompressedHorizontalFilter); @@ -63,7 +61,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithoutFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -77,7 +75,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithSimpleFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -98,7 +96,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithComplexFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -111,7 +109,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_VerySmall(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -128,7 +126,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithPartitions(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -140,7 +138,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithSegmentation(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -155,7 +153,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -178,7 +176,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossy_WithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -188,7 +186,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -199,7 +197,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithoutTransforms(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -216,7 +214,7 @@ public class WebpDecoderTests TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -230,7 +228,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -241,7 +239,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithPredictorTransform(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -252,7 +250,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -274,7 +272,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithTwoTransforms(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -291,7 +289,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -301,7 +299,7 @@ public class WebpDecoderTests public void Decode_AnimatedLossless_VerifyAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); @@ -318,7 +316,7 @@ public class WebpDecoderTests public void Decode_AnimatedLossy_VerifyAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); @@ -336,7 +334,7 @@ public class WebpDecoderTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { MaxFrames = 1 }; - using Image image = provider.GetImage(new WebpDecoder(), options); + using Image image = provider.GetImage(WebpDecoder.Instance, options); Assert.Equal(1, image.Frames.Count); } @@ -348,7 +346,7 @@ public class WebpDecoderTests where TPixel : unmanaged, IPixel { // Just make sure no exception is thrown. The reference decoder fails to load the image. - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); } @@ -362,7 +360,7 @@ public class WebpDecoderTests TargetSize = new() { Width = 150, Height = 150 } }; - using Image image = provider.GetImage(WebpDecoder, options); + using Image image = provider.GetImage(WebpDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -380,7 +378,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Issue1594(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -391,7 +389,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Issue2243(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -402,7 +400,7 @@ public class WebpDecoderTests public void WebpDecoder_CanDecode_Issue2257(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -414,7 +412,7 @@ public class WebpDecoderTests Assert.Throws( () => { - using (provider.GetImage(WebpDecoder)) + using (provider.GetImage(WebpDecoder.Instance)) { } }); @@ -422,7 +420,7 @@ public class WebpDecoderTests private static void RunDecodeLossyWithHorizontalFilter() { var provider = TestImageProvider.File(TestImageLossyHorizontalFilterPath); - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -430,7 +428,7 @@ public class WebpDecoderTests private static void RunDecodeLossyWithVerticalFilter() { var provider = TestImageProvider.File(TestImageLossyVerticalFilterPath); - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -438,7 +436,7 @@ public class WebpDecoderTests private static void RunDecodeLossyWithSimpleFilterTest() { var provider = TestImageProvider.File(TestImageLossySimpleFilterPath); - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } @@ -446,7 +444,7 @@ public class WebpDecoderTests private static void RunDecodeLossyWithComplexFilterTest() { var provider = TestImageProvider.File(TestImageLossyComplexFilterPath); - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs index 566b7e4dd..c0c2e375f 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs @@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [Trait("Format", "Webp")] public class WebpMetaDataTests { - private static WebpDecoder WebpDecoder => new(); - [Theory] [WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, false)] [WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, true)] @@ -21,7 +19,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; - using Image image = provider.GetImage(WebpDecoder, options); + using Image image = provider.GetImage(WebpDecoder.Instance, options); if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); @@ -42,7 +40,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; - using Image image = provider.GetImage(WebpDecoder, options); + using Image image = provider.GetImage(WebpDecoder.Instance, options); if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); @@ -67,7 +65,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; - using Image image = provider.GetImage(WebpDecoder, options); + using Image image = provider.GetImage(WebpDecoder.Instance, options); if (ignoreMetadata) { Assert.Null(image.Metadata.IccProfile); @@ -86,7 +84,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; - using Image image = await provider.GetImageAsync(WebpDecoder, options); + using Image image = await provider.GetImageAsync(WebpDecoder.Instance, options); if (ignoreMetadata) { Assert.Null(image.Metadata.XmpProfile); @@ -129,7 +127,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { // arrange - using Image input = provider.GetImage(WebpDecoder); + using Image input = provider.GetImage(WebpDecoder.Instance); using var memoryStream = new MemoryStream(); ExifProfile expectedExif = input.Metadata.ExifProfile; @@ -150,7 +148,7 @@ public class WebpMetaDataTests where TPixel : unmanaged, IPixel { // arrange - using Image input = provider.GetImage(WebpDecoder); + using Image input = provider.GetImage(WebpDecoder.Instance); using var memoryStream = new MemoryStream(); ExifProfile expectedExif = input.Metadata.ExifProfile; @@ -171,7 +169,7 @@ public class WebpMetaDataTests public void Encode_PreservesColorProfile(TestImageProvider provider, WebpFileFormatType fileFormat) where TPixel : unmanaged, IPixel { - using Image input = provider.GetImage(WebpDecoder); + using Image input = provider.GetImage(WebpDecoder.Instance); ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; byte[] expectedProfileBytes = expectedProfile.ToByteArray(); diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs index d28b2ee40..258ee5b9f 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs @@ -14,8 +14,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [Trait("Format", "Webp")] public class YuvConversionTests { - private static WebpDecoder WebpDecoder => new(); - private static MagickReferenceDecoder ReferenceDecoder => new(); private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Webp.Lossy.NoFilter06); @@ -23,7 +21,7 @@ public class YuvConversionTests public static void RunUpSampleYuvToRgbTest() { var provider = TestImageProvider.File(TestImageLossyFullPath); - using Image image = provider.GetImage(WebpDecoder); + using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs index 14d65fbb9..d8d9f4fe2 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs @@ -2,7 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests; @@ -10,153 +10,89 @@ public partial class ImageTests { public class Decode_Cancellation : ImageLoadTestBase { - private bool isTestStreamSeekable; - private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new(0); - private readonly SemaphoreSlim continueSemaphore = new(0); - private readonly CancellationTokenSource cts = new(); - public Decode_Cancellation() => this.TopLevelConfiguration.StreamProcessingBufferSize = 128; - [Theory] - [InlineData(false)] - [InlineData(true)] - public Task LoadAsync_Specific_Stream(bool isInputStreamSeekable) + public static readonly string[] TestFileForEachCodec = new[] { - this.isTestStreamSeekable = isInputStreamSeekable; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); + TestImages.Jpeg.Baseline.Snake, - DecoderOptions options = new() - { - Configuration = this.TopLevelConfiguration - }; + // TODO: Figure out Unix cancellation failures, and validate cancellation for each decoder. + //TestImages.Bmp.Car, + //TestImages.Png.Bike, + //TestImages.Tiff.RgbUncompressed, + //TestImages.Gif.Kumin, + //TestImages.Tga.Bit32BottomRight, + //TestImages.Webp.Lossless.WithExif, + //TestImages.Pbm.GrayscaleBinaryWide + }; - return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.DataStream, this.cts.Token)); - } + public static object[][] IdentifyData { get; } = TestFileForEachCodec.Select(f => new object[] { f }).ToArray(); [Theory] - [InlineData(false)] - [InlineData(true)] - public Task LoadAsync_Agnostic_Stream(bool isInputStreamSeekable) + [MemberData(nameof(IdentifyData))] + public async Task IdentifyAsync_PreCancelled(string file) { - this.isTestStreamSeekable = isInputStreamSeekable; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - - DecoderOptions options = new() - { - Configuration = this.TopLevelConfiguration - }; - - return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.DataStream, this.cts.Token)); + using FileStream fs = File.OpenRead(TestFile.GetInputFileFullPath(file)); + CancellationToken preCancelled = new(canceled: true); + await Assert.ThrowsAnyAsync(async () => await Image.IdentifyAsync(fs, preCancelled)); } - [Fact] - public Task LoadAsync_Agnostic_Path() + private static TheoryData CreateLoadData() { - this.isTestStreamSeekable = true; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); + double[] percentages = new[] { 0, 0.3, 0.7 }; - DecoderOptions options = new() - { - Configuration = this.TopLevelConfiguration - }; + TheoryData data = new(); - return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token)); - } - - [Fact] - public Task LoadAsync_Specific_Path() - { - this.isTestStreamSeekable = true; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - - DecoderOptions options = new() + foreach (string file in TestFileForEachCodec) { - Configuration = this.TopLevelConfiguration - }; - - return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token)); + foreach (double p in percentages) + { + data.Add(false, file, p); + data.Add(true, file, p); + } + } + + return data; } - [Theory] - [InlineData(false)] - [InlineData(true)] - public Task IdentifyAsync_Stream(bool isInputStreamSeekable) - { - this.isTestStreamSeekable = isInputStreamSeekable; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - - DecoderOptions options = new() - { - Configuration = this.TopLevelConfiguration - }; + public static TheoryData LoadData { get; } = CreateLoadData(); - return Assert.ThrowsAsync(() => Image.IdentifyAsync(options, this.DataStream, this.cts.Token)); - } - - [Fact] - public Task IdentifyAsync_CustomConfiguration_Path() + // TODO: Figure out cancellation failures on Linux + [ConditionalTheory(typeof(TestEnvironment), nameof(TestEnvironment.IsWindows))] + [MemberData(nameof(LoadData))] + public async Task LoadAsync_IsCancellable(bool useMemoryStream, string file, double percentageOfStreamReadToCancel) { - this.isTestStreamSeekable = true; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); + CancellationTokenSource cts = new(); + using IPausedStream pausedStream = useMemoryStream ? + new PausedMemoryStream(TestFile.Create(file).Bytes) : + new PausedStream(TestFile.GetInputFileFullPath(file)); - DecoderOptions options = new() + pausedStream.OnWaiting(s => { - Configuration = this.TopLevelConfiguration - }; - - return Assert.ThrowsAsync(() => Image.IdentifyAsync(options, this.MockFilePath, this.cts.Token)); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public Task IdentifyWithFormatAsync_CustomConfiguration_Stream(bool isInputStreamSeekable) - { - this.isTestStreamSeekable = isInputStreamSeekable; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); + if (s.Position >= s.Length * percentageOfStreamReadToCancel) + { + cts.Cancel(); + pausedStream.Release(); + } + else + { + pausedStream.Next(); + } + }); + + Configuration configuration = Configuration.CreateDefaultInstance(); + configuration.FileSystem = new SingleStreamFileSystem((Stream)pausedStream); + configuration.StreamProcessingBufferSize = (int)Math.Min(128, pausedStream.Length / 4); DecoderOptions options = new() { - Configuration = this.TopLevelConfiguration + Configuration = configuration }; - return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(options, this.DataStream, this.cts.Token)); - } - - [Fact] - public Task IdentifyWithFormatAsync_CustomConfiguration_Path() - { - this.isTestStreamSeekable = true; - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - - DecoderOptions options = new() + await Assert.ThrowsAnyAsync(async () => { - Configuration = this.TopLevelConfiguration - }; - - return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(options, this.MockFilePath, this.cts.Token)); - } - - [Fact] - public Task IdentifyWithFormatAsync_DefaultConfiguration_Stream() - { - _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - - return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(this.DataStream, this.cts.Token)); + using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token); + }); } - - private async Task DoCancel() - { - // wait until we reach the middle of the steam - await this.notifyWaitPositionReachedSemaphore.WaitAsync(); - - // set the cancellation - this.cts.Cancel(); - - // continue processing the stream - this.continueSemaphore.Release(); - } - - protected override Stream CreateStream() => this.TestFormat.CreateAsyncSemaphoreStream(this.notifyWaitPositionReachedSemaphore, this.continueSemaphore, this.isTestStreamSeekable); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index cace719bd..15e57b48a 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -59,14 +59,16 @@ public partial class ImageTests this.localImageInfoMock = new Mock(); this.localImageFormatMock = new Mock(); - var detector = new Mock(); - detector.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 = detector.As(); + 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); @@ -75,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); @@ -84,6 +86,26 @@ public partial class ImageTests }) .Returns(this.localStreamReturnImageAgnostic); + this.localDecoder + .Setup(x => x.DecodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((_, s, _) => + { + using var ms = new MemoryStream(); + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); + }) + .Returns(Task.FromResult(this.localStreamReturnImageRgba32)); + + this.localDecoder + .Setup(x => x.DecodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((_, s, _) => + { + using var ms = new MemoryStream(); + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); + }) + .Returns(Task.FromResult(this.localStreamReturnImageAgnostic)); + this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); this.LocalConfiguration = new Configuration(); diff --git a/tests/ImageSharp.Tests/Image/NonSeekableStream.cs b/tests/ImageSharp.Tests/Image/NonSeekableStream.cs index e9b64d3b2..4b1f6e156 100644 --- a/tests/ImageSharp.Tests/Image/NonSeekableStream.cs +++ b/tests/ImageSharp.Tests/Image/NonSeekableStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Tests; @@ -16,17 +16,73 @@ internal class NonSeekableStream : Stream public override bool CanWrite => false; + public override bool CanTimeout => this.dataStream.CanTimeout; + public override long Length => throw new NotSupportedException(); public override long Position { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int ReadTimeout + { + get => this.dataStream.ReadTimeout; + set => this.dataStream.ReadTimeout = value; + } + + public override int WriteTimeout + { + get => this.dataStream.WriteTimeout; + set => this.dataStream.WriteTimeout = value; } public override void Flush() => this.dataStream.Flush(); - public override int Read(byte[] buffer, int offset, int count) => this.dataStream.Read(buffer, offset, count); + public override int ReadByte() => this.dataStream.ReadByte(); + + public override int Read(byte[] buffer, int offset, int count) + => this.dataStream.Read(buffer, offset, count); + + public override int Read(Span buffer) + => this.dataStream.Read(buffer); + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + => this.dataStream.BeginRead(buffer, offset, count, callback, state); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => this.dataStream.ReadAsync(buffer, offset, count, cancellationToken); + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => this.dataStream.ReadAsync(buffer, cancellationToken); + + public override int EndRead(IAsyncResult asyncResult) + => this.dataStream.EndRead(asyncResult); + + public override void WriteByte(byte value) => this.dataStream.WriteByte(value); + + public override void Write(ReadOnlySpan buffer) => this.dataStream.Write(buffer); + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + => this.dataStream.BeginWrite(buffer, offset, count, callback, state); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => this.dataStream.WriteAsync(buffer, offset, count, cancellationToken); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => this.dataStream.WriteAsync(buffer, cancellationToken); + + public override void EndWrite(IAsyncResult asyncResult) => this.dataStream.EndWrite(asyncResult); + + public override void CopyTo(Stream destination, int bufferSize) => this.dataStream.CopyTo(destination, bufferSize); + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + => this.dataStream.CopyToAsync(destination, bufferSize, cancellationToken); + + public override Task FlushAsync(CancellationToken cancellationToken) => this.dataStream.FlushAsync(cancellationToken); + + public override void Close() => this.dataStream.Close(); public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 8606dae54..1a52ade62 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -10,10 +10,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC; public class IptcProfileTests { - private static JpegDecoder JpegDecoder => new(); - - private static TiffDecoder TiffDecoder => new(); - public static IEnumerable AllIptcTags() { foreach (object tag in Enum.GetValues(typeof(IptcTag))) @@ -117,7 +113,7 @@ public class IptcProfileTests public void ReadIptcMetadata_FromJpg_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(JpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder.Instance)) { Assert.NotNull(image.Metadata.IptcProfile); var iptcValues = image.Metadata.IptcProfile.Values.ToList(); @@ -130,7 +126,7 @@ public class IptcProfileTests public void ReadIptcMetadata_FromTiff_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) + using (Image image = provider.GetImage(TiffDecoder.Instance)) { IptcProfile iptc = image.Frames.RootFrame.Metadata.IptcProfile; Assert.NotNull(iptc); @@ -166,7 +162,7 @@ public class IptcProfileTests public void ReadApp13_WithEmptyIptc_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(JpegDecoder); + using Image image = provider.GetImage(JpegDecoder.Instance); Assert.Null(image.Metadata.IptcProfile); } @@ -231,7 +227,7 @@ public class IptcProfileTests public void WritingImage_PreservesIptcProfile() { // arrange - var image = new Image(1, 1); + using Image image = new(1, 1); image.Metadata.IptcProfile = new IptcProfile(); const string expectedCaptionWriter = "unittest"; const string expectedCaption = "test"; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs index 8ef31a2af..5dc6ac6db 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs @@ -16,22 +16,12 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Xmp; public class XmpProfileTests { - private static GifDecoder GifDecoder => new(); - - private static JpegDecoder JpegDecoder => new(); - - private static PngDecoder PngDecoder => new(); - - private static TiffDecoder TiffDecoder => new(); - - private static WebpDecoder WebpDecoder => new(); - [Theory] [WithFile(TestImages.Gif.Receipt, PixelTypes.Rgba32)] public async Task ReadXmpMetadata_FromGif_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = await provider.GetImageAsync(GifDecoder)) + using (Image image = await provider.GetImageAsync(GifDecoder.Instance)) { XmpProfile actual = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; XmpProfileContainsExpectedValues(actual); @@ -45,7 +35,7 @@ public class XmpProfileTests public async Task ReadXmpMetadata_FromJpg_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = await provider.GetImageAsync(JpegDecoder)) + using (Image image = await provider.GetImageAsync(JpegDecoder.Instance)) { XmpProfile actual = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; XmpProfileContainsExpectedValues(actual); @@ -57,7 +47,7 @@ public class XmpProfileTests public async Task ReadXmpMetadata_FromPng_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = await provider.GetImageAsync(PngDecoder)) + using (Image image = await provider.GetImageAsync(PngDecoder.Instance)) { XmpProfile actual = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; XmpProfileContainsExpectedValues(actual); @@ -69,7 +59,7 @@ public class XmpProfileTests public async Task ReadXmpMetadata_FromTiff_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = await provider.GetImageAsync(TiffDecoder)) + using (Image image = await provider.GetImageAsync(TiffDecoder.Instance)) { XmpProfile actual = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; XmpProfileContainsExpectedValues(actual); @@ -81,7 +71,7 @@ public class XmpProfileTests public async Task ReadXmpMetadata_FromWebp_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = await provider.GetImageAsync(WebpDecoder)) + using (Image image = await provider.GetImageAsync(WebpDecoder.Instance)) { XmpProfile actual = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; XmpProfileContainsExpectedValues(actual); @@ -121,7 +111,7 @@ public class XmpProfileTests public void WritingGif_PreservesXmpProfile() { // arrange - var image = new Image(1, 1); + using var image = new Image(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; var encoder = new GifEncoder(); @@ -139,7 +129,7 @@ public class XmpProfileTests public void WritingJpeg_PreservesXmpProfile() { // arrange - var image = new Image(1, 1); + using var image = new Image(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; var encoder = new JpegEncoder(); @@ -158,7 +148,7 @@ public class XmpProfileTests { // arrange var provider = TestImageProvider.File(TestImages.Jpeg.Baseline.ExtendedXmp); - using Image image = await provider.GetImageAsync(JpegDecoder); + using Image image = await provider.GetImageAsync(JpegDecoder.Instance); XmpProfile original = image.Metadata.XmpProfile; var encoder = new JpegEncoder(); @@ -175,7 +165,7 @@ public class XmpProfileTests public void WritingPng_PreservesXmpProfile() { // arrange - var image = new Image(1, 1); + using var image = new Image(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; var encoder = new PngEncoder(); @@ -193,7 +183,7 @@ public class XmpProfileTests public void WritingTiff_PreservesXmpProfile() { // arrange - var image = new Image(1, 1); + using var image = new Image(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Frames.RootFrame.Metadata.XmpProfile = original; var encoder = new TiffEncoder(); @@ -211,7 +201,7 @@ public class XmpProfileTests public void WritingWebp_PreservesXmpProfile() { // arrange - var image = new Image(1, 1); + using var image = new Image(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; var encoder = new WebpEncoder(); diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 9d920d718..a53e50806 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Collections.Concurrent; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -24,16 +23,6 @@ public sealed class TestFile // ReSharper disable once InconsistentNaming private static readonly Lazy InputImagesDirectoryValue = new(() => TestEnvironment.InputImagesDirectoryFullPath); - /// - /// The image (lazy initialized value) - /// - private volatile Image image; - - /// - /// Used to ensure image loading is threadsafe. - /// - private readonly object syncLock = new(); - /// /// The image bytes /// @@ -65,25 +54,6 @@ public sealed class TestFile /// public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.FullPath); - /// - /// Gets the image with lazy initialization. - /// - private Image Image - { - get - { - if (this.image is null) - { - lock (this.syncLock) - { - this.image ??= ImageSharp.Image.Load(this.Bytes); - } - } - - return this.image; - } - } - /// /// Gets the input image directory. /// @@ -137,12 +107,12 @@ public sealed class TestFile /// /// The . /// - public Image CreateRgba32Image() - => this.Image.Clone(); + public Image CreateRgba32Image() => Image.Load(this.Bytes); /// /// Creates a new image. /// + /// The image decoder. /// /// The . /// @@ -152,13 +122,14 @@ public sealed class TestFile /// /// Creates a new image. /// + /// The image decoder. + /// The general decoder options. /// /// The . /// 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 127ccd32d..f3176f16c 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests; /// /// A test image file. /// -public class TestFormat : IConfigurationModule, IImageFormat +public class TestFormat : IImageFormatConfigurationModule, IImageFormat { private readonly Dictionary sampleImages = new(); @@ -187,7 +187,7 @@ public class TestFormat : IConfigurationModule, IImageFormat public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : IImageDecoderSpecialized + public class TestDecoder : SpecializedImageDecoder { private readonly TestFormat testFormat; @@ -201,18 +201,13 @@ public class TestFormat : IConfigurationModule, IImageFormat public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); + protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); + protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; - public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Configuration configuration = options.GeneralOptions.Configuration; var ms = new MemoryStream(); @@ -229,13 +224,13 @@ public class TestFormat : IConfigurationModule, IImageFormat return this.testFormat.Sample(); } - public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); } public class TestDecoderOptions : ISpecializedDecoderOptions { - public DecoderOptions GeneralOptions { get; set; } = new(); + public DecoderOptions GeneralOptions { get; init; } = DecoderOptions.Default; } public class TestEncoder : IImageEncoder @@ -248,6 +243,8 @@ public class TestFormat : IConfigurationModule, IImageFormat public IEnumerable FileExtensions => this.testFormat.SupportedExtensions; + public bool SkipMetadata { get; init; } + public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { @@ -255,7 +252,8 @@ public class TestFormat : IConfigurationModule, IImageFormat } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel => Task.CompletedTask; // TODO record this happened so we can verify it. + where TPixel : unmanaged, IPixel + => Task.CompletedTask; // TODO record this happened so we can verify it. } public struct TestPixelForAgnosticDecode : IPixel diff --git a/tests/ImageSharp.Tests/TestUtilities/IPausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/IPausedStream.cs new file mode 100644 index 000000000..ec9b2e7e1 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/IPausedStream.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Tests.TestUtilities; + +public interface IPausedStream : IDisposable +{ + public void OnWaiting(Action onWaitingCallback); + + public void OnWaiting(Action onWaitingCallback); + + public void Next(); + + public void Release(); + + public long Length { get; } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index a0f544b2f..3285da31b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -202,7 +202,7 @@ public abstract partial class TestImageProvider : IXunitSerializable return cachedImage.Clone(this.Configuration); } - public override Task> GetImageAsync(IImageDecoder 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); } - public override Image GetImage(IImageDecoderSpecialized 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(IImageDecoderSpecialized 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); } public override void Deserialize(IXunitSerializationInfo info) @@ -272,17 +272,17 @@ public abstract partial class TestImageProvider : IXunitSerializable 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(IImageDecoderSpecialized 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 8cd04f1cd..8f22fb2b2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -99,17 +99,18 @@ public abstract partial class TestImageProvider : ITestImageProvider, IX 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(IImageDecoderSpecialized 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(IImageDecoderSpecialized 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}!"); /// /// Returns an instance to the test case with the necessary traits. /// + /// The operation to apply to the image before returning. /// A test image. public Image GetImage(Action operationsToApply) { diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs new file mode 100644 index 000000000..ae4af24f1 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs @@ -0,0 +1,162 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; + +namespace SixLabors.ImageSharp.Tests.TestUtilities; + +/// +/// is a variant of that derives from instead of encapsulating it. +/// It is used to test decoder REacellation without relying on of our standard prefetching of arbitrary streams to +/// on asynchronous path. +/// +public class PausedMemoryStream : MemoryStream, IPausedStream +{ + private readonly SemaphoreSlim semaphore = new(0); + + private readonly CancellationTokenSource cancelationTokenSource = new(); + + private Action onWaitingCallback; + + public void OnWaiting(Action onWaitingCallback) => this.onWaitingCallback = onWaitingCallback; + + public void OnWaiting(Action onWaitingCallback) => this.OnWaiting(_ => onWaitingCallback()); + + public void Release() + { + this.semaphore.Release(); + this.cancelationTokenSource.Cancel(); + } + + public void Next() => this.semaphore.Release(); + + private void Wait() + { + if (this.cancelationTokenSource.IsCancellationRequested) + { + return; + } + + this.onWaitingCallback?.Invoke(this); + + try + { + this.semaphore.Wait(this.cancelationTokenSource.Token); + } + catch (OperationCanceledException) + { + // ignore this as its just used to unlock any waits in progress + } + } + + private async Task Await(Func action) + { + await Task.Yield(); + this.Wait(); + await action(); + } + + private async Task Await(Func> action) + { + await Task.Yield(); + this.Wait(); + return await action(); + } + + private T Await(Func action) + { + this.Wait(); + return action(); + } + + private void Await(Action action) + { + this.Wait(); + action(); + } + + public PausedMemoryStream(byte[] data) + : base(data) + { + } + + public override bool CanTimeout => base.CanTimeout; + + public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // To make sure the copy operation is buffered and pausable, we should override MemoryStream's strategy + // with the default Stream copy logic of System.IO.Stream: + // https://github.com/dotnet/runtime/blob/4f53c2f7e62df44f07cf410df8a0d439f42a0a71/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L104-L116 + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int bytesRead; + while ((bytesRead = await this.ReadAsync(new Memory(buffer), cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public override bool CanRead => base.CanRead; + + public override bool CanSeek => base.CanSeek; + + public override bool CanWrite => base.CanWrite; + + public override void Flush() => this.Await(() => base.Flush()); + + public override int Read(byte[] buffer, int offset, int count) => this.Await(() => base.Read(buffer, offset, count)); + + public override long Seek(long offset, SeekOrigin origin) => this.Await(() => base.Seek(offset, origin)); + + public override void SetLength(long value) => this.Await(() => base.SetLength(value)); + + public override void Write(byte[] buffer, int offset, int count) => this.Await(() => base.Write(buffer, offset, count)); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => base.ReadAsync(buffer, offset, count, cancellationToken)); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => this.Await(() => base.WriteAsync(buffer, offset, count, cancellationToken)); + + public override void WriteByte(byte value) => this.Await(() => base.WriteByte(value)); + + public override int ReadByte() => this.Await(() => base.ReadByte()); + + public override void CopyTo(Stream destination, int bufferSize) + { + // See comments on CopyToAsync. + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int bytesRead; + while ((bytesRead = this.Read(buffer, 0, buffer.Length)) != 0) + { + destination.Write(buffer, 0, bytesRead); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public override int Read(Span buffer) + { + this.Wait(); + return base.Read(buffer); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => this.Await(() => base.ReadAsync(buffer, cancellationToken)); + + public override void Write(ReadOnlySpan buffer) + { + this.Wait(); + base.Write(buffer); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => this.Await(() => base.WriteAsync(buffer, cancellationToken)); +} diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index 2d13de074..3c780f347 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -1,13 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; + namespace SixLabors.ImageSharp.Tests.TestUtilities; -public class PausedStream : Stream +public class PausedStream : Stream, IPausedStream { - private readonly SemaphoreSlim semaphore = new SemaphoreSlim(0); + private readonly SemaphoreSlim semaphore = new(0); - private readonly CancellationTokenSource cancelationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource cancelationTokenSource = new(); private readonly Stream innerStream; private Action onWaitingCallback; @@ -83,9 +85,25 @@ public class PausedStream : Stream public override bool CanTimeout => this.innerStream.CanTimeout; - public override void Close() => this.Await(() => this.innerStream.Close()); - - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => this.Await(() => this.innerStream.CopyToAsync(destination, bufferSize, cancellationToken)); + public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // To make sure the copy operation is buffered and pausable, we should override MemoryStream's strategy + // with the default Stream copy logic of System.IO.Stream: + // https://github.com/dotnet/runtime/blob/4f53c2f7e62df44f07cf410df8a0d439f42a0a71/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L104-L116 + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int bytesRead; + while ((bytesRead = await this.ReadAsync(new Memory(buffer), cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } public override bool CanRead => this.innerStream.CanRead; @@ -93,9 +111,9 @@ public class PausedStream : Stream public override bool CanWrite => this.innerStream.CanWrite; - public override long Length => this.Await(() => this.innerStream.Length); + public override long Length => this.innerStream.Length; - public override long Position { get => this.Await(() => this.innerStream.Position); set => this.Await(() => this.innerStream.Position = value); } + public override long Position { get => this.innerStream.Position; set => this.innerStream.Position = value; } public override void Flush() => this.Await(() => this.innerStream.Flush()); @@ -115,9 +133,33 @@ public class PausedStream : Stream public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); - protected override void Dispose(bool disposing) => this.innerStream.Dispose(); + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); - public override void CopyTo(Stream destination, int bufferSize) => this.Await(() => this.innerStream.CopyTo(destination, bufferSize)); + if (disposing) + { + this.innerStream.Dispose(); + } + } + + public override void CopyTo(Stream destination, int bufferSize) + { + // See comments on CopyToAsync. + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int bytesRead; + while ((bytesRead = this.Read(buffer, 0, buffer.Length)) != 0) + { + destination.Write(buffer, 0, bytesRead); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } public override int Read(Span buffer) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs index 1290fdea3..a4d305d97 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; @@ -13,38 +12,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; /// public sealed class ImageSharpPngEncoderWithDefaultConfiguration : PngEncoder { - /// - /// Encodes the image to the specified stream from the . - /// - /// The pixel format. - /// The to encode from. - /// The to encode the image data to. - public override void Encode(Image image, Stream stream) + /// + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { Configuration configuration = Configuration.Default; MemoryAllocator allocator = configuration.MemoryAllocator; using PngEncoderCore encoder = new(allocator, configuration, this); - encoder.Encode(image, stream); - } - - /// - /// Encodes the image to the specified stream from the . - /// - /// The pixel format. - /// The to encode from. - /// The to encode the image data to. - /// The token to monitor for cancellation requests. - /// A representing the asynchronous operation. - public override async Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - { - Configuration configuration = Configuration.Default; - MemoryAllocator allocator = configuration.MemoryAllocator; - - // The introduction of a local variable that refers to an object the implements - // 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); + encoder.Encode(image, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 3392d6814..7203116c9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -11,7 +11,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; -public class MagickReferenceDecoder : IImageDecoder +public class MagickReferenceDecoder : ImageDecoder { private readonly bool validate; @@ -24,24 +24,25 @@ public class MagickReferenceDecoder : IImageDecoder public static MagickReferenceDecoder Instance { get; } = new(); - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Configuration configuration = options.Configuration; - var bmpReadDefines = new BmpReadDefines + BmpReadDefines bmpReadDefines = new() { IgnoreFileSize = !this.validate }; - var settings = new MagickReadSettings(); - settings.FrameCount = (int)options.MaxFrames; + MagickReadSettings settings = new() + { + FrameCount = (int)options.MaxFrames + }; settings.SetDefines(bmpReadDefines); - using var magickImageCollection = new MagickImageCollection(stream, settings); - var framesList = new List>(); + using MagickImageCollection magickImageCollection = new(stream, settings); + List> framesList = new(); foreach (IMagickImage magicFrame in magickImageCollection) { - var frame = new ImageFrame(configuration, magicFrame.Width, magicFrame.Height); + ImageFrame frame = new(configuration, magicFrame.Width, magicFrame.Height); framesList.Add(frame); MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; @@ -68,10 +69,10 @@ public class MagickReferenceDecoder : IImageDecoder return new Image(configuration, new ImageMetadata(), framesList); } - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); - public 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) @@ -83,9 +84,9 @@ public class MagickReferenceDecoder : IImageDecoder Span destBuffer = m.Span; PixelOperations.Instance.FromRgba32( configuration, - sourcePixels.Slice(0, destBuffer.Length), + sourcePixels[..destBuffer.Length], destBuffer); - sourcePixels = sourcePixels.Slice(destBuffer.Length); + sourcePixels = sourcePixels[destBuffer.Length..]; } } @@ -100,7 +101,7 @@ public class MagickReferenceDecoder : IImageDecoder rgbaBytes, destBuffer, destBuffer.Length); - rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 8); + rgbaBytes = rgbaBytes[(destBuffer.Length * 8)..]; } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 53d9410c5..503fd53ce 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -9,31 +9,30 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; -public class SystemDrawingReferenceDecoder : IImageDecoder +public class SystemDrawingReferenceDecoder : ImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using var sourceBitmap = new SDBitmap(stream); + using SDBitmap sourceBitmap = new(stream); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using var sourceBitmap = new SDBitmap(stream); + using SDBitmap sourceBitmap = new(stream); if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) { return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sourceBitmap); } - using var convertedBitmap = new SDBitmap( + using SDBitmap convertedBitmap = new( sourceBitmap.Width, sourceBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) + using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(convertedBitmap)) { g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; @@ -46,6 +45,6 @@ public class SystemDrawingReferenceDecoder : IImageDecoder return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); } - public 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/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs index af13d64ce..d8dda2eea 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -9,24 +9,22 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; public class SystemDrawingReferenceEncoder : IImageEncoder { - private readonly System.Drawing.Imaging.ImageFormat imageFormat; + private readonly ImageFormat imageFormat; public SystemDrawingReferenceEncoder(ImageFormat imageFormat) - { - this.imageFormat = imageFormat; - } + => this.imageFormat = imageFormat; public static SystemDrawingReferenceEncoder Png { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Png); public static SystemDrawingReferenceEncoder Bmp { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Bmp); + public bool SkipMetadata { get; init; } + public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { - using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image)) - { - sdBitmap.Save(stream, this.imageFormat); - } + using System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image); + sdBitmap.Save(stream, this.imageFormat); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 89b43a066..68c1664a8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -53,7 +53,7 @@ public static partial class TestEnvironment private static Configuration CreateDefaultConfiguration() { - var cfg = new Configuration( + Configuration cfg = new( new JpegConfigurationModule(), new GifConfigurationModule(), new PbmConfigurationModule(), diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index cd16fe4b2..31c9f541e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -19,6 +19,7 @@ public static class TestImageExtensions /// /// TODO: Consider adding this private processor to the library /// + /// The image processing context. public static void MakeOpaque(this IImageProcessingContext ctx) => ctx.ApplyProcessor(new MakeOpaqueProcessor()); @@ -323,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( @@ -348,11 +349,11 @@ public static class TestImageExtensions { 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); } @@ -533,7 +534,7 @@ public static class TestImageExtensions referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); using MemoryStream stream = new(testFile.Bytes); - using (Image original = referenceDecoder.Decode(referenceDecoderOptions ?? DecoderOptions.Default, stream, default)) + using (Image original = referenceDecoder.Decode(referenceDecoderOptions ?? DecoderOptions.Default, stream)) { comparer.VerifySimilarity(original, image); } @@ -559,7 +560,7 @@ public static class TestImageExtensions referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); using MemoryStream stream = new(testFile.Bytes); - using (Image original = referenceDecoder.Decode(DecoderOptions.Default, stream, default)) + using (Image original = referenceDecoder.Decode(DecoderOptions.Default, stream)) { comparer.VerifySimilarity(original, image); } @@ -680,7 +681,7 @@ public static class TestImageExtensions referenceDecoder ??= TestEnvironment.GetReferenceDecoder(actualOutputFile); using FileStream stream = File.OpenRead(actualOutputFile); - using Image encodedImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default); + using Image encodedImage = referenceDecoder.Decode(DecoderOptions.Default, stream); ImageComparer comparer = customComparer ?? ImageComparer.Exact; comparer.VerifySimilarity(encodedImage, image); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index 3d4dd4a65..81ea77b6b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -40,8 +40,8 @@ public class MagickReferenceCodecTests using FileStream mStream = File.OpenRead(path); using FileStream sdStream = File.OpenRead(path); - using Image mImage = magickDecoder.Decode(DecoderOptions.Default, mStream, default); - using Image sdImage = sdDecoder.Decode(DecoderOptions.Default, sdStream, default); + using Image mImage = magickDecoder.Decode(DecoderOptions.Default, mStream); + using Image sdImage = sdDecoder.Decode(DecoderOptions.Default, sdStream); ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); @@ -71,8 +71,8 @@ public class MagickReferenceCodecTests var comparer = ImageComparer.TolerantPercentage(1, 1020); using FileStream mStream = File.OpenRead(path); using FileStream sdStream = File.OpenRead(path); - using Image mImage = magickDecoder.Decode(DecoderOptions.Default, mStream, default); - using Image sdImage = sdDecoder.Decode(DecoderOptions.Default, sdStream, default); + using Image mImage = magickDecoder.Decode(DecoderOptions.Default, mStream); + using Image sdImage = sdDecoder.Decode(DecoderOptions.Default, sdStream); ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); mImage.DebugSave(dummyProvider); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs index 91df7d149..a89feb3c3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -93,7 +93,7 @@ public class SystemDrawingReferenceCodecTests { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); using FileStream stream = File.OpenRead(path); - using Image image = SystemDrawingReferenceDecoder.Instance.Decode(DecoderOptions.Default, stream, default); + using Image image = SystemDrawingReferenceDecoder.Instance.Decode(DecoderOptions.Default, stream); image.DebugSave(dummyProvider); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 5889907f0..13edd2a06 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -346,7 +346,7 @@ public class TestImageProviderTests } } - private class TestDecoder : IImageDecoderSpecialized + private class TestDecoder : SpecializedImageDecoder { // Couldn't make xUnit happy without this hackery: private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -363,26 +363,21 @@ public class TestImageProviderTests } } - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken); + protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => this.Decode((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken); - - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken); - - public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); + protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -392,7 +387,7 @@ public class TestImageProviderTests } } - private class TestDecoderWithParameters : IImageDecoderSpecialized + private class TestDecoderWithParameters : SpecializedImageDecoder { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -408,26 +403,21 @@ public class TestImageProviderTests } } - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken); + protected override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => this.Decode((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken); - - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken); - - public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + protected override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); + protected override TestDecoderWithParametersOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -439,15 +429,15 @@ public class TestImageProviderTests private class TestDecoderOptions : ISpecializedDecoderOptions { - public DecoderOptions GeneralOptions { get; set; } = new(); + public DecoderOptions GeneralOptions { get; init; } = new(); } private class TestDecoderWithParametersOptions : ISpecializedDecoderOptions { - public string Param1 { get; set; } + public string Param1 { get; init; } - public int Param2 { get; set; } + public int Param2 { get; init; } - public DecoderOptions GeneralOptions { get; set; } = new(); + public DecoderOptions GeneralOptions { get; init; } = new(); } }