diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 5e8b899a4d..1732a6bdbf 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Advanced } /// - /// This method pre-seeds the all in the AoT compiler. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. [Preserve] diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 3474606828..5b0e803f39 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -10,31 +10,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Image decoder for generating an image out of a Windows bitmap stream. /// - public class BmpDecoder : ImageDecoder + public class BmpDecoder : ImageDecoder, IImageDecoderSpecialized { /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - BmpDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); - - return image; + return new BmpDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new BmpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = new BmpDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); + + return image; } + + /// + public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index d9e2c17b8e..1adc34849e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// A useful decoding source example can be found at /// - internal sealed class BmpDecoderCore : IImageDecoderInternals + internal sealed class BmpDecoderCore : IImageDecoderInternals { /// /// The default mask for the red part of the color for 16 bit rgb bitmaps. @@ -99,19 +99,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private readonly MemoryAllocator memoryAllocator; + /// + /// How to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. + /// + private readonly RleSkippedPixelHandling rleSkippedPixelHandling; + /// /// Initializes a new instance of the class. /// /// The options. public BmpDecoderCore(BmpDecoderOptions options) { - this.Options = options; + this.Options = options.GeneralOptions; + this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public BmpDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); @@ -322,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte colorIdx = bufferRow[x]; if (undefinedPixelsSpan[rowStartIdx + x]) { - switch (this.Options.RleSkippedPixelHandling) + switch (this.rleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); @@ -394,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int idx = rowStartIdx + (x * 3); if (undefinedPixelsSpan[yMulWidth + x]) { - switch (this.Options.RleSkippedPixelHandling) + switch (this.rleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 5115486a45..3729e42084 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : ImageDecoder + public sealed class GifDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - GifDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new GifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new GifDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + GifDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index ff4b97b3c8..b307ffd60f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Performs the gif decoding operation. /// - internal sealed class GifDecoderCore : IImageDecoderInternals + internal sealed class GifDecoderCore : IImageDecoderInternals { /// /// The temp buffer used to reduce allocations. @@ -90,17 +90,17 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Initializes a new instance of the class. /// /// The decoder options. - public GifDecoderCore(GifDecoderOptions options) + public GifDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public GifDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); diff --git a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs deleted file mode 100644 index 3c5f7bddc1..0000000000 --- a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Gif -{ - /// - /// Configuration options for decoding Gif images. - /// - public sealed class GifDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index cdec1e9289..b0363ccf9f 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -15,6 +15,9 @@ namespace SixLabors.ImageSharp.Formats /// /// 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. @@ -27,6 +30,9 @@ namespace SixLabors.ImageSharp.Formats /// /// 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. diff --git a/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs similarity index 88% rename from src/ImageSharp/Formats/IImageDecoderInternals{T}.cs rename to src/ImageSharp/Formats/IImageDecoderInternals.cs index 27b8a434b4..63596266fb 100644 --- a/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs +++ b/src/ImageSharp/Formats/IImageDecoderInternals.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; @@ -11,14 +11,12 @@ namespace SixLabors.ImageSharp.Formats /// /// Abstraction for shared internals for XXXDecoderCore implementations to be used with . /// - /// The type of specialized decoder options. - internal interface IImageDecoderInternals - where T : ISpecializedDecoderOptions + internal interface IImageDecoderInternals { /// - /// Gets the specialized decoder options. + /// Gets the general decoder options. /// - T Options { get; } + DecoderOptions Options { get; } /// /// Gets the dimensions of the image being decoded. diff --git a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs new file mode 100644 index 0000000000..46ad284da3 --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all specialized image decoders. + /// + /// The type of specialized options. + public interface IImageDecoderSpecialized : IImageDecoder + where T : ISpecializedDecoderOptions + { + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 443e8b9809..1bc8a44092 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -14,6 +14,9 @@ namespace SixLabors.ImageSharp.Formats /// /// 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. diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs new file mode 100644 index 0000000000..4863f5b67d --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all image decoders. + /// + public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder + { + /// + public abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + /// Performs a resize operation against the decoded image. If the target size is not set, or the image size + /// already matches the target size, the image is untouched. + /// + /// The decoder options. + /// The decoded image. + protected static void Resize(DecoderOptions options, Image image) + { + if (ShouldResize(options, image)) + { + ResizeOptions resizeOptions = new() + { + Size = options.TargetSize.Value, + Sampler = KnownResamplers.Box, + 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; + } + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs b/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs new file mode 100644 index 0000000000..d23f088aaa --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Extensions methods for . + /// + public static class ImageDecoderSpecializedExtensions + { + /// + /// 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 DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) + where T : ISpecializedDecoderOptions + where TPixel : unmanaged, IPixel + => decoder.DecodeSpecialized(options, stream, 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 DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) + where T : ISpecializedDecoderOptions + => decoder.DecodeSpecialized(options, stream, 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> DecodeSpecializedAsync(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.DecodeSpecialized(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 DecodeSpecializedAsync(this IImageDecoderSpecialized decoder, T options, Stream stream, CancellationToken cancellationToken = default) + where T : ISpecializedDecoderOptions + => Image.WithSeekableStreamAsync( + options.GeneralOptions, + stream, + (s, ct) => decoder.DecodeSpecialized(options, s, ct), + cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index 1dc0841f28..b6cefd2c89 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -12,12 +12,11 @@ namespace SixLabors.ImageSharp.Formats { internal static class ImageDecoderUtilities { - public static IImageInfo Identify( - this IImageDecoderInternals decoder, + public static IImageInfo Identify( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions { using var bufferedReadStream = new BufferedReadStream(configuration, stream); @@ -31,22 +30,20 @@ namespace SixLabors.ImageSharp.Formats } } - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel - => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); + => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, Func largeImageExceptionFactory, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel { using var bufferedReadStream = new BufferedReadStream(configuration, stream); diff --git a/src/ImageSharp/Formats/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs deleted file mode 100644 index 6f43b053f9..0000000000 --- a/src/ImageSharp/Formats/ImageDecoder{T}.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// The base class for all image decoders. - /// - /// The type of specialized decoder options. - public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder - where T : class, ISpecializedDecoderOptions, new() - { - /// - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - T specializedOptions = new() { GeneralOptions = options }; - return this.IdentifySpecialized(specializedOptions, stream, cancellationToken); - } - - /// - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - { - T specializedOptions = new() { GeneralOptions = options }; - Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); - - Resize(options, image); - - return image; - } - - /// - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - T specializedOptions = new() { GeneralOptions = options }; - Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); - - Resize(options, image); - - return image; - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The object. - /// Thrown if the encoded image contains errors. - public abstract IImageInfo IdentifySpecialized(T options, Stream stream, CancellationToken cancellationToken); - - /// - /// 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. - /// The . - /// Thrown if the encoded image contains errors. - public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// Thrown if the encoded image contains errors. - public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); - - /// - /// 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. - protected static void Resize(DecoderOptions options, Image image) - { - if (ShouldResize(options, image)) - { - ResizeOptions resizeOptions = new() - { - Size = options.TargetSize.Value, - Sampler = KnownResamplers.Box, - 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; - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 1675fd6d74..a655acbce0 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -10,38 +10,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Decoder for generating an image out of a jpeg encoded stream. /// - public sealed class JpegDecoder : ImageDecoder + public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized { /// - /// - /// Unlike , when - /// is passed, the codec may not be able to scale efficiently to - /// the exact scale factor requested, so returns a size that approximates that scale. - /// Upscaling is not supported, so the original size will be returned. - /// - public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using JpegDecoderCore decoder = new(options); - return decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + Guard.NotNull(stream, nameof(stream)); + + using JpegDecoderCore decoder = new(new() { GeneralOptions = options }); + return decoder.Identify(options.Configuration, stream, cancellationToken); } /// - /// - /// Unlike , when - /// is passed, the codec may not be able to scale efficiently to - /// the exact scale factor requested, so returns a size that approximates that scale. - /// Upscaling is not supported, so the original size will be returned. - /// - public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override IImageInfo IdentifySpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); using JpegDecoderCore decoder = new(options); - return decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) + { + Resize(options.GeneralOptions, image); + } + + return image; } + + /// + public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 30af0b4e26..18201d49b3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals + internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals { /// /// The only supported precision @@ -125,19 +125,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private readonly bool skipMetadata; + /// + /// The jpeg specific resize options. + /// + private readonly JpegDecoderResizeMode resizeMode; + /// /// Initializes a new instance of the class. /// /// The decoder options. public JpegDecoderCore(JpegDecoderOptions options) { - this.Options = options; + this.Options = options.GeneralOptions; + this.resizeMode = options.ResizeMode; this.configuration = options.GeneralOptions.Configuration; this.skipMetadata = options.GeneralOptions.SkipMetadata; } /// - public JpegDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => this.Frame.PixelSize; @@ -207,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - using var spectralConverter = new SpectralConverter(this.configuration, this.Options.GeneralOptions.TargetSize); + using var spectralConverter = new SpectralConverter(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); this.ParseStream(stream, spectralConverter, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs index 80e680cd0e..95d1c81d23 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public sealed class JpegDecoderOptions : ISpecializedDecoderOptions { + /// + /// Gets or sets the resize mode. + /// + public JpegDecoderResizeMode ResizeMode { get; set; } + /// public DecoderOptions GeneralOptions { get; set; } = new(); } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs new file mode 100644 index 0000000000..d99d522c5b --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Jpeg +{ + /// + /// Provides enumeration for resize modes taken during decoding. + /// Applicable only when has a value. + /// + public enum JpegDecoderResizeMode + { + /// + /// Both and . + /// + Combined, + + /// + /// IDCT-only to nearest block scale. + /// + IdctOnly, + + /// + /// Opt-out the IDCT part and only Resize. Can be useful in case of quality concerns. + /// + ScaleOnly + } +} diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs index 34dca4ca4e..1127f8e090 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs @@ -26,29 +26,33 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// The specification of these images is found at . /// - public sealed class PbmDecoder : ImageDecoder + public sealed class PbmDecoder : ImageDecoder { - /// - public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) + /// + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PbmDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new PbmDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new PbmDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + PbmDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index cac1a9c640..05f06deaae 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// Performs the PBM decoding operation. /// - internal sealed class PbmDecoderCore : IImageDecoderInternals + internal sealed class PbmDecoderCore : IImageDecoderInternals { private int maxPixelValue; @@ -52,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// Initializes a new instance of the class. /// /// The decoder options. - public PbmDecoderCore(PbmDecoderOptions options) + public PbmDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; } /// - public PbmDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => this.pixelSize; diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs deleted file mode 100644 index 1253d72b39..0000000000 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Pbm -{ - /// - /// Configuration options for decoding Pbm images. - /// - public sealed class PbmDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 5771e5b09a..9f23982d09 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -10,24 +10,39 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Decoder for generating an image out of a png encoded stream. /// - public sealed class PngDecoder : ImageDecoder + public sealed class PngDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new PngDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + PngDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options.GeneralOptions, image); + Resize(options, image); return image; } /// - public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + PngDecoderCore decoder = new(options, true); - IImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + IImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken); stream.Position = 0; PngMetadata meta = info.Metadata.GetPngMetadata(); @@ -39,50 +54,42 @@ namespace SixLabors.ImageSharp.Formats.Png if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Palette: - return this.DecodeSpecialized(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); default: - return this.DecodeSpecialized(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); } } - - /// - public override IImageInfo IdentifySpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - Guard.NotNull(stream, nameof(stream)); - - return new PngDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); - } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 8d6a1719e4..0ec937a207 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Performs the png decoding operation. /// - internal sealed class PngDecoderCore : IImageDecoderInternals + internal sealed class PngDecoderCore : IImageDecoderInternals { /// /// Reusable buffer. @@ -123,25 +123,25 @@ namespace SixLabors.ImageSharp.Formats.Png /// Initializes a new instance of the class. /// /// The decoder options. - public PngDecoderCore(PngDecoderOptions options) + public PngDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; } - internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) + internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly) { this.Options = options; this.colorMetadataOnly = colorMetadataOnly; this.skipMetadata = true; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public PngDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.header.Width, this.header.Height); diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs deleted file mode 100644 index 722d803254..0000000000 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png -{ - /// - /// Configuration options for decoding Png images. - /// - public sealed class PngDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index ca4d9a5f55..2ad845adb1 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Image decoder for Truevision TGA images. /// - public sealed class TgaDecoder : ImageDecoder + public sealed class TgaDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - TgaDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new TgaDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new TgaDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + TgaDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 056aa9e840..830f2cc3df 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Performs the tga decoding operation. /// - internal sealed class TgaDecoderCore : IImageDecoderInternals + internal sealed class TgaDecoderCore : IImageDecoderInternals { /// /// A scratch buffer to reduce allocations. @@ -61,15 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Tga /// Initializes a new instance of the class. /// /// The options. - public TgaDecoderCore(TgaDecoderOptions options) + public TgaDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public TgaDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs deleted file mode 100644 index 89ec90430d..0000000000 --- a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Tga -{ - /// - /// Configuration options for decoding Tga images. - /// - public sealed class TgaDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs index ced6b9027c..5d09bd7905 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs @@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision); } - /// + /// /// This converter must be used only for RGB and YCbCr color spaces for performance reasons. /// For grayscale images must be used. - /// + /// private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation) => interpretation switch { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 68d27262f8..012adc5863 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : ImageDecoder + public class TiffDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - TiffDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new TiffDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new TiffDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + TiffDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public 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 6b3abbc9e7..8e3f8c16b7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the tiff decoding operation. /// - internal class TiffDecoderCore : IImageDecoderInternals + internal class TiffDecoderCore : IImageDecoderInternals { /// /// General configuration options. @@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Initializes a new instance of the class. /// /// The decoder options. - public TiffDecoderCore(TiffDecoderOptions options) + public TiffDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffPredictor Predictor { get; set; } /// - public TiffDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions { get; private set; } @@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Options.GeneralOptions, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -454,7 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Options.GeneralOptions, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs deleted file mode 100644 index b22cf001e1..0000000000 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// Configuration options for decoding Tiff images. - /// - public sealed class TiffDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index dc653705d2..b2dbbcba98 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -10,29 +10,34 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Image decoder for generating an image out of a webp stream. /// - public sealed class WebpDecoder : ImageDecoder + public sealed class WebpDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - WebpDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + using WebpDecoderCore decoder = new(options); + return decoder.Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new WebpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + using WebpDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index ecb7b1b87d..75f5f88f8b 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Performs the webp decoding operation. /// - internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable + internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable { /// /// Reusable buffer. @@ -76,17 +76,17 @@ namespace SixLabors.ImageSharp.Formats.Webp /// Initializes a new instance of the class. /// /// The decoder options. - public WebpDecoderCore(WebpDecoderOptions options) + public WebpDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public WebpDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs deleted file mode 100644 index 8e8218472f..0000000000 --- a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp -{ - /// - /// Configuration options for decoding Webp images. - /// - public sealed class WebpDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index f7ff9966d5..5c73590aa1 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -521,7 +521,7 @@ namespace SixLabors.ImageSharp /// The input stream. /// The action to perform. /// The . - private static T WithSeekableStream( + internal static T WithSeekableStream( DecoderOptions options, Stream stream, Func action) @@ -562,7 +562,7 @@ namespace SixLabors.ImageSharp /// The action to perform. /// The cancellation token. /// The . - private static async Task WithSeekableStreamAsync( + internal static async Task WithSeekableStreamAsync( DecoderOptions options, Stream stream, Func action, diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 1c8977f5e3..5df2267703 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -65,7 +65,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } } - /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1348 (20H2/October2020Update) Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index eecdb4bb97..96fcb6afd1 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : ImageDecoder + public class TestDecoder : ImageDecoder, IImageDecoderSpecialized { private readonly TestFormat testFormat; @@ -208,10 +208,17 @@ namespace SixLabors.ImageSharp.Tests public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { Configuration configuration = options.GeneralOptions.Configuration; var ms = new MemoryStream(); @@ -228,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests return this.testFormat.Sample(); } - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 1fc0100a69..6cc96dedeb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests return Task.FromResult(decoder.Decode(options, stream, default)); } - public override Image GetImage(ImageDecoder decoder, T options) + public override Image GetImage(IImageDecoderSpecialized decoder, T options) { Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); @@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests return cachedImage.Clone(this.Configuration); } - public override Task> GetImageAsync(ImageDecoder decoder, T options) + public override Task> GetImageAsync(IImageDecoderSpecialized decoder, T options) { Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests return decoder.Decode(options, stream, default); } - private Image DecodeImage(ImageDecoder decoder, T options) + private Image DecodeImage(IImageDecoderSpecialized decoder, T options) where T : class, ISpecializedDecoderOptions, new() { options.GeneralOptions.Configuration = this.Configuration; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 8d186bdab0..bb0e196499 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -101,11 +101,11 @@ namespace SixLabors.ImageSharp.Tests 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(ImageDecoder decoder, T options) + public virtual Image GetImage(IImageDecoderSpecialized 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(ImageDecoder decoder, T options) + public virtual Task> GetImageAsync(IImageDecoderSpecialized decoder, T options) where T : class, ISpecializedDecoderOptions, new() => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 02abbbb430..92b7aeac9b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class MagickReferenceDecoder : ImageDecoder + public class MagickReferenceDecoder : ImageDecoder { private readonly bool validate; @@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public static MagickReferenceDecoder Instance { get; } = new(); - public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Configuration configuration = options.GeneralOptions.Configuration; + Configuration configuration = options.Configuration; var bmpReadDefines = new BmpReadDefines { IgnoreFileSize = !this.validate @@ -70,11 +70,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new Image(configuration, new ImageMetadata(), framesList); } - public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); - public override IImageInfo IdentifySpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel @@ -106,9 +106,4 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } } } - - public class MagickReferenceDecoderOptions : ISpecializedDecoderOptions - { - public DecoderOptions GeneralOptions { get; set; } = new(); - } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 21f0fd8650..ddf09ebddc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -11,18 +11,18 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class SystemDrawingReferenceDecoder : ImageDecoder + public class SystemDrawingReferenceDecoder : ImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public override IImageInfo IdentifySpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using var sourceBitmap = new SDBitmap(stream); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } - public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using var sourceBitmap = new SDBitmap(stream); if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) @@ -47,12 +47,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); } - public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - } - - public class SystemDrawingReferenceDecoderOptions : ISpecializedDecoderOptions - { - public DecoderOptions GeneralOptions { get; set; } = new(); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index d75ac21dca..9255d5d9fa 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoder : ImageDecoder + private class TestDecoder : ImageDecoder, IImageDecoderSpecialized { // Couldn't make xUnit happy without this hackery: private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -368,16 +368,23 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; @@ -389,7 +396,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoderWithParameters : ImageDecoder + private class TestDecoderWithParameters : ImageDecoder, IImageDecoderSpecialized { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -405,16 +412,23 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo IdentifySpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];