diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 0cf28c6bb2..284fc2b53d 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -288,7 +288,7 @@ private static void AotCompileImageEncoder() 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 b299405758..d4d0558238 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -42,11 +42,11 @@ public 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 MemoryAllocator MemoryAllocator /// 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 void Configure(IConfigurationModule 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 05303058ef..38e6e6ea64 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 35d5690c7d..15c213f882 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 26c4e7ec58..b3387ce808 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 9e20da170a..156e2f9610 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 b0d8453a7e..a67b06cb88 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 @@ private BmpFormat() } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static BmpFormat Instance { get; } = new BmpFormat(); @@ -30,5 +30,5 @@ private BmpFormat() 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 3e3f1aa50d..989fc49fc2 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 8c8067adaf..31116d661e 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 cf8f4637ef..e08fc62309 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 351554eb07..386b1bd1c3 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 df302c7eac..4636420711 100644 --- a/src/ImageSharp/Formats/Gif/GifFormat.cs +++ b/src/ImageSharp/Formats/Gif/GifFormat.cs @@ -13,7 +13,7 @@ private GifFormat() } /// - /// 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 7052bc49fe..3729c98d9b 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -6,35 +6,68 @@ 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 112c38bd5a..42d04a54dc 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -6,27 +6,32 @@ 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 ca9c474e13..131949c968 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 @@ 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 e2b221a080..b05a8c037a 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 ab5f536ff1..0000000000 --- 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 75f643d0c6..e0a4c9b62c 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 77cffe34ca..e9506795ce 100644 --- a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs +++ b/src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs @@ -6,37 +6,51 @@ 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 0000000000..695e9c8446 --- /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 a18974bbd2..0000000000 --- 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 a8cbd5aad9..2a5b91b094 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -4,61 +4,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 { @@ -90,7 +50,7 @@ internal static Image Decode( 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 a0c087e646..d6870f716b 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 665431c952..0000000000 --- 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 f8763b72fd..6249f8dc77 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 void SaveAsBmp(this Image source, string path, BmpEncoder encoder) /// 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 Task SaveAsBmpAsync(this Image source, string path, BmpEncoder enc /// 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 void SaveAsBmp(this Image source, Stream stream) /// 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 Task SaveAsBmpAsync(this Image source, Stream stream, Cancellation /// 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 void SaveAsBmp(this Image source, Stream stream, BmpEncoder encode /// 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 Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder e /// 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 Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder e /// 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 Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder e /// 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 void SaveAsGif(this Image source, string path, GifEncoder encoder) /// 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 Task SaveAsGifAsync(this Image source, string path, GifEncoder enc /// 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 void SaveAsGif(this Image source, Stream stream) /// 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 Task SaveAsGifAsync(this Image source, Stream stream, Cancellation /// 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 void SaveAsGif(this Image source, Stream stream, GifEncoder encode /// 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 Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder e /// 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 Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder e /// 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 Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder e /// 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 void SaveAsJpeg(this Image source, string path, JpegEncoder encode /// 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 Task SaveAsJpegAsync(this Image source, string path, JpegEncoder e /// 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 void SaveAsJpeg(this Image source, Stream stream) /// 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 Task SaveAsJpegAsync(this Image source, Stream stream, Cancellatio /// 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 void SaveAsJpeg(this Image source, Stream stream, JpegEncoder enco /// 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 Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder /// 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 Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder /// 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 Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder /// 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 void SaveAsPbm(this Image source, string path, PbmEncoder encoder) /// 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 Task SaveAsPbmAsync(this Image source, string path, PbmEncoder enc /// 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 void SaveAsPbm(this Image source, Stream stream) /// 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 Task SaveAsPbmAsync(this Image source, Stream stream, Cancellation /// 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 void SaveAsPbm(this Image source, Stream stream, PbmEncoder encode /// 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 Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder e /// 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 Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder e /// 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 Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder e /// 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 void SaveAsPng(this Image source, string path, PngEncoder encoder) /// 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 Task SaveAsPngAsync(this Image source, string path, PngEncoder enc /// 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 void SaveAsPng(this Image source, Stream stream) /// 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 Task SaveAsPngAsync(this Image source, Stream stream, Cancellation /// 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 void SaveAsPng(this Image source, Stream stream, PngEncoder encode /// 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 Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder e /// 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 Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder e /// 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 Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder e /// 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 void SaveAsTga(this Image source, string path, TgaEncoder encoder) /// 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 Task SaveAsTgaAsync(this Image source, string path, TgaEncoder enc /// 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 void SaveAsTga(this Image source, Stream stream) /// 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 Task SaveAsTgaAsync(this Image source, Stream stream, Cancellation /// 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 void SaveAsTga(this Image source, Stream stream, TgaEncoder encode /// 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 Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder e /// 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 Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder e /// 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 Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder e /// 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 void SaveAsWebp(this Image source, string path, WebpEncoder encode /// 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 Task SaveAsWebpAsync(this Image source, string path, WebpEncoder e /// 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 void SaveAsWebp(this Image source, Stream stream) /// 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 Task SaveAsWebpAsync(this Image source, Stream stream, Cancellatio /// 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 void SaveAsWebp(this Image source, Stream stream, WebpEncoder enco /// 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 Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder /// 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 Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder /// 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 Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder /// 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 void SaveAsTiff(this Image source, string path, TiffEncoder encode /// 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 Task SaveAsTiffAsync(this Image source, string path, TiffEncoder e /// 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 void SaveAsTiff(this Image source, Stream stream) /// 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 Task SaveAsTiffAsync(this Image source, Stream stream, Cancellatio /// 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 void SaveAsTiff(this Image source, Stream stream, TiffEncoder enco /// 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 9ca3a0f223..7498aa7c3b 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 9f22c62294..b13f3e8d8d 100644 --- a/src/ImageSharp/Formats/ImageFormatManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -22,7 +22,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(); @@ -122,7 +122,7 @@ public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) 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); } /// @@ -135,7 +135,7 @@ public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) 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 963b8eb299..3f1b7bc372 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 7e85ddad5c..b8a142d287 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoderSpecialized.Decode(JpegDe 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 193b2d3a8f..78ee317419 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 2ddd829f87..5ff4b1694d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -48,16 +48,9 @@ public int? Quality 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 b9c126e29d..a07be33fc4 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 @@ private JpegFormat() } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static JpegFormat Instance { get; } = new JpegFormat(); @@ -30,5 +30,5 @@ private JpegFormat() 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 cd411a2d75..9e5d6d3bc6 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 2caf8ecc1f..f7b32b5fce 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 6b25347c04..0f492fae72 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 bdf5e785ec..f21728b073 100644 --- a/src/ImageSharp/Formats/Pbm/PbmFormat.cs +++ b/src/ImageSharp/Formats/Pbm/PbmFormat.cs @@ -13,7 +13,7 @@ private PbmFormat() } /// - /// 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 caee817191..42f7f6e8e1 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 bcc193f0b8..56e76da102 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationTo 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 2247cd6b7b..8d47a9794b 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -176,7 +176,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken this.InitializeImage(metadata, out image); } - this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); + this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata, cancellationToken); break; case PngChunkType.Palette: @@ -555,7 +555,8 @@ private int CalculateScanlineLength(int width) /// 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); @@ -564,11 +565,11 @@ private void ReadScanlines(PngChunk chunk, ImageFrame image, Png 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); } } @@ -579,11 +580,13 @@ private void ReadScanlines(PngChunk chunk, ImageFrame image, Png /// 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) { @@ -639,7 +642,8 @@ private void DecodePixelData(DeflateStream compressedStream, ImageFrame< /// 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; @@ -661,6 +665,7 @@ private void DecodeInterlacedPixelData(DeflateStream compressedStream, I 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 2daa136c3d..29562d2f8f 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -74,19 +74,9 @@ public PngEncoder() => 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 a0cc065004..2d1f2dcc7d 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 @@ private PngFormat() } /// - /// Gets the current instance. + /// Gets the shared instance. /// public static PngFormat Instance { get; } = new PngFormat(); @@ -30,5 +30,5 @@ private PngFormat() 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 0000000000..b7eb86afb0 --- /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 0000000000..fa6461464b --- /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 06f93d1ce5..45847b0a57 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 362daa77d2..f6f1e6762f 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 e0a3932355..71acf3ae83 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 886d4eea36..e024dfc621 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 4e1c9c2f84..a5ce4f8426 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -32,7 +32,8 @@ public WebpTiffCompression(DecoderOptions options, MemoryAllocator memoryAllocat /// 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 67b6517237..d63ea2158a 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 b433222a07..3ec9b3d68f 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 76b486416b..3a80d7533f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -113,7 +113,7 @@ public TiffDecoderCore(DecoderOptions options) 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 e7bb08cdc3..24cca41dc2 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 62ad93c840..76a06d013d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFormat.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFormat.cs @@ -15,7 +15,7 @@ private TiffFormat() } /// - /// 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 f266cde32f..571749957e 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 86c868d7e8..7a97b86a79 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 @@ IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, Ca } /// - 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 @@ Image IImageDecoder.Decode(DecoderOptions options, Stream stream 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 359128254f..e314d38017 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 cc2073bc08..29c74b11bf 100644 --- a/src/ImageSharp/Formats/Webp/WebpFormat.cs +++ b/src/ImageSharp/Formats/Webp/WebpFormat.cs @@ -13,7 +13,7 @@ private WebpFormat() } /// - /// 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 291a318864..efa8f6f4be 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 @@ public override long Position 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 @@ public override int Read(byte[] buffer, int offset, int count) [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 @@ private bool IsInReadBuffer(long newPosition, out long index) [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 ecb299ca31..55271afcb6 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -44,6 +44,7 @@ internal sealed class ChunkedMemoryStream : Stream /// /// Initializes a new instance of the class. /// + /// The memory allocator. public ChunkedMemoryStream(MemoryAllocator allocator) { Guard.NotNull(allocator, nameof(allocator)); diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 6953da1cea..9616a8739b 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -113,12 +113,27 @@ private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stre /// /// 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); @@ -127,11 +142,26 @@ private static (Image Image, IImageFormat Format) Decode(Decoder 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) @@ -139,10 +169,25 @@ private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, return (null, null); } - Image img = decoder.Decode(options, stream, cancellationToken); + Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false); return (img, format); } + /// + /// Reads the raw image information from the specified stream. + /// + /// The general decoder options. + /// The stream. + /// + /// The or null if a suitable info detector is not found. + /// + private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentify(DecoderOptions options, Stream stream) + { + IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); + IImageInfo info = decoder?.Identify(options, stream); + return (info, format); + } + /// /// Reads the raw image information from the specified stream. /// @@ -152,16 +197,19 @@ private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, /// /// 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 eefb087764..33653434d5 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -62,7 +62,7 @@ public static Task DetectFormatAsync(DecoderOptions options, Strea => WithSeekableStreamAsync( options, stream, - (s, _) => InternalDetectFormat(options.Configuration, s), + (s, _) => Task.FromResult(InternalDetectFormat(options.Configuration, s)), cancellationToken); /// @@ -160,7 +160,7 @@ public static async Task IdentifyAsync( /// 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 static IImageInfo Identify(DecoderOptions options, Stream stream, out IIm => WithSeekableStreamAsync( options, stream, - (s, ct) => InternalIdentity(options, s, ct), + (s, ct) => InternalIdentifyAsync(options, s, ct), cancellationToken); /// @@ -289,11 +289,7 @@ public static Image Load(DecoderOptions options, Stream stream) /// 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 static Image Load(DecoderOptions options, Stream stream, 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 @@ await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct) 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 @@ internal static T WithSeekableStream( 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 @@ internal static T WithSeekableStream( internal static async Task WithSeekableStreamAsync( DecoderOptions options, Stream stream, - Func action, + Func> action, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); @@ -575,33 +570,21 @@ internal static async Task WithSeekableStreamAsync( } 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 91c96b55f6..2df86b1097 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -41,6 +41,11 @@ protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetad /// /// 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 e79fceeaa0..2174a0eb94 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -47,10 +47,8 @@ public static void Save(this Image source, string path, IImageEncoder encoder) { 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); } /// @@ -72,10 +70,8 @@ public static async Task SaveAsync( 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 53d6028295..0dc6d26bc7 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 3a3c81b52c..aa88242ce4 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -37,8 +37,7 @@ public void ParseStream() { 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 0b977bfbc8..aed3e4c03e 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 ce2d1625f6..dd9b55e58d 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -208,8 +208,7 @@ public void ImageSharpResize(string input) 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 a3419eb27d..3853c445f7 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 void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeO [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 void ConstructorCallConfigureOnFormatProvider() [Fact] public void AddFormatCallsConfig() { - var provider = new Mock(); + var provider = new Mock(); var config = new Configuration(); config.Configure(provider.Object); @@ -178,7 +179,7 @@ static void RunTest() } } - 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 4bc59a1e1b..e6eb389be7 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 void BmpDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce 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 void BmpDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce 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 void BmpDecoder_CanDecodeBitfields(TestImageProvider prov 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 void BmpDecoder_CanDecode_Inverted(TestImageProvider prov 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 void BmpDecoder_CanDecode_1Bit(TestImageProvider provider 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 void BmpDecoder_CanDecode_2Bit(TestImageProvider provider 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 void BmpDecoder_CanDecode_4Bit(TestImageProvider provider 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 void BmpDecoder_CanDecode_8Bit(TestImageProvider provider 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 void BmpDecoder_CanDecode_16Bit(TestImageProvider provide 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 void BmpDecoder_CanDecode_32Bit(TestImageProvider provide 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 void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma 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 void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider 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 void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRe 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 void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecode 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 void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider } 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 void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvide } 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 void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvide 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 void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider 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 void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvide 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 void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageP 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 void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider 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 void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider 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 void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider(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 void BmpDecoder_ThrowsInvalidImageContentException_OnInvalidPaletteSize => Assert.Throws(() => { - using (provider.GetImage(BmpDecoder)) + using (provider.GetImage(BmpDecoder.Instance)) { } }); @@ -384,7 +382,7 @@ public void BmpDecoder_ThrowsNotSupportedException_OnUnsupportedBitmaps( where TPixel : unmanaged, IPixel => Assert.Throws(() => { - using (provider.GetImage(BmpDecoder)) + using (provider.GetImage(BmpDecoder.Instance)) { } }); @@ -394,7 +392,7 @@ public void BmpDecoder_ThrowsNotSupportedException_OnUnsupportedBitmaps( 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 void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider pro 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 void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider(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 void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider 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 void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider 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 void BmpDecoder_RespectsFileHeaderOffset(TestImageProvider(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 void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider(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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio { 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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio 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 void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider(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 void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider p 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 8743e77bd3..f486310b78 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 void Encode_2Bit_WithV3Header_Works( // 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 void Encode_2Bit_WithV4Header_Works( // 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 void Encode_8BitColor_WithWuQuantizer(TestImageProvider p // 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 void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider 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 void Encode_PreservesAlpha(TestImageProvider provider, Bm 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 adfd926b78..f4ce546708 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -51,7 +51,7 @@ public void Identify_DetectsCorrectBitmapInfoHeaderType(string imagePath, BmpInf 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 172821acc9..c9e50337e8 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -7,6 +7,7 @@ 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 void ResolutionShouldChange(TestImageProvider provider) } [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 void ReadOriginIsRespectedOnLoad() } [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 async Task ReadOriginIsRespectedOnLoadAsync() 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 2a9d2b791d..9ddae6645c 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 void GifDecoder_Decode_Resize(TestImageProvider provider) 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 unsafe void Decode_NonTerminatedFinalFrame() 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 void CanDecodeJustOneFrame(TestImageProvider provider) 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 void CanDecodeJustOneFrame(TestImageProvider provider) 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 void Decode_WithInvalidDimensions_DoesThrowException(TestImagePro 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 void Decode_WithInvalidDimensions_DoesThrowException(TestImagePro 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 void GifDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce 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 @@ TestImageProvider provider 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 2d66842e88..24eaa9607b 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -56,7 +56,7 @@ public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { 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 void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() 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 void Decode_CanDecodeLargeTextComment() { 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 void Decode_CanDecodeLargeTextComment() [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 void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut { 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 async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, i { 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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio { 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 async Task Decode_VerifyRatioAsync(string imagePath, int xResolution, int { 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 void Identify_VerifyRepeatCount(string imagePath, uint repeatCount) { 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 void Decode_VerifyRepeatCount(string imagePath, uint repeatCount) { 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 06fe2601cc..d276f7eb6d 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -102,8 +102,8 @@ public void RegisterMimeTypeDecoderReplacesLast() [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 void AddFormatCallsConfig() 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 f8a562c4c8..f147e41325 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 3603000f6c..d972f539ee 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -65,7 +65,7 @@ public void MetadataIsParsedCorrectly( bool exifProfilePresent, bool iccProfilePresent) => TestMetadataImpl( useIdentify, - JpegDecoder, + JpegDecoder.Instance, imagePath, expectedPixelSize, exifProfilePresent, @@ -77,8 +77,7 @@ public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio { 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 void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut { 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 async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, i { 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 void Identify_VerifyQuality(string imagePath, int quality) { 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 void Decode_VerifyQuality(string imagePath, int quality) { 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 async Task Decode_VerifyQualityAsync(string imagePath, int quality) { 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 void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor { 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 void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor 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 @@ private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool 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 void IgnoreMetadata_ControlsWhetherMetadataIsParsed(bool ignoreMetadata) 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 void IgnoreMetadata_ControlsWhetherMetadataIsParsed(bool ignoreMetadata) [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 void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) => Test [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 void Decode_WithInvalidIptcTag_DoesNotThrowException(TestImagePro { 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 void Clone_WithNullRationalArrayTag_DoesNotThrowException(TestIma { 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 8e07d9ac91..a5472e1aef 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 void DecodeProgressiveJpeg(TestImageProvider provider) 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 deca7f69fa..18b4e1fba9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -8,7 +8,6 @@ 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 @@ private static bool SkipTest(ITestImageProvider provider) private ITestOutputHelper Output { get; } - private static JpegDecoder JpegDecoder => new(); - [Fact] public void ParseStream_BasicPropertiesAreCorrect() { @@ -105,7 +102,7 @@ public async Task DecodeAsync_NonGeneric_CreatesRgb24Image() 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 void JpegDecoder_Decode_Resize(TestImageProvider provider 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 void JpegDecoder_Decode_Resize_Bicubic(TestImageProvider 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 void JpegDecoder_Decode_Specialized_IDCT_Resize(TestImageProvider 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 void JpegDecoder_Decode_Specialized_Scale_Resize(TestImageProvide 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 void JpegDecoder_Decode_Specialized_Combined_Resize(TestImageProv 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 void Decode_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExceptio 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 async Task DecodeAsync_DegenerateMemoryRequest_ShouldTranslateTo_ImageFor 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 void ThrowsNotSupported_WithUnsupportedJpegs(TestImageProvider(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 void Issue1732_DecodesWithRgbColorSpace(TestImageProvider 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 void Issue2057_DecodeWorks(TestImageProvider provider) 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 void Issue2133_DeduceColorSpace(TestImageProvider provide 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 7e8f13ed3d..2b721b9b51 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -100,10 +100,11 @@ public void Encode_WithValidExifProfile_DoesNotThrowException(TestImageP 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 void Encode_PreservesColorType(TestImageProvider provider 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 0e92be4285..61c01b15c4 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 2e00ea8eb0..5bdab7b37a 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs @@ -108,7 +108,7 @@ public void PbmDecoder_Decode_Resize(TestImageProvider provider) 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 5a23c60378..aff8bc12a2 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -70,10 +70,8 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType 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 c83b97a88d..2e1785cbb9 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 async Task DecodeAsync_NonGeneric_CreatesCorrectImageType(string path, Ty 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 void PngDecoder_Decode_Resize(TestImageProvider provider) 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 void PngDecoder_Decode_Resize(TestImageProvider provider) 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 void Decode_WithAverageFilter(TestImageProvider provider) 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 void Decode_WithSubFilter(TestImageProvider provider) 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 void Decode_WithUpFilter(TestImageProvider provider) 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 void Decode_WithPaethFilter(TestImageProvider provider) 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 void Decode_GrayWithAlpha(TestImageProvider provider) 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 void Decode_Interlaced(TestImageProvider provider) 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 void Decode_Indexed(TestImageProvider provider) 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 void Decode_48Bpp(TestImageProvider provider) 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 void Decode_64Bpp(TestImageProvider provider) 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 void Decoder_L8bitInterlaced(TestImageProvider provider) 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 void Decode_L16Bit(TestImageProvider provider) 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 void Decode_GrayAlpha16Bit(TestImageProvider provider) 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 void Decoder_CanDecode_Grey8bitInterlaced_WithAlpha(TestImageProv 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 void Decoder_CanDecode_CorruptedImages(TestImageProvider 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 void Decode_MissingDataChunk_ThrowsException(TestImageProvider { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -335,7 +333,7 @@ public void Decode_MissingPaletteChunk_ThrowsException(TestImageProvider 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 void Decode_InvalidGammaChunk_Ignored(TestImageProvider p 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 void Decode_InvalidBitDepth_ThrowsException(TestImageProvider { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -381,7 +379,7 @@ public void Decode_InvalidColorType_ThrowsException(TestImageProvider { - using Image image = provider.GetImage(PngDecoder); + using Image image = provider.GetImage(PngDecoder.Instance); image.DebugSave(provider); }); Assert.NotNull(ex); @@ -396,7 +394,7 @@ public void Decode_InvalidDataChunkCrc_ThrowsException(TestImageProvider 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 void Issue1014_DataSplitOverMultipleIDatChunks(TestImageProvider< 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 void Issue1177_CRC_Omitted(TestImageProvider provider) 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 void Issue1127(TestImageProvider provider) 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 void Issue1047(TestImageProvider provider) 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 void Issue1765(TestImageProvider provider) 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 void Issue1765(TestImageProvider provider) 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 void Issue410_MalformedApplePng(TestImageProvider provide 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 void PngDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 2656144d30..74038c7616 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -284,9 +284,7 @@ public void InfersColorTypeAndBitDepth(TestImageProvider provide 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 void EncodeFixesInvalidOptions() // 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 @@ private static void TestPngEncoderCore( // 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 a0d2423aab..d283efc00b 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -52,7 +52,7 @@ public void CloneIsDeep() 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 void Decoder_CanReadTextData(TestImageProvider provider) 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 void Encoder_PreservesTextData(TestImageProvider provider 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 void Decoder_IgnoresInvalidTextData(TestImageProvider pro 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 void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works(T }); 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 void Decode_ReadsExifData(TestImageProvider provider) 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 void Decode_IgnoresExifData_WhenIgnoreMetadataIsTrue(TestImagePro 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 void Decode_IgnoreMetadataIsFalse_TextChunkIsRead() 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 void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() 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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio { 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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio 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 void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut { 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 8bc64362dd..a4288a3d84 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -24,7 +24,7 @@ public void GeneralTest(TestImageProvider provider) // 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 void Resize(TestImageProvider provider) // 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 b96e284b92..3c9a2f8261 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 void TgaDecoder_CanDecode_Gray_WithTopLeftOrigin_8Bit(TestImagePr 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 void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_8Bit(TestImag 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 void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_8Bit(TestImageP 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 void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_8Bit(TestIma 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 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); @@ -96,7 +94,7 @@ 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); @@ -108,7 +106,7 @@ public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_8Bit 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 void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_8Bi 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 void TgaDecoder_CanDecode_Gray_16Bit(TestImageProvider pr 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 void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_16Bit(TestIma 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 void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_16Bit(TestIm 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 void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_16Bit(TestImage 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 void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImagePr 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 void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_16Bi 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 void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_16B 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 void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_16Bit< 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 void TgaDecoder_CanDecode_15Bit(TestImageProvider provide 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 void TgaDecoder_CanDecode_RunLengthEncoded_15Bit(TestImageProvide 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 void TgaDecoder_CanDecode_WithBottomLeftOrigin_16Bit(TestImagePro 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 void TgaDecoder_CanDecode_RunLengthEncoded_WithPalette_16Bit(Test 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 void TgaDecoder_CanDecode_WithTopLeftOrigin_24Bit(TestImageProvid 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 void TgaDecoder_CanDecode_WithBottomLeftOrigin_24Bit(TestImagePro 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 void TgaDecoder_CanDecode_WithTopRightOrigin_24Bit(TestImageProvi 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 void TgaDecoder_CanDecode_WithBottomRightOrigin_24Bit(TestImagePr 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 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); @@ -360,7 +358,7 @@ 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); @@ -372,7 +370,7 @@ 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); @@ -384,7 +382,7 @@ public void TgaDecoder_CanDecode_Palette_WithTopLeftOrigin_24Bit(TestIma 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 void TgaDecoder_CanDecode_WithTopLeftOrigin_32Bit(TestImageProvid 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 void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvi 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 void TgaDecoder_CanDecode_WithBottomLeftOrigin_32Bit(TestImagePro 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 void TgaDecoder_CanDecode_WithBottomRightOrigin_32Bit(TestImagePr 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 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); @@ -456,7 +454,7 @@ 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); @@ -468,7 +466,7 @@ 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); @@ -480,7 +478,7 @@ 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); @@ -492,7 +490,7 @@ 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); @@ -504,7 +502,7 @@ 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); @@ -516,7 +514,7 @@ public void TgaDecoder_CanDecode_RLE_Paletted_WithTopLeftOrigin_32Bit(Te 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 void TgaDecoder_CanDecode_RLE_Paletted_WithBottomLeftOrigin_32Bit 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 void TgaDecoder_CanDecode_RLE_WithTopRightOrigin_32Bit(TestImageP 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 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); @@ -564,7 +562,7 @@ public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit(TestI 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 void TgaDecoder_CanDecode_WithPaletteTopLeftOrigin_24Bit(TestImag 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 void TgaDecoder_CanDecode_WithPaletteTopRightOrigin_24Bit(TestIma 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 void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_24Bit(TestI 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 void TgaDecoder_CanDecode_WithPaletteBottomRightOrigin_24Bit(Test 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 void TgaDecoder_CanDecode_RLE_WithPaletteTopLeftOrigin_24Bit(Test 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 void TgaDecoder_CanDecode_RLE_WithPaletteTopRightOrigin_24Bit(Tes 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 void TgaDecoder_CanDecode_RLE_WithPaletteBottomLeftOrigin_24Bit(T 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 void TgaDecoder_CanDecode_RLE_WithPaletteBottomRightOrigin_24Bit( 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 void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider(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 void TgaDecoder_CanDecode_WithPalette_WithBottomLeftOrigin_32Bit( 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 void TgaDecoder_CanDecode_WithPalette_WithBottomRightOrigin_32Bit 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 void TgaDecoder_CanDecode_WithPalette_WithTopRightOrigin_32Bit(Te 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 void TgaDecoder_CanDecode_WhenAlphaBitsNotSet_16Bit(TestImageProv 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 void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider(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 void TgaDecoder_Decode_Resize(TestImageProvider provider) 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 void TgaDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce 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 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) 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 62ca6da3de..29017a6747 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -39,7 +39,7 @@ public void DamagedFiles(TestImageProvider provider) { 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 void ByteOrder(string imagePath, ByteOrder expectedByteOrder) 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 95f37ba407..4acdf3e509 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 ba5b77baf3..ced8ed6dda 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 void TiffDecoder_CanDecode_12Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_20Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_24Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_32Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_40Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_48Bit_WithAssociatedAlpha(TestImagePro 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 void TiffDecoder_CanDecode_56Bit_WithAssociatedAlpha(TestImagePro 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 void CanDecodeJustOneFrame(TestImageProvider provider) 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 void TiffDecoder_CanDecode_OldJpegCompressed(TestImageProvider image = provider.GetImage(TiffDecoder, decoderOptions); + using Image image = provider.GetImage(TiffDecoder.Instance, decoderOptions); image.DebugSave(provider); image.CompareToOriginal( provider, @@ -723,7 +723,7 @@ public void TiffDecoder_ThrowsException_WithTooManyDirectories(TestImage where TPixel : unmanaged, IPixel => Assert.Throws( () => { - using (provider.GetImage(TiffDecoder)) + using (provider.GetImage(TiffDecoder.Instance)) { } }); @@ -739,7 +739,7 @@ public void TiffDecoder_CanDecode_Fax4CompressedWithStrips(TestImageProv 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 void TiffDecoder_Decode_Resize(TestImageProvider provider 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 2c30cc3d05..97445ad6cf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -295,7 +295,7 @@ public void TiffEncoder_EncodesWithCorrectBiColorModeCompression(TestIma [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 ffe4c573b3..f882bf4155 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 void TiffMetadata_CloneIsDeep() 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 void MetadataProfiles(TestImageProvider provider, bool ig 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 void MetadataProfiles(TestImageProvider provider, bool ig 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 void CanDecodeImage_WithIptcDataAsLong(TestImageProvider 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 void BaselineTags(TestImageProvider provider) 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 void Encode_PreservesMetadata(TestImageProvider provider) { // 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 55c6233654..694b8c23e1 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 void Identify_DetectsCorrectDimensionsAndBitDepth( 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 void WebpDecoder_CanDecode_Lossy_WithoutFilter(TestImageProvider< 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 void WebpDecoder_CanDecode_Lossy_WithSimpleFilter(TestImageProvid 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 void WebpDecoder_CanDecode_Lossy_WithComplexFilter(TestImageProvi 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 void WebpDecoder_CanDecode_Lossy_VerySmall(TestImageProvider(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 void WebpDecoder_CanDecode_Lossy_WithPartitions(TestImageProvider 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 void WebpDecoder_CanDecode_Lossy_WithSegmentation(TestImageProvid 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 void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel(TestImageProv 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 void WebpDecoder_CanDecode_Lossy_WithAlpha(TestImageProvider(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 void WebpDecoder_CanDecode_Lossless_WithAlpha(TestImageProvider(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 void WebpDecoder_CanDecode_Lossless_WithSubtractGreenTransform( 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 void WebpDecoder_CanDecode_Lossless_WithSubtractGreenTransform( 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 void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform(TestI 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 void WebpDecoder_CanDecode_Lossless_WithPredictorTransform(TestIm 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 void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform(TestI 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 void WebpDecoder_CanDecode_Lossless_WithTwoTransforms(TestImagePr 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 void WebpDecoder_CanDecode_Lossless_WithThreeTransforms(TestImage 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 void Decode_AnimatedLossless_VerifyAllFrames(TestImageProvider(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 void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFra 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 void WebpDecoder_CanDecode_Lossless_WithIssues(TestImageProvider< 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 void WebpDecoder_Decode_Resize(TestImageProvider provider 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 void WebpDecoder_Decode_Resize(TestImageProvider provider 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 void WebpDecoder_CanDecode_Issue1594(TestImageProvider pr 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 void WebpDecoder_CanDecode_Issue2243(TestImageProvider pr 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 void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestIm Assert.Throws( () => { - using (provider.GetImage(WebpDecoder)) + using (provider.GetImage(WebpDecoder.Instance)) { } }); @@ -422,7 +420,7 @@ public void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestIm 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 @@ private static void RunDecodeLossyWithHorizontalFilter() 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 @@ private static void RunDecodeLossyWithVerticalFilter() 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 @@ private static void RunDecodeLossyWithSimpleFilterTest() 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 566b7e4dd2..c0c2e375f4 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 void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLossyImage(Te 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 void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLosslessImage 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 void IgnoreMetadata_ControlsWhetherIccpIsParsed(TestImageProvider 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 async Task IgnoreMetadata_ControlsWhetherXmpIsParsed(TestImagePro 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 void EncodeLossyWebp_PreservesExif(TestImageProvider prov 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 void EncodeLosslessWebp_PreservesExif(TestImageProvider p 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 void EncodeLosslessWebp_PreservesExif(TestImageProvider p 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 d28b2ee40d..258ee5b9f7 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 14d65fbb96..d8d9f4fe2a 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 cace719bd9..15e57b48a3 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -59,14 +59,16 @@ protected ImageLoadTestBase() 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 @@ protected ImageLoadTestBase() .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 @@ protected ImageLoadTestBase() }) .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 e9b64d3b2c..4b1f6e1568 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 @@ public NonSeekableStream(Stream dataStream) 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 8606dae54f..1a52ade629 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 void IptcProfile_SetTimeValue_Works(IptcTag tag) 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 void ReadIptcMetadata_FromJpg_Works(TestImageProvider pro 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 @@ private static void IptcProfileContainsExpectedValues(List iptcValues 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 void IptcValue_CloneIsDeep() 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 8ef31a2af2..5dc6ac6db7 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 async Task ReadXmpMetadata_FromGif_Works(TestImageProvider(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 async Task ReadXmpMetadata_FromJpg_Works(TestImageProvider(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 async Task ReadXmpMetadata_FromPng_Works(TestImageProvider(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 async Task ReadXmpMetadata_FromTiff_Works(TestImageProvider(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 void XmpProfile_CloneIsDeep() 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 void WritingGif_PreservesXmpProfile() 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 async Task WritingJpeg_PreservesExtendedXmpProfile() { // 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 async Task WritingJpeg_PreservesExtendedXmpProfile() 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 void WritingPng_PreservesXmpProfile() 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 void WritingTiff_PreservesXmpProfile() 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 9d920d7189..a53e508067 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 string GetFileNameWithoutExtension(object value) /// /// 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 Image CreateRgba32Image(IImageDecoder decoder) /// /// 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 127ccd32df..f3176f16c5 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 IImageFormat DetectFormat(ReadOnlySpan header) 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 TestDecoder : IImageDecoderSpecialized 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 Image Decode(TestDecoderOptions options, Stream stream, C 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 TestEncoder : IImageEncoder 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 void Encode(Image image, Stream stream) } 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 0000000000..ec9b2e7e17 --- /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 a0f544b2fa..3285da31b4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -202,7 +202,7 @@ public override Image GetImage(IImageDecoder decoder, DecoderOptions opt 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 override Task> GetImageAsync(IImageDecoder decoder, Decoder // 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 override Image GetImage(IImageDecoderSpecialized decoder, T 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 override Task> GetImageAsync(IImageDecoderSpecialized // 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 @@ private Image DecodeImage(IImageDecoder decoder, DecoderOptions options) 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 8cd04f1cd1..8f22fb2b29 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -99,17 +99,18 @@ public virtual Image GetImage(IImageDecoder decoder, DecoderOptions opti 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 0000000000..ae4af24f14 --- /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 2d13de0745..3c780f3474 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 PausedStream(string filePath) 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 PausedStream(string filePath) 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 PausedStream(string filePath) 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 1290fdea3a..a4d305d97f 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 3392d68145..7203116c93 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; -public class MagickReferenceDecoder : IImageDecoder +public class MagickReferenceDecoder : ImageDecoder { private readonly bool validate; @@ -24,24 +24,25 @@ public MagickReferenceDecoder() 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 Image Decode(DecoderOptions options, Stream stream, Cance 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 @@ private static void FromRgba32Bytes(Configuration configuration, 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 @@ private static void FromRgba64Bytes(Configuration configuration, Span 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 Image Decode(DecoderOptions options, Stream stream, Cance 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 af13d64ce2..d8dda2eea8 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 89b43a0661..68c1664a8f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -53,7 +53,7 @@ private static void ConfigureCodecs( 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 cd16fe4b2d..31c9f541ea 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 Image GetReferenceOutputImage( 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 Image GetReferenceOutputImageMultiFrame( { 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 Image CompareToOriginal( 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 Image CompareToOriginalMultiFrame( 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 @@ internal static string VerifyEncoder( 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 3d4dd4a65b..81ea77b6b6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -40,8 +40,8 @@ public void MagickDecode_8BitDepthImage_IsEquivalentTo_SystemDrawingResult 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 void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawi 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 91df7d1496..a89feb3c35 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -93,7 +93,7 @@ public void OpenWithReferenceDecoder(TestImageProvider dummyProv { 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 5889907f09..13edd2a062 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -346,7 +346,7 @@ private static void EnsureCustomConfigurationIsApplied(TestImageProvider } } - 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 static void DoTestThreadSafe(Action action) } } - 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 @@ internal void InitCaller(string name) } } - private class TestDecoderWithParameters : IImageDecoderSpecialized + private class TestDecoderWithParameters : SpecializedImageDecoder { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -408,26 +403,21 @@ public static void DoTestThreadSafe(Action action) } } - 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 @@ internal void InitCaller(string name) 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(); } }