diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 7db2b7f963..1732a6bdbf 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -178,7 +178,7 @@ private static unsafe void AotCompileImage() img.CloneAs(default); img.CloneAs(default); - ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); + ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); } @@ -217,14 +217,14 @@ private static void AotCompileImageEncoderInternals() private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { - default(WebpDecoderCore).Decode(default, default, default); - default(BmpDecoderCore).Decode(default, default, default); - default(GifDecoderCore).Decode(default, default, default); - default(JpegDecoderCore).Decode(default, default, default); - default(PbmDecoderCore).Decode(default, default, default); - default(PngDecoderCore).Decode(default, default, default); - default(TgaDecoderCore).Decode(default, default, default); - default(TiffDecoderCore).Decode(default, default, default); + default(WebpDecoderCore).Decode(default, default); + default(BmpDecoderCore).Decode(default, default); + default(GifDecoderCore).Decode(default, default); + default(JpegDecoderCore).Decode(default, default); + default(PbmDecoderCore).Decode(default, default); + default(PngDecoderCore).Decode(default, default); + default(TgaDecoderCore).Decode(default, default); + default(TiffDecoderCore).Decode(default, default); } /// @@ -286,9 +286,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, default); /// /// This method pre-seeds the all in the AoT compiler. diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 15d5b9a087..0b93cf01fc 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -10,43 +10,40 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Image decoder for generating an image out of a Windows bitmap stream. /// - /// - /// Does not support the following formats at the moment: - /// - /// JPG - /// PNG - /// Some OS/2 specific subtypes like: Bitmap Array, Color Icon, Color Pointer, Icon, Pointer. - /// - /// Formats will be supported in a later releases. We advise always - /// to use only 24 Bit Windows bitmaps. - /// - public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector + public class BmpDecoder : IImageDecoderSpecialized { - /// - /// Gets or sets a value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. - /// - public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black; - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new BmpDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + return new BmpDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken); } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + 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); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoderSpecialized.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new BmpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + Image image = new BmpDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options.GeneralOptions, image); + + return image; } + + /// + Image IImageDecoderSpecialized.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 3a96c40223..1adc34849e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -89,34 +89,38 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private BmpInfoHeader infoHeader; + /// + /// The global configuration. + /// + private readonly Configuration configuration; + /// /// Used for allocating memory during processing operations. /// private readonly MemoryAllocator memoryAllocator; /// - /// The bitmap decoder options. + /// How to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. /// - private readonly IBmpDecoderOptions options; + private readonly RleSkippedPixelHandling rleSkippedPixelHandling; /// /// Initializes a new instance of the class. /// - /// The configuration. /// The options. - public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) + public BmpDecoderCore(BmpDecoderOptions options) { - this.Configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; + this.Options = options.GeneralOptions; + this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public Configuration Configuration { get; } + public DecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); /// @@ -128,7 +132,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken { int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); - image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); + image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); @@ -325,7 +329,7 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels 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])); @@ -397,7 +401,7 @@ private void ReadRle24(Buffer2D pixels, int width, int height, b 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])); @@ -943,7 +947,7 @@ private void ReadRgb24(Buffer2D pixels, int width, int height, b int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -971,7 +975,7 @@ private void ReadRgb32Fast(Buffer2D pixels, int width, int heigh int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -1006,7 +1010,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh this.stream.Read(rowSpan); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, bgraRowSpan, width); @@ -1042,7 +1046,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -1056,7 +1060,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh { this.stream.Read(rowSpan); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, bgraRowSpan, width); diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs new file mode 100644 index 0000000000..1f5ce08d1c --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Configuration options for decoding Windows Bitmap images. + /// + public sealed class BmpDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + + /// + /// Gets or sets the value indicating how to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. + /// + public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } + } +} diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs deleted file mode 100644 index 1283d0ae16..0000000000 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Bmp -{ - /// - /// Image decoder options for decoding Windows bitmap streams. - /// - internal interface IBmpDecoderOptions - { - /// - /// Gets the value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. - /// - RleSkippedPixelHandling RleSkippedPixelHandling { get; } - } -} diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs new file mode 100644 index 0000000000..8f16c6ecc7 --- /dev/null +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Provides general configuration options for decoding image formats. + /// + public sealed class DecoderOptions + { + private static readonly Lazy LazyOptions = new(() => new()); + + private uint maxFrames = int.MaxValue; + + /// + /// Gets the shared default general decoder options instance. + /// + internal static DecoderOptions Default { get; } = LazyOptions.Value; + + /// + /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. + /// + public Configuration Configuration { get; set; } = Configuration.Default; + + /// + /// Gets or sets the target size to decode the image into. + /// + public Size? TargetSize { get; set; } = null; + + /// + /// Gets or sets the sampler to use when resizing during decoding. + /// + public IResampler Sampler { get; set; } = KnownResamplers.Box; + + /// + /// Gets or sets a value indicating whether to ignore encoded metadata when decoding. + /// + public bool SkipMetadata { get; set; } = false; + + /// + /// Gets or sets the maximum number of image frames to decode, inclusive. + /// + public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } + } +} diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 666c3b92c1..a737ce74ed 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Gif @@ -11,37 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector + public sealed class GifDecoder : IImageDecoder { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } = false; - - /// - /// Gets or sets the decoding mode for multi-frame images - /// - public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - var decoder = new GifDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); - } + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + return new GifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new GifDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + GifDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } + + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 25354f6f33..b307ffd60f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -56,6 +56,26 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// private GifImageDescriptor imageDescriptor; + /// + /// The global configuration. + /// + private readonly Configuration configuration; + + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The maximum number of frames to decode. Inclusive. + /// + private readonly uint maxFrames; + + /// + /// Whether to skip metadata during decode. + /// + private readonly bool skipMetadata; + /// /// The abstract metadata. /// @@ -69,39 +89,27 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) + public GifDecoderCore(DecoderOptions options) { - this.IgnoreMetadata = options.IgnoreMetadata; - this.DecodingMode = options.DecodingMode; - this.Configuration = configuration ?? Configuration.Default; + this.Options = options; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public Configuration Configuration { get; } - - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; internal set; } + public DecoderOptions Options { get; } - /// - /// Gets the decoding mode for multi-frame images. - /// - public FrameDecodingMode DecodingMode { get; } - - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); - private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator; - /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + uint frameCount = 0; Image image = null; ImageFrame previousFrame = null; try @@ -114,7 +122,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken { if (nextFlag == GifConstants.ImageLabel) { - if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + if (previousFrame != null && ++frameCount == this.maxFrames) { break; } @@ -277,9 +285,9 @@ private void ReadApplicationExtension() this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize); bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes); - if (isXmp && !this.IgnoreMetadata) + if (isXmp && !this.skipMetadata) { - var extension = GifXmpApplicationExtension.Read(this.stream, this.MemoryAllocator); + var extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator); if (extension.Data.Length > 0) { this.metadata.XmpProfile = new XmpProfile(extension.Data); @@ -346,13 +354,13 @@ private void ReadComments() GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block"); } - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.stream.Seek(length, SeekOrigin.Current); continue; } - using IMemoryOwner commentsBuffer = this.MemoryAllocator.Allocate(length); + using IMemoryOwner commentsBuffer = this.memoryAllocator.Allocate(length); Span commentsSpan = commentsBuffer.GetSpan(); this.stream.Read(commentsSpan); @@ -385,11 +393,11 @@ private void ReadFrame(ref Image image, ref ImageFrame p if (this.imageDescriptor.LocalColorTableFlag) { int length = this.imageDescriptor.LocalColorTableSize * 3; - localColorTable = this.Configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); + localColorTable = this.configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); this.stream.Read(localColorTable.GetSpan()); } - indices = this.Configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); + indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); this.ReadFrameIndices(indices); Span rawColorTable = default; @@ -423,7 +431,7 @@ private void ReadFrame(ref Image image, ref ImageFrame p private void ReadFrameIndices(Buffer2D indices) { int minCodeSize = this.stream.ReadByte(); - using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream); + using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); lzwDecoder.DecodePixels(minCodeSize, indices); } @@ -451,12 +459,12 @@ private void ReadFrameColors(ref Image image, ref ImageFrame(this.Configuration, imageWidth, imageHeight, Color.Black.ToPixel(), this.metadata); + image = new Image(this.configuration, imageWidth, imageHeight, Color.Black.ToPixel(), this.metadata); } else { // This initializes the image to become fully transparent because the alpha channel is zero. - image = new Image(this.Configuration, imageWidth, imageHeight, this.metadata); + image = new Image(this.configuration, imageWidth, imageHeight, this.metadata); } this.SetFrameMetadata(image.Frames.RootFrame.Metadata); @@ -675,7 +683,7 @@ private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream s if (globalColorTableLength > 0) { - this.globalColorTable = this.MemoryAllocator.Allocate(globalColorTableLength, AllocationOptions.Clean); + this.globalColorTable = this.memoryAllocator.Allocate(globalColorTableLength, AllocationOptions.Clean); // Read the global color table data from the stream stream.Read(this.globalColorTable.GetSpan()); diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs deleted file mode 100644 index 74068e1624..0000000000 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp.Formats.Gif -{ - /// - /// Decoder for generating an image out of a gif encoded stream. - /// - internal interface IGifDecoderOptions - { - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - bool IgnoreMetadata { get; } - - /// - /// Gets the decoding mode for multi-frame images. - /// - FrameDecodingMode DecodingMode { get; } - } -} diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 82c7804d57..949e2133ad 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -10,28 +10,34 @@ namespace SixLabors.ImageSharp.Formats /// /// Encapsulates properties and methods required for decoding an image from a stream. /// - public interface IImageDecoder + public interface IImageDecoder : IImageInfoDetector { /// /// 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 configuration for the image. + /// The general decoder options. /// The containing image data. /// The token to monitor for cancellation requests. /// The . - // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110) - Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + /// Thrown if the encoded image contains errors. + Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; /// /// Decodes the image from the specified stream to an . /// - /// The configuration for the image. + /// + /// 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 . - // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110) - Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken); + /// Thrown if the encoded image contains errors. + Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); } } diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs index db3d71b894..63596266fb 100644 --- a/src/ImageSharp/Formats/IImageDecoderInternals.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; @@ -9,14 +9,14 @@ namespace SixLabors.ImageSharp.Formats { /// - /// Abstraction for shared internals for ***DecoderCore implementations to be used with . + /// Abstraction for shared internals for XXXDecoderCore implementations to be used with . /// internal interface IImageDecoderInternals { /// - /// Gets the associated configuration. + /// Gets the general decoder options. /// - Configuration Configuration { 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..93a3abb7a7 --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs @@ -0,0 +1,45 @@ +// 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. + /// + /// + /// 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) + 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); + } +} diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 2d30af5ceb..1bc8a44092 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -14,10 +14,14 @@ public interface IImageInfoDetector /// /// Reads the raw image information from the specified stream. /// - /// The configuration for the image. + /// + /// 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 - IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken); + /// 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 new file mode 100644 index 0000000000..eacb901838 --- /dev/null +++ b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Provides specialized configuration options for decoding image formats. + /// + public interface ISpecializedDecoderOptions + { + /// + /// Gets or sets the general decoder options. + /// + DecoderOptions GeneralOptions { get; set; } + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderExtensions.cs b/src/ImageSharp/Formats/ImageDecoderExtensions.cs new file mode 100644 index 0000000000..0e3bcdf9a0 --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoderExtensions.cs @@ -0,0 +1,182 @@ +// 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 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 b6cefd2c89..37a4ebe5fe 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -7,12 +7,55 @@ using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Formats { + /// + /// Utility methods for . + /// internal static class ImageDecoderUtilities { - public static IImageInfo Identify( + /// + /// 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, @@ -30,7 +73,7 @@ public static IImageInfo Identify( } } - public static Image Decode( + internal static Image Decode( this IImageDecoderInternals decoder, Configuration configuration, Stream stream, @@ -38,7 +81,7 @@ public static Image Decode( where TPixel : unmanaged, IPixel => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); - public static Image Decode( + internal static Image Decode( this IImageDecoderInternals decoder, Configuration configuration, Stream stream, diff --git a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs deleted file mode 100644 index 76874b7ffd..0000000000 --- a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Jpeg -{ - /// - /// Image decoder for generating an image out of a jpg stream. - /// - internal interface IJpegDecoderOptions - { - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - bool IgnoreMetadata { get; } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index b2ad00e077..6267fd099a 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -3,66 +3,52 @@ using System.IO; using System.Threading; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg { /// - /// Image decoder for generating an image out of a jpg stream. + /// Decoder for generating an image out of a jpeg encoded stream. /// - public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector + public sealed class JpegDecoder : IImageDecoderSpecialized { /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - using var decoder = new JpegDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + using JpegDecoderCore decoder = new(new() { GeneralOptions = options }); + return decoder.Identify(options.Configuration, stream, cancellationToken); } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - /// - /// Decodes and downscales the image from the specified stream if possible. - /// - /// The pixel format. - /// Configuration. - /// Stream. - /// Target size. - /// Cancellation token. - internal Image DecodeInto(Configuration configuration, Stream stream, Size targetSize, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + 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) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - using var decoder = new JpegDecoderCore(configuration, this); - using var bufferedReadStream = new BufferedReadStream(configuration, stream); - try - { - return decoder.DecodeInto(bufferedReadStream, targetSize, cancellationToken); - } - catch (InvalidMemoryOperationException ex) + using JpegDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) { - throw new InvalidImageContentException(((IImageDecoderInternals)decoder).Dimensions, ex); + ImageDecoderUtilities.Resize(options.GeneralOptions, image); } + + return image; } /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) - { - Guard.NotNull(stream, nameof(stream)); - - using var decoder = new JpegDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); - } + Image IImageDecoderSpecialized.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 7fc64bd057..c91749f95f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -116,31 +116,42 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals private int? resetInterval; /// - /// Initializes a new instance of the class. + /// The global configuration. /// - /// The configuration. - /// The options. - public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.Configuration = configuration ?? Configuration.Default; - this.IgnoreMetadata = options.IgnoreMetadata; - } + private readonly Configuration configuration; - /// - public Configuration Configuration { get; } + /// + /// Whether to skip metadata during decode. + /// + private readonly bool skipMetadata; /// - /// Gets the frame + /// The jpeg specific resize options. /// - public JpegFrame Frame { get; private set; } + private readonly JpegDecoderResizeMode resizeMode; + + /// + /// Initializes a new instance of the class. + /// + /// The decoder options. + public JpegDecoderCore(JpegDecoderOptions options) + { + this.Options = options.GeneralOptions; + this.resizeMode = options.ResizeMode; + this.configuration = options.GeneralOptions.Configuration; + this.skipMetadata = options.GeneralOptions.SkipMetadata; + } + + /// + public DecoderOptions Options { get; } /// - Size IImageDecoderInternals.Dimensions => this.Frame.PixelSize; + public Size Dimensions => this.Frame.PixelSize; /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// Gets the frame /// - public bool IgnoreMetadata { get; } + public JpegFrame Frame { get; private set; } /// /// Gets the decoded by this decoder instance. @@ -201,48 +212,33 @@ public static JpegFileMarker FindNextFileMarker(BufferedReadStream stream) /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel - => this.Decode(stream, targetSize: null, cancellationToken); - - /// - public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { - this.ParseStream(stream, spectralConverter: null, cancellationToken); + using var spectralConverter = new SpectralConverter(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); + this.ParseStream(stream, spectralConverter, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); this.InitXmpProfile(); this.InitDerivedMetadataProperties(); - Size pixelSize = this.Frame.PixelSize; - return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, this.Metadata); + return new Image( + this.configuration, + spectralConverter.GetPixelBuffer(cancellationToken), + this.Metadata); } - /// - /// Decodes and downscales the image from the specified stream if possible. - /// - /// The pixel format. - /// Stream. - /// Target size. - /// Cancellation token. - internal Image DecodeInto(BufferedReadStream stream, Size targetSize, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => this.Decode(stream, targetSize, cancellationToken); - - private Image Decode(BufferedReadStream stream, Size? targetSize, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { - using var spectralConverter = new SpectralConverter(this.Configuration, targetSize); - this.ParseStream(stream, spectralConverter, cancellationToken); + this.ParseStream(stream, spectralConverter: null, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); this.InitXmpProfile(); this.InitDerivedMetadataProperties(); - return new Image( - this.Configuration, - spectralConverter.GetPixelBuffer(cancellationToken), - this.Metadata); + Size pixelSize = this.Frame.PixelSize; + return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, this.Metadata); } /// @@ -262,7 +258,7 @@ public void LoadTables(byte[] tableBytes, IJpegScanDecoder scanDecoder) } using var ms = new MemoryStream(tableBytes); - using var stream = new BufferedReadStream(this.Configuration, ms); + using var stream = new BufferedReadStream(this.configuration, ms); // Check for the Start Of Image marker. int bytesRead = stream.Read(this.markerBuffer, 0, 2); @@ -778,7 +774,7 @@ private void ProcessApp1Marker(BufferedReadStream stream, int remaining) { const int ExifMarkerLength = 6; const int XmpMarkerLength = 29; - if (remaining < ExifMarkerLength || this.IgnoreMetadata) + if (remaining < ExifMarkerLength || this.skipMetadata) { // Skip the application header length. stream.Skip(remaining); @@ -816,7 +812,7 @@ private void ProcessApp1Marker(BufferedReadStream stream, int remaining) if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker.Slice(0, ExifMarkerLength))) { const int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength; - if (remaining < remainingXmpMarkerBytes || this.IgnoreMetadata) + if (remaining < remainingXmpMarkerBytes || this.skipMetadata) { // Skip the application header length. stream.Skip(remaining); @@ -858,7 +854,7 @@ private void ProcessApp2Marker(BufferedReadStream stream, int remaining) { // Length is 14 though we only need to check 12. const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) + if (remaining < Icclength || this.skipMetadata) { stream.Skip(remaining); return; @@ -899,7 +895,7 @@ private void ProcessApp2Marker(BufferedReadStream stream, int remaining) /// The remaining bytes in the segment block. private void ProcessApp13Marker(BufferedReadStream stream, int remaining) { - if (remaining < ProfileResolver.AdobePhotoshopApp13Marker.Length || this.IgnoreMetadata) + if (remaining < ProfileResolver.AdobePhotoshopApp13Marker.Length || this.skipMetadata) { stream.Skip(remaining); return; @@ -1283,8 +1279,8 @@ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, } IJpegComponent component = decodingComponentType is ComponentType.Huffman ? - new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i) : - new ArithmeticDecodingComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i); + new JpegComponent(this.configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i) : + new ArithmeticDecodingComponent(this.configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i); this.Frame.Components[i] = (JpegComponent)component; this.Frame.ComponentIds[i] = componentId; @@ -1323,7 +1319,7 @@ private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int rem } int length = remaining; - using (IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(totalBufferSize)) + using (IMemoryOwner buffer = this.configuration.MemoryAllocator.Allocate(totalBufferSize)) { Span bufferSpan = buffer.GetSpan(); Span huffmanLengthsSpan = bufferSpan.Slice(0, codeLengthsByteSize); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs new file mode 100644 index 0000000000..95d1c81d23 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Jpeg +{ + /// + /// Configuration options for decoding Jpeg images. + /// + 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..5037c85809 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Processing; + +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. Similar in output to . + /// + 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 835582bb69..394da0a60f 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 : IImageDecoder, IImageInfoDetector + public sealed class PbmDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new PbmDecoderCore(configuration); - return decoder.Decode(configuration, stream, cancellationToken); + return new PbmDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); - - /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new PbmDecoderCore(configuration); - return decoder.Identify(configuration, stream, cancellationToken); + PbmDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } + + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 5a549f7558..05f06deaae 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -19,43 +19,50 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals private int maxPixelValue; /// - /// Initializes a new instance of the class. + /// The general configuration. /// - /// The configuration. - public PbmDecoderCore(Configuration configuration) => this.Configuration = configuration ?? Configuration.Default; + private readonly Configuration configuration; - /// - public Configuration Configuration { get; } + /// + /// The colortype to use + /// + private PbmColorType colorType; /// - /// Gets the colortype to use + /// The size of the pixel array /// - public PbmColorType ColorType { get; private set; } + private Size pixelSize; /// - /// Gets the size of the pixel array + /// The component data type /// - public Size PixelSize { get; private set; } + private PbmComponentType componentType; /// - /// Gets the component data type + /// The Encoding of pixels /// - public PbmComponentType ComponentType { get; private set; } + private PbmEncoding encoding; /// - /// Gets the Encoding of pixels + /// The decoded by this decoder instance. /// - public PbmEncoding Encoding { get; private set; } + private ImageMetadata metadata; /// - /// Gets the decoded by this decoder instance. + /// Initializes a new instance of the class. /// - public ImageMetadata Metadata { get; private set; } + /// The decoder options. + public PbmDecoderCore(DecoderOptions options) + { + this.Options = options; + this.configuration = options.Configuration; + } /// - Size IImageDecoderInternals.Dimensions => this.PixelSize; + public DecoderOptions Options { get; } - private bool NeedsUpscaling => this.ColorType != PbmColorType.BlackAndWhite && this.maxPixelValue is not 255 and not 65535; + /// + public Size Dimensions => this.pixelSize; /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) @@ -63,12 +70,12 @@ public Image Decode(BufferedReadStream stream, CancellationToken { this.ProcessHeader(stream); - var image = new Image(this.Configuration, this.PixelSize.Width, this.PixelSize.Height, this.Metadata); + var image = new Image(this.configuration, this.pixelSize.Width, this.pixelSize.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); this.ProcessPixels(stream, pixels); - if (this.NeedsUpscaling) + if (this.NeedsUpscaling()) { this.ProcessUpscaling(image); } @@ -82,8 +89,8 @@ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancella this.ProcessHeader(stream); // BlackAndWhite pixels are encoded into a byte. - int bitsPerPixel = this.ComponentType == PbmComponentType.Short ? 16 : 8; - return new ImageInfo(new PixelTypeInfo(bitsPerPixel), this.PixelSize.Width, this.PixelSize.Height, this.Metadata); + int bitsPerPixel = this.componentType == PbmComponentType.Short ? 16 : 8; + return new ImageInfo(new PixelTypeInfo(bitsPerPixel), this.pixelSize.Width, this.pixelSize.Height, this.metadata); } /// @@ -104,33 +111,33 @@ private void ProcessHeader(BufferedReadStream stream) { case '1': // Plain PBM format: 1 component per pixel, boolean value ('0' or '1'). - this.ColorType = PbmColorType.BlackAndWhite; - this.Encoding = PbmEncoding.Plain; + this.colorType = PbmColorType.BlackAndWhite; + this.encoding = PbmEncoding.Plain; break; case '2': // Plain PGM format: 1 component per pixel, in decimal text. - this.ColorType = PbmColorType.Grayscale; - this.Encoding = PbmEncoding.Plain; + this.colorType = PbmColorType.Grayscale; + this.encoding = PbmEncoding.Plain; break; case '3': // Plain PPM format: 3 components per pixel, in decimal text. - this.ColorType = PbmColorType.Rgb; - this.Encoding = PbmEncoding.Plain; + this.colorType = PbmColorType.Rgb; + this.encoding = PbmEncoding.Plain; break; case '4': // Binary PBM format: 1 component per pixel, 8 pixels per byte. - this.ColorType = PbmColorType.BlackAndWhite; - this.Encoding = PbmEncoding.Binary; + this.colorType = PbmColorType.BlackAndWhite; + this.encoding = PbmEncoding.Binary; break; case '5': // Binary PGM format: 1 components per pixel, in binary integers. - this.ColorType = PbmColorType.Grayscale; - this.Encoding = PbmEncoding.Binary; + this.colorType = PbmColorType.Grayscale; + this.encoding = PbmEncoding.Binary; break; case '6': // Binary PPM format: 3 components per pixel, in binary integers. - this.ColorType = PbmColorType.Rgb; - this.Encoding = PbmEncoding.Binary; + this.colorType = PbmColorType.Rgb; + this.encoding = PbmEncoding.Binary; break; case '7': // PAM image: sequence of images. @@ -144,52 +151,54 @@ private void ProcessHeader(BufferedReadStream stream) stream.SkipWhitespaceAndComments(); int height = stream.ReadDecimal(); stream.SkipWhitespaceAndComments(); - if (this.ColorType != PbmColorType.BlackAndWhite) + if (this.colorType != PbmColorType.BlackAndWhite) { this.maxPixelValue = stream.ReadDecimal(); if (this.maxPixelValue > 255) { - this.ComponentType = PbmComponentType.Short; + this.componentType = PbmComponentType.Short; } else { - this.ComponentType = PbmComponentType.Byte; + this.componentType = PbmComponentType.Byte; } stream.SkipWhitespaceAndComments(); } else { - this.ComponentType = PbmComponentType.Bit; + this.componentType = PbmComponentType.Bit; } - this.PixelSize = new Size(width, height); - this.Metadata = new ImageMetadata(); - PbmMetadata meta = this.Metadata.GetPbmMetadata(); - meta.Encoding = this.Encoding; - meta.ColorType = this.ColorType; - meta.ComponentType = this.ComponentType; + this.pixelSize = new Size(width, height); + this.metadata = new ImageMetadata(); + PbmMetadata meta = this.metadata.GetPbmMetadata(); + meta.Encoding = this.encoding; + meta.ColorType = this.colorType; + meta.ComponentType = this.componentType; } private void ProcessPixels(BufferedReadStream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { - if (this.Encoding == PbmEncoding.Binary) + if (this.encoding == PbmEncoding.Binary) { - BinaryDecoder.Process(this.Configuration, pixels, stream, this.ColorType, this.ComponentType); + BinaryDecoder.Process(this.configuration, pixels, stream, this.colorType, this.componentType); } else { - PlainDecoder.Process(this.Configuration, pixels, stream, this.ColorType, this.ComponentType); + PlainDecoder.Process(this.configuration, pixels, stream, this.colorType, this.componentType); } } private void ProcessUpscaling(Image image) where TPixel : unmanaged, IPixel { - int maxAllocationValue = this.ComponentType == PbmComponentType.Short ? 65535 : 255; + int maxAllocationValue = this.componentType == PbmComponentType.Short ? 65535 : 255; float factor = maxAllocationValue / this.maxPixelValue; image.Mutate(x => x.Brightness(factor)); } + + private bool NeedsUpscaling() => this.colorType != PbmColorType.BlackAndWhite && this.maxPixelValue is not 255 and not 65535; } } diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs deleted file mode 100644 index 873abc1fd7..0000000000 --- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png -{ - /// - /// The options for decoding png images - /// - internal interface IPngDecoderOptions - { - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - bool IgnoreMetadata { get; } - } -} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index f1a03b83e3..2aa5634358 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -10,78 +10,88 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Decoder for generating an image out of a png encoded stream. /// - public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector + public sealed class PngDecoder : IImageDecoder { /// - public bool IgnoreMetadata { get; set; } + IImageInfo IImageInfoDetector.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 Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PngDecoderCore decoder = new(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + PngDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PngDecoderCore decoder = new(configuration, true); - IImageInfo info = decoder.Identify(configuration, stream, cancellationToken); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + PngDecoderCore decoder = new(options, true); + IImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken); stream.Position = 0; PngMetadata meta = info.Metadata.GetPngMetadata(); 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 - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.Palette: - return this.Decode(configuration, stream, cancellationToken); + return imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); default: - return this.Decode(configuration, stream, cancellationToken); + return imageDecoder.Decode(options, stream, cancellationToken); } } - - /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) - { - PngDecoderCore decoder = new(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); - } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 29b28c9180..6ab9f4c4b0 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -34,10 +34,15 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// private readonly byte[] buffer = new byte[4]; + /// + /// The general decoder options. + /// + private readonly Configuration configuration; + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// - private readonly bool ignoreMetadata; + private readonly bool skipMetadata; /// /// Gets or sets a value indicating whether to read the IHDR and tRNS chunks only. @@ -117,29 +122,28 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) + public PngDecoderCore(DecoderOptions options) { - this.Configuration = configuration ?? Configuration.Default; - this.memoryAllocator = this.Configuration.MemoryAllocator; - this.ignoreMetadata = options.IgnoreMetadata; + this.Options = options; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.memoryAllocator = this.configuration.MemoryAllocator; } - internal PngDecoderCore(Configuration configuration, bool colorMetadataOnly) + internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly) { - this.Configuration = configuration ?? Configuration.Default; - this.memoryAllocator = this.Configuration.MemoryAllocator; + this.Options = options; this.colorMetadataOnly = colorMetadataOnly; - this.ignoreMetadata = true; + this.skipMetadata = true; + this.configuration = options.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public Configuration Configuration { get; } + public DecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.header.Width, this.header.Height); /// @@ -198,7 +202,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken this.ReadInternationalTextChunk(metadata, chunk.Data.GetSpan()); break; case PngChunkType.Exif: - if (!this.ignoreMetadata) + if (!this.skipMetadata) { byte[] exifData = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(exifData); @@ -335,7 +339,7 @@ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancella break; } - if (!this.ignoreMetadata) + if (!this.skipMetadata) { byte[] exifData = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(exifData); @@ -468,7 +472,7 @@ private void InitializeImage(ImageMetadata metadata, out Image i where TPixel : unmanaged, IPixel { image = Image.CreateUninitialized( - this.Configuration, + this.configuration, this.header.Width, this.header.Height, metadata); @@ -484,7 +488,7 @@ private void InitializeImage(ImageMetadata metadata, out Image i this.previousScanline?.Dispose(); this.scanline?.Dispose(); this.previousScanline = this.memoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); - this.scanline = this.Configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); + this.scanline = this.configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); } /// @@ -797,7 +801,7 @@ private void ProcessDefilteredScanline(ReadOnlySpan defilteredScan case PngColorType.Rgb: PngScanlineProcessor.ProcessRgbScanline( - this.Configuration, + this.configuration, this.header, scanlineSpan, rowSpan, @@ -811,7 +815,7 @@ private void ProcessDefilteredScanline(ReadOnlySpan defilteredScan case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessRgbaScanline( - this.Configuration, + this.configuration, this.header, scanlineSpan, rowSpan, @@ -1004,7 +1008,7 @@ private void ReadHeaderChunk(PngMetadata pngMetadata, ReadOnlySpan data) /// The containing the data. private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1039,7 +1043,7 @@ private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, Rea /// The containing the data. private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1225,10 +1229,10 @@ private unsafe bool TryUncompressZlibData(ReadOnlySpan compressedData, out { fixed (byte* compressedDataBase = compressedData) { - using (IMemoryOwner destBuffer = this.memoryAllocator.Allocate(this.Configuration.StreamProcessingBufferSize)) + using (IMemoryOwner destBuffer = this.memoryAllocator.Allocate(this.configuration.StreamProcessingBufferSize)) using (var memoryStreamOutput = new MemoryStream(compressedData.Length)) using (var memoryStreamInput = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length)) - using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStreamInput)) + using (var bufferedStream = new BufferedReadStream(this.configuration, memoryStreamInput)) using (var inflateStream = new ZlibInflateStream(bufferedStream)) { Span destUncompressedData = destBuffer.GetSpan(); @@ -1327,7 +1331,7 @@ private void MergeOrSetExifProfile(ImageMetadata metadata, ExifProfile newProfil /// The containing the data. private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1566,7 +1570,7 @@ private void SkipChunkDataAndCrc(in PngChunk chunk) private IMemoryOwner ReadChunkData(int length) { // We rent the buffer here to return it afterwards in Decode() - IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); + IMemoryOwner buffer = this.configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); this.currentStream.Read(buffer.GetSpan(), 0, length); diff --git a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs deleted file mode 100644 index d2d0944a2c..0000000000 --- a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Tga -{ - /// - /// The options for decoding tga images. Currently empty, but this may change in the future. - /// - internal interface ITgaDecoderOptions - { - } -} diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index a256850e6a..ac99271cb9 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -10,28 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Image decoder for Truevision TGA images. /// - public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector + public sealed class TgaDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new TgaDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + return new TgaDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); - /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new TgaDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + TgaDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } + + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index af586e2544..0f46d5953e 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -22,6 +22,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// private readonly byte[] scratchBuffer = new byte[4]; + /// + /// General configuration options. + /// + private readonly Configuration configuration; + /// /// The metadata. /// @@ -47,11 +52,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// private BufferedReadStream currentStream; - /// - /// The bitmap decoder options. - /// - private readonly ITgaDecoderOptions options; - /// /// Indicates whether there is a alpha channel present. /// @@ -60,21 +60,18 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// Initializes a new instance of the class. /// - /// The configuration. /// The options. - public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) + public TgaDecoderCore(DecoderOptions options) { - this.Configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; + this.Options = options; + this.configuration = options.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public Configuration Configuration { get; } + public DecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); /// @@ -97,7 +94,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken throw new UnknownImageFormatException("Width or height cannot be 0"); } - var image = Image.CreateUninitialized(this.Configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); + var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); if (this.fileHeader.ColorMapType == 1) @@ -463,11 +460,11 @@ private void ReadBgra16(int width, int height, Buffer2D pixels, if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { - PixelOperations.Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width); + PixelOperations.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width); } else { - PixelOperations.Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width); + PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); } } } @@ -672,7 +669,7 @@ private void ReadL8Row(int width, Buffer2D pixels, Span ro } Span pixelSpan = pixels.DangerousGetRowSpan(y); - PixelOperations.Instance.FromL8Bytes(this.Configuration, row, pixelSpan, width); + PixelOperations.Instance.FromL8Bytes(this.configuration, row, pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -709,7 +706,7 @@ private void ReadBgr24Row(int width, Buffer2D pixels, Span } Span pixelSpan = pixels.DangerousGetRowSpan(y); - PixelOperations.Instance.FromBgr24Bytes(this.Configuration, row, pixelSpan, width); + PixelOperations.Instance.FromBgr24Bytes(this.configuration, row, pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -738,7 +735,7 @@ private void ReadBgra32Row(int width, Buffer2D pixels, Span pixelSpan = pixels.DangerousGetRowSpan(y); - PixelOperations.Instance.FromBgra32Bytes(this.Configuration, row, pixelSpan, width); + PixelOperations.Instance.FromBgra32Bytes(this.configuration, row, pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index 37debc9f6f..ae89fce0ee 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -3,6 +3,7 @@ using System; using System.IO.Compression; +using System.Threading; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; @@ -40,7 +41,7 @@ public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bi } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { long pos = stream.Position; using (var deframeStream = new ZlibInflateStream( diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs index 0d9a6ba139..ef44263a56 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// internal sealed class JpegTiffCompression : TiffBaseDecompressor { - private readonly Configuration configuration; + private readonly JpegDecoderOptions options; private readonly byte[] jpegTables; @@ -27,14 +27,14 @@ internal sealed class JpegTiffCompression : TiffBaseDecompressor /// /// Initializes a new instance of the class. /// - /// The configuration. + /// The specialized jpeg decoder options. /// The memoryAllocator to use for buffer allocations. /// The image width. /// The bits per pixel. /// The JPEG tables containing the quantization and/or Huffman tables. /// The photometric interpretation. public JpegTiffCompression( - Configuration configuration, + JpegDecoderOptions options, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, @@ -42,31 +42,29 @@ public JpegTiffCompression( TiffPhotometricInterpretation photometricInterpretation) : base(memoryAllocator, width, bitsPerPixel) { - this.configuration = configuration; + this.options = options; this.jpegTables = jpegTables; this.photometricInterpretation = photometricInterpretation; } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { if (this.jpegTables != null) { - using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder()); - + using var jpegDecoder = new JpegDecoderCore(this.options); + Configuration configuration = this.options.GeneralOptions.Configuration; switch (this.photometricInterpretation) { case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: { - using SpectralConverter spectralConverterGray = - new GrayJpegSpectralConverter(this.configuration); - var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None); + using SpectralConverter spectralConverterGray = new GrayJpegSpectralConverter(configuration); + var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, cancellationToken); jpegDecoder.LoadTables(this.jpegTables, scanDecoderGray); - jpegDecoder.ParseStream(stream, spectralConverterGray, CancellationToken.None); + jpegDecoder.ParseStream(stream, spectralConverterGray, cancellationToken); - // TODO: Should we pass through the CancellationToken from the tiff decoder? - using Buffer2D decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None); + using Buffer2D decompressedBuffer = spectralConverterGray.GetPixelBuffer(cancellationToken); CopyImageBytesToBuffer(buffer, decompressedBuffer); break; } @@ -75,13 +73,12 @@ protected override void Decompress(BufferedReadStream stream, int byteCount, int case TiffPhotometricInterpretation.Rgb: { using SpectralConverter spectralConverter = - new TiffJpegSpectralConverter(this.configuration, this.photometricInterpretation); - var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); + new TiffJpegSpectralConverter(configuration, this.photometricInterpretation); + var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken); jpegDecoder.LoadTables(this.jpegTables, scanDecoder); - jpegDecoder.ParseStream(stream, spectralConverter, CancellationToken.None); + jpegDecoder.ParseStream(stream, spectralConverter, cancellationToken); - // TODO: Should we pass through the CancellationToken from the tiff decoder? - using Buffer2D decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None); + using Buffer2D decompressedBuffer = spectralConverter.GetPixelBuffer(cancellationToken); CopyImageBytesToBuffer(buffer, decompressedBuffer); break; } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index 57da76f6c7..a53704abf6 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.IO; @@ -35,7 +36,7 @@ public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPe } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { var decoder = new TiffLzwDecoder(stream); decoder.DecodePixels(buffer); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 16d3f125bd..c95048b686 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -40,7 +41,7 @@ public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffFillOrder f private TiffFillOrder FillOrder { get; } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index 291d40c859..6bec4e2ce5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System; +using System.Threading; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -24,7 +25,7 @@ public NoneTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsP } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) => _ = stream.Read(buffer, 0, Math.Min(buffer.Length, byteCount)); /// diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 9df331b6ea..230f9f5c92 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Threading; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -27,7 +28,7 @@ public PackBitsTiffCompression(MemoryAllocator memoryAllocator, int width, int b } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { if (this.compressedDataMemory == null) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 2fed2b7427..e8513c474b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -53,7 +54,7 @@ public T4TiffCompression( private TiffFillOrder FillOrder { get; } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { if (this.faxCompressionOptions.HasFlag(FaxCompressionOptions.TwoDimensionalCoding)) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 2373e7b9be..1a105e96a9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -49,7 +50,7 @@ public T6TiffCompression( private TiffFillOrder FillOrder { get; } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { int height = stripHeight; buffer.Clear(); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs index 3b86d1e87a..8b892757da 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 @@ protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRa 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/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index 0d63382ff1..db93406a0d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.IO; @@ -16,22 +17,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// internal class WebpTiffCompression : TiffBaseDecompressor { + private readonly DecoderOptions options; + /// /// Initializes a new instance of the class. /// + /// The general decoder options. /// The memory allocator. /// The width of the image. /// The bits per pixel. /// The predictor. - public WebpTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + public WebpTiffCompression(DecoderOptions options, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(memoryAllocator, width, bitsPerPixel, predictor) - { - } + => this.options = options; /// - protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) + protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { - using var image = Image.Load(stream, new WebpDecoder()); + using Image image = ((IImageDecoder)new WebpDecoder()).Decode(this.options, stream, cancellationToken); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index d828a7f4f5..aaa3e94993 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -34,13 +35,14 @@ protected TiffBaseDecompressor(MemoryAllocator memoryAllocator, int width, int b /// The number of bytes to read from the input stream. /// The height of the strip. /// The output buffer for uncompressed data. - public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer) + /// The token to monitor cancellation. + public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { DebugGuard.MustBeLessThanOrEqualTo(stripOffset, (ulong)long.MaxValue, nameof(stripOffset)); DebugGuard.MustBeLessThanOrEqualTo(stripByteCount, (ulong)long.MaxValue, nameof(stripByteCount)); stream.Seek((long)stripOffset, SeekOrigin.Begin); - this.Decompress(stream, (int)stripByteCount, stripHeight, buffer); + this.Decompress(stream, (int)stripByteCount, stripHeight, buffer, cancellationToken); if ((long)stripOffset + (long)stripByteCount < stream.Position) { @@ -55,6 +57,7 @@ public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong strip /// The number of bytes to read from the input stream. /// The height of the strip. /// The output buffer for uncompressed data. - protected abstract void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer); + /// The token to monitor cancellation. + protected abstract void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 5a8aa3b98e..213746d7de 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression internal static class TiffDecompressorsFactory { public static TiffBaseDecompressor Create( - Configuration configuration, + DecoderOptions options, TiffDecoderCompressionType method, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, @@ -58,11 +58,11 @@ public static TiffBaseDecompressor Create( case TiffDecoderCompressionType.Jpeg: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); - return new JpegTiffCompression(configuration, allocator, width, bitsPerPixel, jpegTables, photometricInterpretation); + return new JpegTiffCompression(new() { GeneralOptions = options }, allocator, width, bitsPerPixel, jpegTables, photometricInterpretation); case TiffDecoderCompressionType.Webp: DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); - return new WebpTiffCompression(allocator, width, bitsPerPixel); + return new WebpTiffCompression(options, allocator, width, bitsPerPixel); default: throw TiffThrowHelper.NotSupportedDecompressor(nameof(method)); diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs deleted file mode 100644 index 6fd7e74c7c..0000000000 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// Encapsulates the options for the . - /// - internal interface ITiffDecoderOptions - { - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - bool IgnoreMetadata { get; } - - /// - /// Gets the decoding mode for multi-frame images. - /// - FrameDecodingMode DecodingMode { get; } - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index b5da9dc0b3..dfbee19a05 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff @@ -11,39 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : IImageDecoder, ITiffDecoderOptions, IImageInfoDetector + public class TiffDecoder : IImageDecoder { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - /// Gets or sets the decoding mode for multi-frame images. - /// - public FrameDecodingMode DecodingMode { get; set; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + return new TiffDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); - - /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + TiffDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } + + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 2a19dab250..8e3f8c16b7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -22,20 +22,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal class TiffDecoderCore : IImageDecoderInternals { + /// + /// General configuration options. + /// + private readonly Configuration configuration; + /// /// Used for allocating memory during processing operations. /// private readonly MemoryAllocator memoryAllocator; /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// A value indicating whether the metadata should be ignored when the image is being decoded. /// - private readonly bool ignoreMetadata; + private readonly bool skipMetadata; /// - /// Gets the decoding mode for multi-frame images + /// The maximum number of frames to decode. Inclusive. /// - private readonly FrameDecodingMode decodingMode; + private readonly uint maxFrames; /// /// The stream to decode from. @@ -55,16 +60,14 @@ internal class TiffDecoderCore : IImageDecoderInternals /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) + public TiffDecoderCore(DecoderOptions options) { - options ??= new TiffDecoder(); - - this.Configuration = configuration ?? Configuration.Default; - this.ignoreMetadata = options.IgnoreMetadata; - this.decodingMode = options.DecodingMode; - this.memoryAllocator = this.Configuration.MemoryAllocator; + this.Options = options; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// @@ -148,7 +151,7 @@ public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) public TiffPredictor Predictor { get; set; } /// - public Configuration Configuration { get; } + public DecoderOptions Options { get; } /// public Size Dimensions { get; private set; } @@ -161,25 +164,26 @@ public Image Decode(BufferedReadStream stream, CancellationToken try { this.inputStream = stream; - var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); + var reader = new DirectoryReader(stream, this.configuration.MemoryAllocator); IEnumerable directories = reader.Read(); this.byteOrder = reader.ByteOrder; this.isBigTiff = reader.IsBigTiff; + uint frameCount = 0; foreach (ExifProfile ifd in directories) { cancellationToken.ThrowIfCancellationRequested(); ImageFrame frame = this.DecodeFrame(ifd, cancellationToken); frames.Add(frame); - if (this.decodingMode is FrameDecodingMode.First) + if (++frameCount == this.maxFrames) { break; } } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff); // TODO: Tiff frames can have different sizes. ImageFrame root = frames[0]; @@ -192,7 +196,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken } } - return new Image(this.Configuration, metadata, frames); + return new Image(this.configuration, metadata, frames); } catch { @@ -209,7 +213,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.inputStream = stream; - var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); + var reader = new DirectoryReader(stream, this.configuration.MemoryAllocator); IEnumerable directories = reader.Read(); ExifProfile rootFrameExifProfile = directories.First(); @@ -233,7 +237,7 @@ private ImageFrame DecodeFrame(ExifProfile tags, CancellationTok where TPixel : unmanaged, IPixel { var imageFrameMetaData = new ImageFrameMetadata(); - if (!this.ignoreMetadata) + if (!this.skipMetadata) { imageFrameMetaData.ExifProfile = tags; } @@ -245,7 +249,7 @@ private ImageFrame DecodeFrame(ExifProfile tags, CancellationTok int width = GetImageWidth(tags); int height = GetImageHeight(tags); - var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); + var frame = new ImageFrame(this.configuration, width, height, imageFrameMetaData); int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; @@ -369,7 +373,7 @@ private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStr } using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Configuration, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -406,7 +410,8 @@ private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStr stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, - stripBuffers[planeIndex].GetSpan()); + stripBuffers[planeIndex].GetSpan(), + cancellationToken); stripIndex += stripsPerPlane; } @@ -449,7 +454,7 @@ private void DecodeStripsChunky(ImageFrame frame, int rowsPerStr Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Configuration, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -463,7 +468,7 @@ private void DecodeStripsChunky(ImageFrame frame, int rowsPerStr this.byteOrder); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create( - this.Configuration, + this.configuration, this.memoryAllocator, this.ColorType, this.BitsPerSample, @@ -494,7 +499,8 @@ private void DecodeStripsChunky(ImageFrame frame, int rowsPerStr stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, - stripBufferSpan); + stripBufferSpan, + cancellationToken); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); } diff --git a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs deleted file mode 100644 index 63bc2b9fd8..0000000000 --- a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp.Formats.Webp -{ - /// - /// Image decoder options for generating an image out of a webp stream. - /// - internal interface IWebpDecoderOptions - { - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - bool IgnoreMetadata { get; } - - /// - /// Gets the decoding mode for multi-frame images. - /// - FrameDecodingMode DecodingMode { get; } - } -} diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 7bd4a3eb5b..57c01cac74 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -33,6 +33,11 @@ internal class WebpAnimationDecoder : IDisposable /// private readonly Configuration configuration; + /// + /// The maximum number of frames to decode. Inclusive. + /// + private readonly uint maxFrames; + /// /// The area to restore. /// @@ -48,29 +53,24 @@ internal class WebpAnimationDecoder : IDisposable /// private WebpMetadata webpMetadata; + /// + /// The alpha data, if an ALPH chunk is present. + /// + private IMemoryOwner alphaData; + /// /// Initializes a new instance of the class. /// /// The memory allocator. /// The global configuration. - /// The frame decoding mode. - public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, FrameDecodingMode decodingMode) + /// The maximum number of frames to decode. Inclusive. + public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, uint maxFrames) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; - this.DecodingMode = decodingMode; + this.maxFrames = maxFrames; } - /// - /// Gets or sets the alpha data, if an ALPH chunk is present. - /// - public IMemoryOwner AlphaData { get; set; } - - /// - /// Gets the decoding mode for multi-frame images. - /// - public FrameDecodingMode DecodingMode { get; } - /// /// Decodes the animated webp image from the specified stream. /// @@ -90,6 +90,7 @@ public Image Decode(BufferedReadStream stream, WebpFeatures feat this.webpMetadata = this.metadata.GetWebpMetadata(); this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount; + uint frameCount = 0; int remainingBytes = (int)completeDataSize; while (remainingBytes > 0) { @@ -110,7 +111,7 @@ public Image Decode(BufferedReadStream stream, WebpFeatures feat break; } - if (stream.Position == stream.Length || this.DecodingMode is FrameDecodingMode.First) + if (stream.Position == stream.Length || ++frameCount == this.maxFrames) { break; } @@ -224,14 +225,14 @@ private void SetFrameMetadata(ImageFrameMetadata meta, uint duration) /// The stream to read from. private byte ReadAlphaData(BufferedReadStream stream) { - this.AlphaData?.Dispose(); + this.alphaData?.Dispose(); uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer); int alphaDataSize = (int)(alphaChunkSize - 1); - this.AlphaData = this.memoryAllocator.Allocate(alphaDataSize); + this.alphaData = this.memoryAllocator.Allocate(alphaDataSize); byte alphaChunkHeader = (byte)stream.ReadByte(); - Span alphaData = this.AlphaData.GetSpan(); + Span alphaData = this.alphaData.GetSpan(); stream.Read(alphaData, 0, alphaDataSize); return alphaChunkHeader; @@ -260,7 +261,7 @@ private Buffer2D DecodeImageData(AnimationFrameData frameData, W else { var lossyDecoder = new WebpLossyDecoder(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration); - lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.AlphaData); + lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.alphaData); } return pixelBufferDecoded; @@ -381,6 +382,6 @@ private AnimationFrameData ReadFrameHeader(BufferedReadStream stream) } /// - public void Dispose() => this.AlphaData?.Dispose(); + public void Dispose() => this.alphaData?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index 078f090a1f..9b05de5ea3 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -3,8 +3,6 @@ using System.IO; using System.Threading; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp @@ -12,49 +10,34 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Image decoder for generating an image out of a webp stream. /// - public sealed class WebpDecoder : IImageDecoder, IWebpDecoderOptions, IImageInfoDetector + public sealed class WebpDecoder : IImageDecoder { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - /// Gets or sets the decoding mode for multi-frame images. - /// Defaults to All. - /// - public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - using var decoder = new WebpDecoderCore(configuration, this); - - try - { - return decoder.Decode(configuration, stream, cancellationToken); - } - catch (InvalidMemoryOperationException ex) - { - Size dims = decoder.Dimensions; - - throw new InvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dims.Width}x{dims.Height}.", ex); - } + using WebpDecoderCore decoder = new(options); + return decoder.Identify(options.Configuration, stream, cancellationToken); } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); - /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new WebpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + using WebpDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + ImageDecoderUtilities.Resize(options, image); + + return image; } + + /// + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 0b82b6d55f..75f5f88f8b 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Buffers.Binary; -using System.IO; using System.Threading; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; @@ -29,65 +28,68 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable private readonly byte[] buffer = new byte[4]; /// - /// Used for allocating memory during the decoding operations. + /// General configuration options. /// - private readonly MemoryAllocator memoryAllocator; + private readonly Configuration configuration; /// - /// The stream to decode from. + /// A value indicating whether the metadata should be ignored when the image is being decoded. /// - private BufferedReadStream currentStream; + private readonly bool skipMetadata; /// - /// The webp specific metadata. + /// The maximum number of frames to decode. Inclusive. /// - private WebpMetadata webpMetadata; + private readonly uint maxFrames; /// - /// Information about the webp image. + /// Gets the decoded by this decoder instance. /// - private WebpImageInfo webImageInfo; + private ImageMetadata metadata; /// - /// Initializes a new instance of the class. + /// Gets or sets the alpha data, if an ALPH chunk is present. /// - /// The configuration. - /// The options. - public WebpDecoderCore(Configuration configuration, IWebpDecoderOptions options) - { - this.Configuration = configuration; - this.DecodingMode = options.DecodingMode; - this.memoryAllocator = configuration.MemoryAllocator; - this.IgnoreMetadata = options.IgnoreMetadata; - } - - /// - public Configuration Configuration { get; } + private IMemoryOwner alphaData; /// - /// Gets the decoding mode for multi-frame images. + /// Used for allocating memory during the decoding operations. /// - public FrameDecodingMode DecodingMode { get; } + private readonly MemoryAllocator memoryAllocator; /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// The stream to decode from. /// - public bool IgnoreMetadata { get; } + private BufferedReadStream currentStream; /// - /// Gets the decoded by this decoder instance. + /// The webp specific metadata. /// - public ImageMetadata Metadata { get; private set; } + private WebpMetadata webpMetadata; /// - /// Gets the dimensions of the image. + /// Information about the webp image. /// - public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); + private WebpImageInfo webImageInfo; /// - /// Gets or sets the alpha data, if an ALPH chunk is present. + /// Initializes a new instance of the class. /// - public IMemoryOwner AlphaData { get; set; } + /// The decoder options. + public WebpDecoderCore(DecoderOptions options) + { + this.Options = options; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; + } + + /// + public DecoderOptions Options { get; } + + /// + public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) @@ -96,7 +98,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken Image image = null; try { - this.Metadata = new ImageMetadata(); + this.metadata = new ImageMetadata(); this.currentStream = stream; uint fileSize = this.ReadImageHeader(); @@ -105,7 +107,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken { if (this.webImageInfo.Features is { Animation: true }) { - using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.Configuration, this.DecodingMode); + using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.configuration, this.maxFrames); return animationDecoder.Decode(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize); } @@ -114,17 +116,17 @@ public Image Decode(BufferedReadStream stream, CancellationToken WebpThrowHelper.ThrowNotSupportedException("Animations are not supported"); } - image = new Image(this.Configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata); + image = new Image(this.configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); if (this.webImageInfo.IsLossless) { - var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.Configuration); + var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration); losslessDecoder.Decode(pixels, image.Width, image.Height); } else { - var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.Configuration); - lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo, this.AlphaData); + var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.configuration); + lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo, this.alphaData); } // There can be optional chunks after the image data, like EXIF and XMP. @@ -151,7 +153,7 @@ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancella this.ReadImageHeader(); using (this.webImageInfo = this.ReadVp8Info(true)) { - return new ImageInfo(new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata); + return new ImageInfo(new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata); } } @@ -182,8 +184,8 @@ private uint ReadImageHeader() /// Information about the webp image. private WebpImageInfo ReadVp8Info(bool ignoreAlpha = false) { - this.Metadata = new ImageMetadata(); - this.webpMetadata = this.Metadata.GetFormatMetadata(WebpFormat.Instance); + this.metadata = new ImageMetadata(); + this.webpMetadata = this.metadata.GetFormatMetadata(WebpFormat.Instance); WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer); @@ -277,7 +279,7 @@ private bool ParseOptionalExtendedChunks(WebpChunkType chunkType, WebpFeatures f /// The webp features. private void ParseOptionalChunks(WebpFeatures features) { - if (this.IgnoreMetadata || (features.ExifProfile == false && features.XmpMetaData == false)) + if (this.skipMetadata || (!features.ExifProfile && !features.XmpMetaData)) { return; } @@ -287,11 +289,11 @@ private void ParseOptionalChunks(WebpFeatures features) { // Read chunk header. WebpChunkType chunkType = this.ReadChunkType(); - if (chunkType == WebpChunkType.Exif && this.Metadata.ExifProfile == null) + if (chunkType == WebpChunkType.Exif && this.metadata.ExifProfile == null) { this.ReadExifProfile(); } - else if (chunkType == WebpChunkType.Xmp && this.Metadata.XmpProfile == null) + else if (chunkType == WebpChunkType.Xmp && this.metadata.XmpProfile == null) { this.ReadXmpProfile(); } @@ -310,7 +312,7 @@ private void ParseOptionalChunks(WebpFeatures features) private void ReadExifProfile() { uint exifChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)exifChunkSize); } @@ -325,7 +327,7 @@ private void ReadExifProfile() } var profile = new ExifProfile(exifData); - this.Metadata.ExifProfile = profile; + this.metadata.ExifProfile = profile; } } @@ -335,7 +337,7 @@ private void ReadExifProfile() private void ReadXmpProfile() { uint xmpChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)xmpChunkSize); } @@ -350,7 +352,7 @@ private void ReadXmpProfile() } var profile = new XmpProfile(xmpData); - this.Metadata.XmpProfile = profile; + this.metadata.XmpProfile = profile; } } @@ -360,7 +362,7 @@ private void ReadXmpProfile() private void ReadIccProfile() { uint iccpChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)iccpChunkSize); } @@ -376,7 +378,7 @@ private void ReadIccProfile() var profile = new IccProfile(iccpData); if (profile.CheckIsValid()) { - this.Metadata.IccProfile = profile; + this.metadata.IccProfile = profile; } } } @@ -419,8 +421,8 @@ private void ReadAlphaData(WebpFeatures features, bool ignoreAlpha) features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); int alphaDataSize = (int)(alphaChunkSize - 1); - this.AlphaData = this.memoryAllocator.Allocate(alphaDataSize); - Span alphaData = this.AlphaData.GetSpan(); + this.alphaData = this.memoryAllocator.Allocate(alphaDataSize); + Span alphaData = this.alphaData.GetSpan(); int bytesRead = this.currentStream.Read(alphaData, 0, alphaDataSize); if (bytesRead != alphaDataSize) { @@ -462,6 +464,6 @@ private uint ReadChunkSize() } /// - public void Dispose() => this.AlphaData?.Dispose(); + public void Dispose() => this.alphaData?.Dispose(); } } diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 3fcd44d3a8..efa337d6d0 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -44,14 +44,14 @@ internal static Image CreateUninitialized( /// /// By reading the header on the provided stream this calculates the images format. /// + /// The general configuration. /// The image stream to read the header from. - /// The configuration. /// The mime type or null if none found. - private static IImageFormat InternalDetectFormat(Stream stream, Configuration config) + private static IImageFormat InternalDetectFormat(Configuration configuration, Stream stream) { // We take a minimum of the stream length vs the max header size and always check below // to ensure that only formats that headers fit within the given buffer length are tested. - int headerSize = (int)Math.Min(config.MaxHeaderSize, stream.Length); + int headerSize = (int)Math.Min(configuration.MaxHeaderSize, stream.Length); if (headerSize <= 0) { return null; @@ -80,7 +80,7 @@ private static IImageFormat InternalDetectFormat(Stream stream, Configuration co // and does that data match the format specification? // Individual formats should still check since they are public. IImageFormat format = null; - foreach (IImageFormatDetector formatDetector in config.ImageFormatsManager.FormatDetectors) + foreach (IImageFormatDetector formatDetector in configuration.ImageFormatsManager.FormatDetectors) { if (formatDetector.HeaderSize <= headerSize) { @@ -98,73 +98,73 @@ private static IImageFormat InternalDetectFormat(Stream stream, Configuration co /// /// By reading the header on the provided stream this calculates the images format. /// + /// The general decoder options. /// The image stream to read the header from. - /// The configuration. /// The IImageFormat. /// The image format or null if none found. - private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format) + private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format) { - format = InternalDetectFormat(stream, config); + format = InternalDetectFormat(options.Configuration, stream); return format != null - ? config.ImageFormatsManager.FindDecoder(format) + ? options.Configuration.ImageFormatsManager.FindDecoder(format) : null; } /// /// Decodes the image stream to the current image. /// + /// The general decoder options. /// The stream. - /// the configuration. /// The token to monitor for cancellation requests. /// The pixel format. /// /// A new . /// - private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config, CancellationToken cancellationToken = default) + private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel { - IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); + IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); if (decoder is null) { return (null, null); } - Image img = decoder.Decode(config, stream, cancellationToken); + Image img = decoder.Decode(options, stream, cancellationToken); return (img, format); } - private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config, CancellationToken cancellationToken = default) + private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); + IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); if (decoder is null) { return (null, null); } - Image img = decoder.Decode(config, stream, cancellationToken); + Image img = decoder.Decode(options, stream, cancellationToken); return (img, format); } /// /// Reads the raw image information from the specified stream. /// + /// The general decoder options. /// The stream. - /// the configuration. /// The token to monitor for cancellation requests. /// /// The or null if a suitable info detector is not found. /// - private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(Stream stream, Configuration config, CancellationToken cancellationToken = default) + private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); + IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); if (decoder is not IImageInfoDetector detector) { return (null, null); } - IImageInfo info = detector?.Identify(config, stream, cancellationToken); + IImageInfo info = detector?.Identify(options, stream, cancellationToken); return (info, format); } } diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 342143bb5a..341961a2b9 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -9,47 +9,59 @@ namespace SixLabors.ImageSharp { /// - /// Adds static methods allowing the creation of new image from a byte array. + /// Adds static methods allowing the creation of new image from a byte span. /// public abstract partial class Image { /// - /// By reading the header on the provided byte array this calculates the images format. + /// By reading the header on the provided byte span this calculates the images format. /// - /// The byte array containing encoded image data to read the header from. - /// The data is null. + /// The byte span containing encoded image data to read the header from. /// The format or null if none found. - public static IImageFormat DetectFormat(byte[] data) - => DetectFormat(Configuration.Default, data); + public static IImageFormat DetectFormat(ReadOnlySpan data) + => DetectFormat(DecoderOptions.Default, data); /// - /// By reading the header on the provided byte array this calculates the images format. + /// By reading the header on the provided byte span this calculates the images format. /// - /// The configuration. - /// The byte array containing encoded image data to read the header from. - /// The configuration is null. - /// The data is null. + /// The general decoder options. + /// The byte span containing encoded image data to read the header from. + /// The options are null. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, byte[] data) + public static IImageFormat DetectFormat(DecoderOptions options, ReadOnlySpan data) { - Guard.NotNull(data, nameof(data)); + Guard.NotNull(options, nameof(options.Configuration)); - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) + Configuration configuration = options.Configuration; + int maxHeaderSize = configuration.MaxHeaderSize; + if (maxHeaderSize <= 0) { - return DetectFormat(configuration, stream); + return null; + } + + foreach (IImageFormatDetector detector in configuration.ImageFormatsManager.FormatDetectors) + { + IImageFormat f = detector.DetectFormat(data); + + if (f != null) + { + return f; + } } + + return default; } /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The byte array containing encoded image data to read the header from. + /// The byte span containing encoded image data to read the header from. /// The data is null. /// The data is not readable. /// /// The or null if suitable info detector not found. /// - public static IImageInfo Identify(byte[] data) => Identify(data, out IImageFormat _); + public static IImageInfo Identify(ReadOnlySpan data) => Identify(data, out IImageFormat _); /// /// Reads the raw image information from the specified stream without fully decoding it. @@ -61,13 +73,14 @@ public static IImageFormat DetectFormat(Configuration configuration, byte[] data /// /// The or null if suitable info detector not found. /// - public static IImageInfo Identify(byte[] data, out IImageFormat format) => Identify(Configuration.Default, data, out format); + public static IImageInfo Identify(ReadOnlySpan data, out IImageFormat format) + => Identify(DecoderOptions.Default, data, out format); /// - /// Reads the raw image information from the specified stream without fully decoding it. + /// Reads the raw image information from the specified span of bytes without fully decoding it. /// - /// The configuration. - /// The byte array containing encoded image data to read the header from. + /// The general decoder options. + /// The byte span containing encoded image data to read the header from. /// The format type of the decoded image. /// The configuration is null. /// The data is null. @@ -75,185 +88,15 @@ public static IImageFormat DetectFormat(Configuration configuration, byte[] data /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration configuration, byte[] data, out IImageFormat format) + public static unsafe IImageInfo Identify(DecoderOptions options, ReadOnlySpan data, out IImageFormat format) { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) + fixed (byte* ptr = data) { - return Identify(configuration, stream, out format); + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Identify(options, stream, out format); } } - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing image data. - /// The configuration is null. - /// The data is null. - /// A new . - public static Image Load(byte[] data) - => Load(Configuration.Default, data); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The pixel format. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(byte[] data) - where TPixel : unmanaged, IPixel - => Load(Configuration.Default, data); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing image data. - /// The mime type of the decoded image. - /// The pixel format. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(byte[] data, out IImageFormat format) - where TPixel : unmanaged, IPixel - => Load(Configuration.Default, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The configuration options. - /// The byte array containing encoded image data. - /// The pixel format. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(Configuration configuration, byte[] data) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Load(configuration, stream); - } - } - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The configuration options. - /// The byte array containing encoded image data. - /// The of the decoded image. - /// The pixel format. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Load(configuration, stream, out format); - } - } - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The decoder. - /// The pixel format. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(byte[] data, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Load(stream, decoder); - } - } - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The Configuration. - /// The byte array containing encoded image data. - /// The decoder. - /// The pixel format. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A new . - public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Load(configuration, stream, decoder); - } - } - - /// - /// By reading the header on the provided byte span this calculates the images format. - /// - /// The byte span containing encoded image data to read the header from. - /// The format or null if none found. - public static IImageFormat DetectFormat(ReadOnlySpan data) => DetectFormat(Configuration.Default, data); - - /// - /// By reading the header on the provided byte span this calculates the images format. - /// - /// The configuration. - /// The byte span containing encoded image data to read the header from. - /// The configuration is null. - /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpan data) - { - Guard.NotNull(configuration, nameof(configuration)); - - int maxHeaderSize = configuration.MaxHeaderSize; - if (maxHeaderSize <= 0) - { - return null; - } - - foreach (IImageFormatDetector detector in configuration.ImageFormatsManager.FormatDetectors) - { - IImageFormat f = detector.DetectFormat(data); - - if (f != null) - { - return f; - } - } - - return default; - } - /// /// Load a new instance of from the given encoded byte span. /// @@ -265,7 +108,7 @@ public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpa /// A new . public static Image Load(ReadOnlySpan data) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, data); + => Load(DecoderOptions.Default, data); /// /// Load a new instance of from the given encoded byte span. @@ -279,177 +122,51 @@ public static Image Load(ReadOnlySpan data) /// A new . public static Image Load(ReadOnlySpan data, out IImageFormat format) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, data, out format); + => Load(DecoderOptions.Default, data, out format); /// /// Load a new instance of from the given encoded byte span. /// + /// The general decoder options. /// The byte span containing encoded image data. - /// The decoder. - /// The pixel format. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// A new . - public static Image Load(ReadOnlySpan data, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - => Load(Configuration.Default, data, decoder); - - /// - /// Load a new instance of from the given encoded byte span. - /// - /// The configuration options. - /// The byte span containing encoded image data. - /// The pixel format. - /// The configuration is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// A new . - public static unsafe Image Load(Configuration configuration, ReadOnlySpan data) - where TPixel : unmanaged, IPixel - { - fixed (byte* ptr = &data.GetPinnableReference()) - { - using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) - { - return Load(configuration, stream); - } - } - } - - /// - /// Load a new instance of from the given encoded byte span. - /// - /// The Configuration. - /// The byte span containing image data. - /// The decoder. /// The pixel format. - /// The configuration is null. + /// The options are null. /// Image format not recognised. /// Image contains invalid content. /// Image format is not supported. /// A new . - public static unsafe Image Load( - Configuration configuration, - ReadOnlySpan data, - IImageDecoder decoder) + public static unsafe Image Load(DecoderOptions options, ReadOnlySpan data) where TPixel : unmanaged, IPixel { - fixed (byte* ptr = &data.GetPinnableReference()) + fixed (byte* ptr = data) { - using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) - { - return Load(configuration, stream, decoder); - } + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Load(options, stream); } } /// /// Load a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The general decoder options. /// The byte span containing image data. /// The of the decoded image. /// The pixel format. - /// The configuration is null. + /// The options are null. /// Image format not recognised. /// Image contains invalid content. /// Image format is not supported. /// A new . public static unsafe Image Load( - Configuration configuration, + DecoderOptions options, ReadOnlySpan data, out IImageFormat format) where TPixel : unmanaged, IPixel { - fixed (byte* ptr = &data.GetPinnableReference()) - { - using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) - { - return Load(configuration, stream, out format); - } - } - } - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing image data. - /// The detected format. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(byte[] data, out IImageFormat format) - => Load(Configuration.Default, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The decoder. - /// The data is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(byte[] data, IImageDecoder decoder) - => Load(Configuration.Default, data, decoder); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The configuration for the decoder. - /// The byte array containing encoded image data. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(Configuration configuration, byte[] data) - => Load(configuration, data, out _); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The configuration for the decoder. - /// The byte array containing image data. - /// The decoder. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) - { - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Load(configuration, stream, decoder); - } - } - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The configuration for the decoder. - /// The byte array containing image data. - /// The mime type of the decoded image. - /// The configuration is null. - /// The data is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) - { - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) + fixed (byte* ptr = data) { - return Load(configuration, stream, out format); + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Load(options, stream, out format); } } @@ -462,21 +179,7 @@ public static Image Load(Configuration configuration, byte[] data, out IImageFor /// Image format is not supported. /// The . public static Image Load(ReadOnlySpan data) - => Load(Configuration.Default, data); - - /// - /// Load a new instance of from the given encoded byte span. - /// - /// The byte span containing image data. - /// The decoder. - /// The data is null. - /// The decoder is null. - /// Image format not recognised. - /// Image contains invalid content. - /// Image format is not supported. - /// The . - public static Image Load(ReadOnlySpan data, IImageDecoder decoder) - => Load(Configuration.Default, data, decoder); + => Load(DecoderOptions.Default, data); /// /// Load a new instance of from the given encoded byte array. @@ -489,65 +192,37 @@ public static Image Load(ReadOnlySpan data, IImageDecoder decoder) /// Image format is not supported. /// The . public static Image Load(ReadOnlySpan data, out IImageFormat format) - => Load(Configuration.Default, data, out format); + => Load(DecoderOptions.Default, data, out format); /// /// Decodes a new instance of from the given encoded byte span. /// - /// The configuration options. + /// The general decoder options. /// The byte span containing image data. /// The . - public static Image Load(Configuration configuration, ReadOnlySpan data) - => Load(configuration, data, out _); + public static Image Load(DecoderOptions options, ReadOnlySpan data) + => Load(options, data, out _); /// /// Load a new instance of from the given encoded byte span. /// - /// The Configuration. - /// The byte span containing image data. - /// The decoder. - /// The configuration is null. - /// The decoder is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The . - public static unsafe Image Load( - Configuration configuration, - ReadOnlySpan data, - IImageDecoder decoder) - { - fixed (byte* ptr = &data.GetPinnableReference()) - { - using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) - { - return Load(configuration, stream, decoder); - } - } - } - - /// - /// Load a new instance of from the given encoded byte span. - /// - /// The configuration options. + /// The general decoder options. /// The byte span containing image data. /// The of the decoded image.> - /// The configuration is null. + /// The options are null. /// Image format not recognised. /// Image contains invalid content. /// Image format is not supported. /// The . public static unsafe Image Load( - Configuration configuration, + DecoderOptions options, ReadOnlySpan data, out IImageFormat format) { - fixed (byte* ptr = &data.GetPinnableReference()) + fixed (byte* ptr = data) { - using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) - { - return Load(configuration, stream, out format); - } + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Load(options, stream, out format); } } } diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 79c036d24d..6594ed2183 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -21,23 +21,21 @@ public abstract partial class Image /// The image file to open and to read the header from. /// The mime type or null if none found. public static IImageFormat DetectFormat(string filePath) - => DetectFormat(Configuration.Default, filePath); + => DetectFormat(DecoderOptions.Default, filePath); /// /// By reading the header on the provided file this calculates the images mime type. /// - /// The configuration. + /// The general decoder options. /// The image file to open and to read the header from. /// The configuration is null. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, string filePath) + public static IImageFormat DetectFormat(DecoderOptions options, string filePath) { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); - using (Stream file = configuration.FileSystem.OpenRead(filePath)) - { - return DetectFormat(configuration, file); - } + using Stream file = options.Configuration.FileSystem.OpenRead(filePath); + return DetectFormat(options, file); } /// @@ -59,25 +57,23 @@ public static IImageInfo Identify(string filePath) /// The or null if suitable info detector not found. /// public static IImageInfo Identify(string filePath, out IImageFormat format) - => Identify(Configuration.Default, filePath, out format); + => Identify(DecoderOptions.Default, filePath, out format); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image file to open and to read the header from. /// The format type of the decoded image. /// The configuration is null. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration configuration, string filePath, out IImageFormat format) + public static IImageInfo Identify(DecoderOptions options, string filePath, out IImageFormat format) { - Guard.NotNull(configuration, nameof(configuration)); - using (Stream file = configuration.FileSystem.OpenRead(filePath)) - { - return Identify(configuration, file, out format); - } + Guard.NotNull(options, nameof(options)); + using Stream file = options.Configuration.FileSystem.OpenRead(filePath); + return Identify(options, file, out format); } /// @@ -91,12 +87,12 @@ public static IImageInfo Identify(Configuration configuration, string filePath, /// property set to null if suitable info detector is not found. /// public static Task IdentifyAsync(string filePath, CancellationToken cancellationToken = default) - => IdentifyAsync(Configuration.Default, filePath, cancellationToken); + => IdentifyAsync(DecoderOptions.Default, filePath, cancellationToken); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image file to open and to read the header from. /// The token to monitor for cancellation requests. /// The configuration is null. @@ -105,12 +101,12 @@ public static Task IdentifyAsync(string filePath, CancellationToken /// property set to null if suitable info detector is not found. /// public static async Task IdentifyAsync( - Configuration configuration, + DecoderOptions options, string filePath, CancellationToken cancellationToken = default) { - (IImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(configuration, filePath, cancellationToken) - .ConfigureAwait(false); + (IImageInfo ImageInfo, IImageFormat Format) res = + await IdentifyWithFormatAsync(options, filePath, cancellationToken).ConfigureAwait(false); return res.ImageInfo; } @@ -127,12 +123,12 @@ public static async Task IdentifyAsync( public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync( string filePath, CancellationToken cancellationToken = default) - => IdentifyWithFormatAsync(Configuration.Default, filePath, cancellationToken); + => IdentifyWithFormatAsync(DecoderOptions.Default, filePath, cancellationToken); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image file to open and to read the header from. /// The token to monitor for cancellation requests. /// The configuration is null. @@ -141,13 +137,13 @@ public static async Task IdentifyAsync( /// property set to null if suitable info detector is not found. /// public static async Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync( - Configuration configuration, + DecoderOptions options, string filePath, CancellationToken cancellationToken = default) { - Guard.NotNull(configuration, nameof(configuration)); - using Stream stream = configuration.FileSystem.OpenRead(filePath); - return await IdentifyWithFormatAsync(configuration, stream, cancellationToken) + Guard.NotNull(options, nameof(options)); + using Stream stream = options.Configuration.FileSystem.OpenRead(filePath); + return await IdentifyWithFormatAsync(options, stream, cancellationToken) .ConfigureAwait(false); } @@ -160,7 +156,7 @@ public static async Task IdentifyAsync( /// /// The . public static Image Load(string path) - => Load(Configuration.Default, path); + => Load(DecoderOptions.Default, path); /// /// Create a new instance of the class from the given file. @@ -172,12 +168,12 @@ public static Image Load(string path) /// /// A new . public static Image Load(string path, out IImageFormat format) - => Load(Configuration.Default, path, out format); + => Load(DecoderOptions.Default, path, out format); /// /// Create a new instance of the class from the given file. /// - /// The configuration for the decoder. + /// The general decoder options. /// The file path to the image. /// The configuration is null. /// The path is null. @@ -185,13 +181,13 @@ public static Image Load(string path, out IImageFormat format) /// Image format is not supported. /// Image contains invalid content. /// The . - public static Image Load(Configuration configuration, string path) - => Load(configuration, path, out _); + public static Image Load(DecoderOptions options, string path) + => Load(options, path, out _); /// /// Create a new instance of the class from the given file. /// - /// The configuration for the decoder. + /// The general decoder options. /// The file path to the image. /// The token to monitor for cancellation requests. /// The configuration is null. @@ -201,40 +197,16 @@ public static Image Load(Configuration configuration, string path) /// Image contains invalid content. /// A representing the asynchronous operation. public static async Task LoadAsync( - Configuration configuration, + DecoderOptions options, string path, CancellationToken cancellationToken = default) { - using Stream stream = configuration.FileSystem.OpenRead(path); - (Image img, _) = await LoadWithFormatAsync(configuration, stream, cancellationToken) + using Stream stream = options.Configuration.FileSystem.OpenRead(path); + (Image img, _) = await LoadWithFormatAsync(options, stream, cancellationToken) .ConfigureAwait(false); return img; } - /// - /// Create a new instance of the class from the given file. - /// - /// The Configuration. - /// The file path to the image. - /// The decoder. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The . - public static Image Load(Configuration configuration, string path, IImageDecoder decoder) - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(path, nameof(path)); - - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return Load(configuration, stream, decoder); - } - } - /// /// Create a new instance of the class from the given file. /// @@ -248,99 +220,7 @@ public static Image Load(Configuration configuration, string path, IImageDecoder /// Image contains invalid content. /// A representing the asynchronous operation. public static Task LoadAsync(string path, CancellationToken cancellationToken = default) - => LoadAsync(Configuration.Default, path, cancellationToken); - - /// - /// Create a new instance of the class from the given file. - /// - /// The file path to the image. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A representing the asynchronous operation. - public static Task LoadAsync(string path, IImageDecoder decoder, CancellationToken cancellationToken = default) - => LoadAsync(Configuration.Default, path, decoder, cancellationToken); - - /// - /// Create a new instance of the class from the given file. - /// - /// The file path to the image. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The pixel format. - /// A representing the asynchronous operation. - public static Task> LoadAsync(string path, IImageDecoder decoder, CancellationToken cancellationToken = default) - where TPixel : unmanaged, IPixel - => LoadAsync(Configuration.Default, path, decoder, cancellationToken); - - /// - /// Create a new instance of the class from the given file. - /// - /// The Configuration. - /// The file path to the image. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// A representing the asynchronous operation. - public static async Task LoadAsync( - Configuration configuration, - string path, - IImageDecoder decoder, - CancellationToken cancellationToken = default) - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(path, nameof(path)); - - using Stream stream = configuration.FileSystem.OpenRead(path); - return await LoadAsync(configuration, stream, decoder, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Create a new instance of the class from the given file. - /// - /// The Configuration. - /// The file path to the image. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The pixel format. - /// A representing the asynchronous operation. - public static async Task> LoadAsync( - Configuration configuration, - string path, - IImageDecoder decoder, - CancellationToken cancellationToken = default) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(path, nameof(path)); - - using Stream stream = configuration.FileSystem.OpenRead(path); - return await LoadAsync(configuration, stream, decoder, cancellationToken) - .ConfigureAwait(false); - } + => LoadAsync(DecoderOptions.Default, path, cancellationToken); /// /// Create a new instance of the class from the given file. @@ -356,12 +236,12 @@ public static async Task> LoadAsync( /// A representing the asynchronous operation. public static Task> LoadAsync(string path, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel - => LoadAsync(Configuration.Default, path, cancellationToken); + => LoadAsync(DecoderOptions.Default, path, cancellationToken); /// /// Create a new instance of the class from the given file. /// - /// The configuration for the decoder. + /// The general decoder options. /// The file path to the image. /// The token to monitor for cancellation requests. /// The configuration is null. @@ -372,31 +252,19 @@ public static Task> LoadAsync(string path, CancellationTok /// The pixel format. /// A representing the asynchronous operation. public static async Task> LoadAsync( - Configuration configuration, + DecoderOptions options, string path, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel { - using Stream stream = configuration.FileSystem.OpenRead(path); - (Image img, _) = await LoadWithFormatAsync(configuration, stream, cancellationToken) - .ConfigureAwait(false); + Guard.NotNull(options, nameof(options)); + + using Stream stream = options.Configuration.FileSystem.OpenRead(path); + (Image img, _) = + await LoadWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false); return img; } - /// - /// Create a new instance of the class from the given file. - /// - /// The file path to the image. - /// The decoder. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The . - public static Image Load(string path, IImageDecoder decoder) - => Load(Configuration.Default, path, decoder); - /// /// Create a new instance of the class from the given file. /// @@ -409,7 +277,7 @@ public static Image Load(string path, IImageDecoder decoder) /// A new . public static Image Load(string path) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, path); + => Load(DecoderOptions.Default, path); /// /// Create a new instance of the class from the given file. @@ -424,12 +292,12 @@ public static Image Load(string path) /// A new . public static Image Load(string path, out IImageFormat format) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, path, out format); + => Load(DecoderOptions.Default, path, out format); /// /// Create a new instance of the class from the given file. /// - /// The configuration options. + /// The general decoder options. /// The file path to the image. /// The configuration is null. /// The path is null. @@ -438,22 +306,20 @@ public static Image Load(string path, out IImageFormat format) /// Image contains invalid content. /// The pixel format. /// A new . - public static Image Load(Configuration configuration, string path) + public static Image Load(DecoderOptions options, string path) where TPixel : unmanaged, IPixel { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); Guard.NotNull(path, nameof(path)); - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return Load(configuration, stream); - } + using Stream stream = options.Configuration.FileSystem.OpenRead(path); + return Load(options, stream); } /// /// Create a new instance of the class from the given file. /// - /// The configuration options. + /// The general decoder options. /// The file path to the image. /// The mime type of the decoded image. /// The configuration is null. @@ -463,23 +329,21 @@ public static Image Load(Configuration configuration, string pat /// Image contains invalid content. /// The pixel format. /// A new . - public static Image Load(Configuration configuration, string path, out IImageFormat format) + public static Image Load(DecoderOptions options, string path, out IImageFormat format) where TPixel : unmanaged, IPixel { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); Guard.NotNull(path, nameof(path)); - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return Load(configuration, stream, out format); - } + using Stream stream = options.Configuration.FileSystem.OpenRead(path); + return Load(options, stream, out format); } /// /// Create a new instance of the class from the given file. /// The pixel type is selected by the decoder. /// - /// The configuration options. + /// The general decoder options. /// The file path to the image. /// The mime type of the decoded image. /// The configuration is null. @@ -488,56 +352,13 @@ public static Image Load(Configuration configuration, string pat /// Image format is not supported. /// Image contains invalid content. /// A new . - public static Image Load(Configuration configuration, string path, out IImageFormat format) - { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(path, nameof(path)); - - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return Load(configuration, stream, out format); - } - } - - /// - /// Create a new instance of the class from the given file. - /// - /// The file path to the image. - /// The decoder. - /// The path is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The pixel format. - /// A new . - public static Image Load(string path, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - => Load(Configuration.Default, path, decoder); - - /// - /// Create a new instance of the class from the given file. - /// - /// The Configuration. - /// The file path to the image. - /// The decoder. - /// The configuration is null. - /// The path is null. - /// The decoder is null. - /// Image format not recognised. - /// Image format is not supported. - /// Image contains invalid content. - /// The pixel format. - /// A new . - public static Image Load(Configuration configuration, string path, IImageDecoder decoder) - where TPixel : unmanaged, IPixel + public static Image Load(DecoderOptions options, string path, out IImageFormat format) { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); Guard.NotNull(path, nameof(path)); - using (Stream stream = configuration.FileSystem.OpenRead(path)) - { - return Load(configuration, stream, decoder); - } + using Stream stream = options.Configuration.FileSystem.OpenRead(path); + return Load(options, stream, out format); } } } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 803d8a7544..5c73590aa1 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Threading; @@ -26,19 +27,19 @@ public abstract partial class Image /// The stream is not readable. /// The format type or null if none found. public static IImageFormat DetectFormat(Stream stream) - => DetectFormat(Configuration.Default, stream); + => DetectFormat(DecoderOptions.Default, stream); /// /// By reading the header on the provided stream this calculates the images format type. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the header from. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// The format type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, Stream stream) - => WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration)); + public static IImageFormat DetectFormat(DecoderOptions options, Stream stream) + => WithSeekableStream(options, stream, s => InternalDetectFormat(options.Configuration, s)); /// /// By reading the header on the provided stream this calculates the images format type. @@ -49,23 +50,23 @@ public static IImageFormat DetectFormat(Configuration configuration, Stream stre /// The stream is not readable. /// A representing the asynchronous operation or null if none is found. public static Task DetectFormatAsync(Stream stream, CancellationToken cancellationToken = default) - => DetectFormatAsync(Configuration.Default, stream, cancellationToken); + => DetectFormatAsync(DecoderOptions.Default, stream, cancellationToken); /// /// By reading the header on the provided stream this calculates the images format type. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the header from. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// A representing the asynchronous operation. - public static Task DetectFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default) + public static Task DetectFormatAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) => WithSeekableStreamAsync( - configuration, + options, stream, - (s, _) => InternalDetectFormat(s, configuration), + (s, _) => InternalDetectFormat(options.Configuration, s), cancellationToken); /// @@ -94,7 +95,7 @@ public static IImageInfo Identify(Stream stream) /// a suitable detector is not found. /// public static Task IdentifyAsync(Stream stream, CancellationToken cancellationToken = default) - => IdentifyAsync(Configuration.Default, stream, cancellationToken); + => IdentifyAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Reads the raw image information from the specified stream without fully decoding it. @@ -108,30 +109,30 @@ public static Task IdentifyAsync(Stream stream, CancellationToken ca /// The or null if a suitable info detector is not found. /// public static IImageInfo Identify(Stream stream, out IImageFormat format) - => Identify(Configuration.Default, stream, out format); + => Identify(DecoderOptions.Default, stream, out format); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the information from. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// Image contains invalid content. /// /// The or null if a suitable info detector is not found. /// - public static IImageInfo Identify(Configuration configuration, Stream stream) - => Identify(configuration, stream, out _); + public static IImageInfo Identify(DecoderOptions options, Stream stream) + => Identify(options, stream, out _); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the information from. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// Image contains invalid content. @@ -140,30 +141,30 @@ public static IImageInfo Identify(Configuration configuration, Stream stream) /// a suitable detector is not found. /// public static async Task IdentifyAsync( - Configuration configuration, + DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - (IImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(false); + (IImageInfo ImageInfo, IImageFormat Format) res = await IdentifyWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false); return res.ImageInfo; } /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the information from. /// The format type of the decoded image. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// Image contains invalid content. /// /// The or null if a suitable info detector is not found. /// - public static IImageInfo Identify(Configuration configuration, Stream stream, out IImageFormat format) + public static IImageInfo Identify(DecoderOptions options, Stream stream, out IImageFormat format) { - (IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(configuration, stream, s => InternalIdentity(s, configuration ?? Configuration.Default)); + (IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentity(options, s)); format = data.Format; return data.ImageInfo; @@ -174,7 +175,7 @@ public static IImageInfo Identify(Configuration configuration, Stream stream, ou /// /// The image stream to read the information from. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// Image contains invalid content. @@ -185,15 +186,15 @@ public static IImageInfo Identify(Configuration configuration, Stream stream, ou public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync( Stream stream, CancellationToken cancellationToken = default) - => IdentifyWithFormatAsync(Configuration.Default, stream, cancellationToken); + => IdentifyWithFormatAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Reads the raw image information from the specified stream without fully decoding it. /// - /// The configuration. + /// The general decoder options. /// The image stream to read the information from. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable. /// Image contains invalid content. @@ -202,13 +203,13 @@ public static IImageInfo Identify(Configuration configuration, Stream stream, ou /// property set to null if suitable info detector is not found. /// public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync( - Configuration configuration, + DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) => WithSeekableStreamAsync( - configuration, + options, stream, - (s, ct) => InternalIdentity(s, configuration ?? Configuration.Default, ct), + (s, ct) => InternalIdentity(options, s, ct), cancellationToken); /// @@ -223,7 +224,7 @@ public static IImageInfo Identify(Configuration configuration, Stream stream, ou /// Image contains invalid content. /// The . public static Image Load(Stream stream, out IImageFormat format) - => Load(Configuration.Default, stream, out format); + => Load(DecoderOptions.Default, stream, out format); /// /// Decode a new instance of the class from the given stream. @@ -237,7 +238,7 @@ public static Image Load(Stream stream, out IImageFormat format) /// Image contains invalid content. /// A representing the asynchronous operation. public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream, CancellationToken cancellationToken = default) - => LoadWithFormatAsync(Configuration.Default, stream, cancellationToken); + => LoadWithFormatAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Decode a new instance of the class from the given stream. @@ -249,7 +250,7 @@ public static Image Load(Stream stream, out IImageFormat format) /// Image format not recognised. /// Image contains invalid content. /// The . - public static Image Load(Stream stream) => Load(Configuration.Default, stream); + public static Image Load(Stream stream) => Load(DecoderOptions.Default, stream); /// /// Decode a new instance of the class from the given stream. @@ -263,116 +264,37 @@ public static Image Load(Stream stream, out IImageFormat format) /// Image contains invalid content. /// A representing the asynchronous operation. public static Task LoadAsync(Stream stream, CancellationToken cancellationToken = default) - => LoadAsync(Configuration.Default, stream, cancellationToken); - - /// - /// Decode a new instance of the class from the given stream. - /// The pixel format is selected by the decoder. - /// - /// The stream containing image information. - /// The decoder. - /// The stream is null. - /// The decoder is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The . - public static Image Load(Stream stream, IImageDecoder decoder) - => Load(Configuration.Default, stream, decoder); - - /// - /// Decode a new instance of the class from the given stream. - /// The pixel format is selected by the decoder. - /// - /// The stream containing image information. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The stream is null. - /// The decoder is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// A representing the asynchronous operation. - public static Task LoadAsync(Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default) - => LoadAsync(Configuration.Default, stream, decoder, cancellationToken); - - /// - /// Decode a new instance of the class from the given stream. - /// The pixel format is selected by the decoder. - /// - /// The configuration for the decoder. - /// The stream containing image information. - /// The decoder. - /// The configuration is null. - /// The stream is null. - /// The decoder is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// A new . - public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) - { - Guard.NotNull(decoder, nameof(decoder)); - return WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s, default)); - } - - /// - /// Decode a new instance of the class from the given stream. - /// The pixel format is selected by the decoder. - /// - /// The configuration for the decoder. - /// The stream containing image information. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The stream is null. - /// The decoder is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// A representing the asynchronous operation. - public static Task LoadAsync( - Configuration configuration, - Stream stream, - IImageDecoder decoder, - CancellationToken cancellationToken = default) - { - Guard.NotNull(decoder, nameof(decoder)); - return WithSeekableStreamAsync( - configuration, - stream, - (s, ct) => decoder.Decode(configuration, s, ct), - cancellationToken); - } + => LoadAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Decode a new instance of the class from the given stream. /// - /// The configuration for the decoder. + /// The general decoder options. /// The stream containing image information. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// A new . - public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _); + public static Image Load(DecoderOptions options, Stream stream) + => Load(options, stream, out _); /// /// Decode a new instance of the class from the given stream. /// - /// The configuration for the decoder. + /// The general decoder options. /// The stream containing image information. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// A representing the asynchronous operation. - public static async Task LoadAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default) + public static async Task LoadAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - (Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(configuration, stream, cancellationToken) + (Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(options, stream, cancellationToken) .ConfigureAwait(false); return fmt.Image; } @@ -389,7 +311,7 @@ public static async Task LoadAsync(Configuration configuration, Stream st /// A new . public static Image Load(Stream stream) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, stream); + => Load(DecoderOptions.Default, stream); /// /// Create a new instance of the class from the given stream. @@ -404,7 +326,7 @@ public static Image Load(Stream stream) /// A representing the asynchronous operation. public static Task> LoadAsync(Stream stream, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel - => LoadAsync(Configuration.Default, stream, cancellationToken); + => LoadAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Create a new instance of the class from the given stream. @@ -419,7 +341,7 @@ public static Task> LoadAsync(Stream stream, CancellationT /// A new . public static Image Load(Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel - => Load(Configuration.Default, stream, out format); + => Load(DecoderOptions.Default, stream, out format); /// /// Create a new instance of the class from the given stream. @@ -434,185 +356,88 @@ public static Image Load(Stream stream, out IImageFormat format) /// A representing the asynchronous operation. public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel - => LoadWithFormatAsync(Configuration.Default, stream, cancellationToken); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The stream containing image information. - /// The decoder. - /// The stream is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The pixel format. - /// A new . - public static Image Load(Stream stream, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - => WithSeekableStream(Configuration.Default, stream, s => decoder.Decode(Configuration.Default, s, default)); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The stream containing image information. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The stream is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The pixel format. - /// A representing the asynchronous operation. - public static Task> LoadAsync(Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default) - where TPixel : unmanaged, IPixel - => WithSeekableStreamAsync( - Configuration.Default, - stream, - (s, ct) => decoder.Decode(Configuration.Default, s, ct), - cancellationToken); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The Configuration. - /// The stream containing image information. - /// The decoder. - /// The configuration is null. - /// The stream is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The pixel format. - /// A new . - public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) - where TPixel : unmanaged, IPixel - => WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s, default)); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The Configuration. - /// The stream containing image information. - /// The decoder. - /// The token to monitor for cancellation requests. - /// The configuration is null. - /// The stream is null. - /// The stream is not readable or the image format is not supported. - /// Image format not recognised. - /// Image contains invalid content. - /// The pixel format. - /// A representing the asynchronous operation. - public static Task> LoadAsync( - Configuration configuration, - Stream stream, - IImageDecoder decoder, - CancellationToken cancellationToken = default) - where TPixel : unmanaged, IPixel - => WithSeekableStreamAsync( - configuration, - stream, - (s, ct) => decoder.Decode(configuration, s, ct), - cancellationToken); + => LoadWithFormatAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// The pixel format. /// A new . - public static Image Load(Configuration configuration, Stream stream) + public static Image Load(DecoderOptions options, Stream stream) where TPixel : unmanaged, IPixel - => Load(configuration, stream, out IImageFormat _); + => Load(options, stream, out IImageFormat _); /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. /// The format type of the decoded image. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// The pixel format. /// A representing the asynchronous operation. - public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) + public static Image Load(DecoderOptions options, Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel { - (Image Image, IImageFormat Format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration)); + (Image Image, IImageFormat Format) data = WithSeekableStream(options, stream, s => Decode(options, s)); format = data.Format; - if (data.Image != null) - { - return data.Image; - } - - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); - - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + if (data.Image is null) { - sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); + ThrowNotLoaded(options); } - throw new UnknownImageFormatException(sb.ToString()); + return data.Image; } /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// A representing the asynchronous operation. public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync( - Configuration configuration, + DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - (Image Image, IImageFormat Format) data = await WithSeekableStreamAsync( - configuration, - stream, - (s, ct) => Decode(s, configuration, ct), - cancellationToken) - .ConfigureAwait(false); - - if (data.Image != null) - { - return data; - } - - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); + (Image Image, IImageFormat Format) data = + await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken) + .ConfigureAwait(false); - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + if (data.Image is null) { - sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); + ThrowNotLoaded(options); } - throw new UnknownImageFormatException(sb.ToString()); + return data; } /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. @@ -620,42 +445,30 @@ public static Image Load(Configuration configuration, Stream str /// The pixel format. /// A representing the asynchronous operation. public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync( - Configuration configuration, + DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel { (Image Image, IImageFormat Format) data = - await WithSeekableStreamAsync( - configuration, - stream, - (s, ct) => Decode(s, configuration, ct), - cancellationToken) - .ConfigureAwait(false); + await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken) + .ConfigureAwait(false); - if (data.Image != null) + if (data.Image is null) { - return data; + ThrowNotLoaded(options); } - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); - - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) - { - sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); - } - - throw new UnknownImageFormatException(sb.ToString()); + return data; } /// /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. @@ -663,13 +476,13 @@ await WithSeekableStreamAsync( /// The pixel format. /// A representing the asynchronous operation. public static async Task> LoadAsync( - Configuration configuration, + DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) where TPixel : unmanaged, IPixel { - (Image img, _) = await LoadWithFormatAsync(configuration, stream, cancellationToken) - .ConfigureAwait(false); + (Image img, _) = await LoadWithFormatAsync(options, stream, cancellationToken) + .ConfigureAwait(false); return img; } @@ -677,51 +490,43 @@ public static async Task> LoadAsync( /// Decode a new instance of the class from the given stream. /// The pixel format is selected by the decoder. /// - /// The configuration options. + /// The general decoder options. /// The stream containing image information. /// The format type of the decoded image. - /// The configuration is null. + /// The options are null. /// The stream is null. /// The stream is not readable or the image format is not supported. /// Image format not recognised. /// Image contains invalid content. /// A new . - public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) + public static Image Load(DecoderOptions options, Stream stream, out IImageFormat format) { - (Image Img, IImageFormat Format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration)); - - format = data.Format; + (Image img, IImageFormat fmt) = WithSeekableStream(options, stream, s => Decode(options, s)); - if (data.Img != null) - { - return data.Img; - } - - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); + format = fmt; - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + if (img is null) { - sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); + ThrowNotLoaded(options); } - throw new UnknownImageFormatException(sb.ToString()); + return img; } /// /// Performs the given action against the stream ensuring that it is seekable. /// /// The type of object returned from the action. - /// The configuration. + /// The general decoder options. /// The input stream. /// The action to perform. /// The . - private static T WithSeekableStream( - Configuration configuration, + internal static T WithSeekableStream( + DecoderOptions options, Stream stream, Func action) { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); if (!stream.CanRead) @@ -729,6 +534,7 @@ private static T WithSeekableStream( throw new NotSupportedException("Cannot read from the stream."); } + Configuration configuration = options.Configuration; if (stream.CanSeek) { if (configuration.ReadOrigin == ReadOrigin.Begin) @@ -751,18 +557,18 @@ private static T WithSeekableStream( /// Performs the given action asynchronously against the stream ensuring that it is seekable. /// /// The type of object returned from the action. - /// The configuration. + /// The general decoder options. /// The input stream. /// The action to perform. /// The cancellation token. /// The . - private static async Task WithSeekableStreamAsync( - Configuration configuration, + internal static async Task WithSeekableStreamAsync( + DecoderOptions options, Stream stream, Func action, CancellationToken cancellationToken) { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); if (!stream.CanRead) @@ -770,6 +576,7 @@ private static async Task WithSeekableStreamAsync( throw new NotSupportedException("Cannot read from the stream."); } + Configuration configuration = options.Configuration; if (stream.CanSeek) { if (configuration.ReadOrigin == ReadOrigin.Begin) @@ -788,5 +595,19 @@ private static async Task WithSeekableStreamAsync( return action(memoryStream, cancellationToken); } + + [DoesNotReturn] + private static void ThrowNotLoaded(DecoderOptions options) + { + var sb = new StringBuilder(); + sb.AppendLine("Image cannot be loaded. Available decoders:"); + + foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders) + { + sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); + } + + throw new UnknownImageFormatException(sb.ToString()); + } } } diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs index 93563c794d..a59f5a34ce 100644 --- a/src/ImageSharp/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image.LoadPixelData.cs @@ -16,20 +16,7 @@ public abstract partial class Image /// /// Create a new instance of the class from the raw data. /// - /// The byte array containing image data. - /// The width of the final image. - /// The height of the final image. - /// The pixel format. - /// The data length is incorrect. - /// A new . - public static Image LoadPixelData(TPixel[] data, int width, int height) - where TPixel : unmanaged, IPixel - => LoadPixelData(Configuration.Default, data, width, height); - - /// - /// Create a new instance of the class from the raw data. - /// - /// The byte array containing image data. + /// The readonly span of bytes containing image data. /// The width of the final image. /// The height of the final image. /// The pixel format. @@ -40,22 +27,9 @@ public static Image LoadPixelData(ReadOnlySpan data, int => LoadPixelData(Configuration.Default, data, width, height); /// - /// Create a new instance of the class from the given byte array in format. + /// Create a new instance of the class from the given readonly span of bytes in format. /// - /// The byte array containing image data. - /// The width of the final image. - /// The height of the final image. - /// The pixel format. - /// The data length is incorrect. - /// A new . - public static Image LoadPixelData(byte[] data, int width, int height) - where TPixel : unmanaged, IPixel - => LoadPixelData(Configuration.Default, data, width, height); - - /// - /// Create a new instance of the class from the given byte array in format. - /// - /// The byte array containing image data. + /// The readonly span of bytes containing image data. /// The width of the final image. /// The height of the final image. /// The pixel format. @@ -66,25 +40,10 @@ public static Image LoadPixelData(ReadOnlySpan data, int w => LoadPixelData(Configuration.Default, data, width, height); /// - /// Create a new instance of the class from the given byte array in format. - /// - /// The configuration for the decoder. - /// The byte array containing image data. - /// The width of the final image. - /// The height of the final image. - /// The pixel format. - /// The configuration is null. - /// The data length is incorrect. - /// A new . - public static Image LoadPixelData(Configuration configuration, byte[] data, int width, int height) - where TPixel : unmanaged, IPixel - => LoadPixelData(configuration, MemoryMarshal.Cast(new ReadOnlySpan(data)), width, height); - - /// - /// Create a new instance of the class from the given byte array in format. + /// Create a new instance of the class from the given readonly span of bytes in format. /// /// The configuration for the decoder. - /// The byte array containing image data. + /// The readonly span of bytes containing image data. /// The width of the final image. /// The height of the final image. /// The pixel format. @@ -99,22 +58,7 @@ public static Image LoadPixelData(Configuration configuration, R /// Create a new instance of the class from the raw data. /// /// The configuration for the decoder. - /// The Span containing the image Pixel data. - /// The width of the final image. - /// The height of the final image. - /// The pixel format. - /// The configuration is null. - /// The data length is incorrect. - /// A new . - public static Image LoadPixelData(Configuration configuration, TPixel[] data, int width, int height) - where TPixel : unmanaged, IPixel - => LoadPixelData(configuration, new ReadOnlySpan(data), width, height); - - /// - /// Create a new instance of the class from the raw data. - /// - /// The configuration for the decoder. - /// The Span containing the image Pixel data. + /// The readonly span containing the image pixel data. /// The width of the final image. /// The height of the final image. /// The configuration is null. diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 79daa7df48..1fee65fa0b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -3,6 +3,7 @@ using System.IO; using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; @@ -24,7 +25,7 @@ private void GenericSetup(string imageSubpath) private void GenericBechmark() { this.preloadedImageStream.Position = 0; - using Image img = this.decoder.Decode(Configuration.Default, this.preloadedImageStream, default); + using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream); } [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] @@ -64,7 +65,6 @@ public void Cleanup() } } - /* 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.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 34aa111448..546051772e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -38,8 +38,10 @@ 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; - using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder { IgnoreMetadata = true }); + using var decoder = new JpegDecoderCore(options); var spectralConverter = new NoopSpectralConverter(); decoder.ParseStream(bufferedStream, spectralConverter, cancellationToken: default); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index 5b3fa68b8e..d6d699c0c8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; @@ -18,22 +17,22 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Config(typeof(Config.ShortMultiFramework))] public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase { - protected override IEnumerable InputImageSubfoldersOrFiles => - new[] - { - TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, - TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, - TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, - TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, - TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, - }; + protected override IEnumerable InputImageSubfoldersOrFiles + => new[] + { + TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, + TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + }; [Params(InputImageCategory.AllImages)] public override InputImageCategory InputCategory { get; set; } [Benchmark] public void ImageSharp() - => this.ForEachStream(ms => Image.Load(ms, new JpegDecoder())); + => this.ForEachStream(ms => Image.Load(ms)); [Benchmark(Baseline = true)] public void SystemDrawing() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 2c0c109978..f704bef280 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -3,8 +3,7 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; using SDSize = System.Drawing.Size; @@ -47,25 +46,17 @@ public void ReadImages() [Benchmark(Baseline = true)] public SDSize SystemDrawing() { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = SDImage.FromStream(memoryStream)) - { - return image.Size; - } - } + using var memoryStream = new MemoryStream(this.jpegBytes); + using var image = SDImage.FromStream(memoryStream); + return image.Size; } [Benchmark] public Size ImageSharp() { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = Image.Load(memoryStream, new JpegDecoder { IgnoreMetadata = true })) - { - return new Size(image.Width, image.Height); - } - } + using var memoryStream = new MemoryStream(this.jpegBytes); + using var image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); + return new Size(image.Width, image.Height); } /* diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index b9f1e72fcd..a9cbb418a2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -3,6 +3,7 @@ using System.IO; using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; @@ -31,8 +32,8 @@ public void ReadImages() public IImageInfo Identify() { using var memoryStream = new MemoryStream(this.jpegBytes); - var decoder = new JpegDecoder(); - return decoder.Identify(Configuration.Default, memoryStream, default); + IImageDecoder decoder = new JpegDecoder(); + return decoder.Identify(DecoderOptions.Default, memoryStream, default); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index 4a96a2b533..9e60fecacc 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -7,7 +7,6 @@ using BenchmarkDotNet.Attributes; using ImageMagick; using Pfim; -using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -18,7 +17,7 @@ public class DecodeTga { private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - private readonly PfimConfig pfimConfig = new PfimConfig(allocator: new PfimAllocator()); + private readonly PfimConfig pfimConfig = new(allocator: new PfimAllocator()); private byte[] data; @@ -40,7 +39,7 @@ public int TgaImageMagick() [Benchmark(Description = "ImageSharp Tga")] public int TgaImageSharp() { - using var image = Image.Load(this.data, new TgaDecoder()); + using var image = Image.Load(this.data); return image.Width; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs index 55d2a83452..f49310b9c3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs @@ -55,7 +55,7 @@ public int WebpLossyMagick() public int WebpLossy() { using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = Image.Load(this.configuration, memoryStream); + using var image = Image.Load(memoryStream); return image.Height; } @@ -72,7 +72,7 @@ public int WebpLosslessMagick() public int WebpLossless() { using var memoryStream = new MemoryStream(this.webpLosslessBytes); - using var image = Image.Load(this.configuration, memoryStream); + using var image = Image.Load(memoryStream); return image.Height; } diff --git a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs index 68a2bcde97..7712fc4e69 100644 --- a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs +++ b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs @@ -1,7 +1,6 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -12,7 +11,8 @@ public class Buffer2D_DangerousGetRowSpan { private const int Height = 1024; - [Params(0.5, 2.0, 10.0)] public double SizeMegaBytes { get; set; } + [Params(0.5, 2.0, 10.0)] + public double SizeMegaBytes { get; set; } private Buffer2D buffer; diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index 50abcf89d8..da71ce4dac 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using ImageMagick; using PhotoSauce.MagicScaler; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -159,7 +160,7 @@ private void LogImageProcessed(int width, int height) this.TotalProcessedMegapixels += pixels / 1_000_000.0; } - private string OutputPath(string inputPath, [CallerMemberName]string postfix = null) => + private string OutputPath(string inputPath, [CallerMemberName] string postfix = null) => Path.Combine( this.outputDirectory, Path.GetFileNameWithoutExtension(inputPath) + "-" + postfix + Path.GetExtension(inputPath)); @@ -210,18 +211,15 @@ public void ImageSharpResize(string input) using FileStream outputStream = File.Open(this.OutputPath(input), FileMode.Create); // Resize it to fit a 150x150 square - var targetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize); + DecoderOptions options = new() + { + TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) + }; + var decoder = new JpegDecoder(); - using ImageSharpImage image = decoder.DecodeInto(Configuration.Default, inputStream, targetSize, CancellationToken.None); + using ImageSharpImage image = decoder.Decode(options, inputStream); this.LogImageProcessed(image.Width, image.Height); - image.Mutate(i => i.Resize(new ResizeOptions - { - Size = targetSize, - Mode = ResizeMode.Max, - Sampler = KnownResamplers.Box - })); - // Reduce the size of the file image.Metadata.ExifProfile = null; image.Metadata.XmpProfile = null; @@ -237,18 +235,19 @@ public async Task ImageSharpResizeAsync(string input) using FileStream output = File.Open(this.OutputPath(input), FileMode.Create); // Resize it to fit a 150x150 square. - using ImageSharpImage image = await ImageSharpImage.LoadAsync(input); - this.LogImageProcessed(image.Width, image.Height); - - // Resize checks whether image size and target sizes are equal - image.Mutate(i => i.Resize(new ResizeOptions + DecoderOptions options = new() { - Size = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize), - Mode = ResizeMode.Max - })); + TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize) + }; + + using ImageSharpImage image = await ImageSharpImage.LoadAsync(options, input); + this.LogImageProcessed(image.Width, image.Height); // Reduce the size of the file image.Metadata.ExifProfile = null; + image.Metadata.XmpProfile = null; + image.Metadata.IccProfile = null; + image.Metadata.IptcProfile = null; // Save the results await image.SaveAsync(output, this.imageSharpJpegEncoder); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 239e209765..e303cb5164 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -4,7 +4,7 @@ using System; using System.IO; using Microsoft.DotNet.RemoteExecutor; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -75,11 +75,7 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) } string providerDump = BasicSerializer.Serialize(provider); - RemoteExecutor.Invoke( - RunTest, - providerDump, - "Disco") - .Dispose(); + RemoteExecutor.Invoke(RunTest, providerDump, "Disco").Dispose(); } [Theory] @@ -98,11 +94,9 @@ public void BmpDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatExce public void BmpDecoder_CanDecodeBitfields(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -111,11 +105,9 @@ public void BmpDecoder_CanDecodeBitfields(TestImageProvider prov public void BmpDecoder_CanDecode_Inverted(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -124,11 +116,9 @@ public void BmpDecoder_CanDecode_Inverted(TestImageProvider prov public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } [Theory] @@ -136,11 +126,9 @@ public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -148,11 +136,9 @@ public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider public void BmpDecoder_CanDecode_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -160,11 +146,9 @@ public void BmpDecoder_CanDecode_8Bit(TestImageProvider provider public void BmpDecoder_CanDecode_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -172,11 +156,9 @@ public void BmpDecoder_CanDecode_16Bit(TestImageProvider provide public void BmpDecoder_CanDecode_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -184,11 +166,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -199,11 +179,11 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma where TPixel : unmanaged, IPixel { RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette; - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = skippedPixelHandling })) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling }; + + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -212,11 +192,11 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider where TPixel : unmanaged, IPixel { RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette; - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = skippedPixelHandling })) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling }; + + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -227,13 +207,12 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRefDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black }; + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); + if (TestEnvironment.IsWindows) { - image.DebugSave(provider); - if (TestEnvironment.IsWindows) - { - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); - } + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -243,11 +222,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRe public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -263,11 +241,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider.LimitAllocatorBufferCapacity().InBytesSqrt(400); } - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -285,13 +262,12 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvide provider.LimitAllocatorBufferCapacity().InBytesSqrt(400); } - using (Image image = provider.GetImage(new BmpDecoder { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) - { - image.DebugSave(provider); + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black }; + using Image image = provider.GetImage(BmpDecoder, options); + image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); - } + // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. + // image.CompareToOriginal(provider); } [Theory] @@ -299,13 +275,11 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvide public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); - } + // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. + // image.CompareToOriginal(provider); } [Theory] @@ -313,11 +287,9 @@ public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -325,17 +297,15 @@ public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvide public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - - // Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel - // seems to be wrong. This bitmap has an alpha channel of two bits. In many cases this alpha channel has a value of 3, - // which should be remapped to 255 for RGBA32, but the magick decoder has a value of 191 set. - // The total difference without the alpha channel is still: 0.0204% - // Exporting the image as PNG with GIMP yields to the same result as the ImageSharp implementation. - image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), new MagickReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + + // Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel + // seems to be wrong. This bitmap has an alpha channel of two bits. In many cases this alpha channel has a value of 3, + // which should be remapped to 255 for RGBA32, but the magick decoder has a value of 191 set. + // The total difference without the alpha channel is still: 0.0204% + // Exporting the image as PNG with GIMP yields to the same result as the ImageSharp implementation. + image.CompareToOriginal(provider, ImageComparer.TolerantPercentage(6.1f), new MagickReferenceDecoder()); } [Theory] @@ -344,13 +314,11 @@ public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageP public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); - // Do not validate. Reference files will fail validation. - image.CompareToOriginal(provider, new MagickReferenceDecoder(false)); - } + // Do not validate. Reference files will fail validation. + image.CompareToOriginal(provider, new MagickReferenceDecoder(false)); } [Theory] @@ -358,11 +326,9 @@ public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider public void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -370,11 +336,9 @@ public void BmpDecoder_CanDecodeBmpv3(TestImageProvider provider public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -383,13 +347,11 @@ public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + if (TestEnvironment.IsWindows) { - image.DebugSave(provider); - if (TestEnvironment.IsWindows) - { - image.CompareToOriginal(provider); - } + image.CompareToOriginal(provider); } } @@ -397,39 +359,33 @@ public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel - { - Assert.Throws(() => + => Assert.Throws(() => { using (provider.GetImage(BmpDecoder)) { } }); - } [Theory] [WithFile(Rgb24jpeg, PixelTypes.Rgba32)] [WithFile(Rgb24png, PixelTypes.Rgba32)] public void BmpDecoder_ThrowsNotSupportedException_OnUnsupportedBitmaps(TestImageProvider provider) where TPixel : unmanaged, IPixel - { - Assert.Throws(() => + => Assert.Throws(() => { using (provider.GetImage(BmpDecoder)) { } }); - } [Theory] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -437,11 +393,9 @@ public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider pro public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -449,11 +403,9 @@ public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -462,11 +414,9 @@ public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -474,11 +424,9 @@ public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider public void BmpDecoder_RespectsFileHeaderOffset(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -486,11 +434,9 @@ public void BmpDecoder_RespectsFileHeaderOffset(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -498,11 +444,9 @@ public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } [Theory] @@ -521,12 +465,10 @@ public void BmpDecoder_CanDecode4BytePerEntryPalette(TestImageProvider image = decoder.Decode(Configuration.Default, stream, default)) - { - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new BmpDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -573,13 +509,11 @@ 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)) - { - image.DebugSave(provider); + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); - } + // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // image.CompareToOriginal(provider); } [Theory] @@ -587,15 +521,13 @@ public void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); - // TODO: System.Drawing can not decode this image. MagickReferenceDecoder can decode it, - // but i think incorrectly. I have loaded the image with GIMP and exported as PNG. - // The results are the same as the image sharp implementation. - // image.CompareToOriginal(provider, new MagickReferenceDecoder()); - } + // TODO: System.Drawing can not decode this image. MagickReferenceDecoder can decode it, + // but i think incorrectly. I have loaded the image with GIMP and exported as PNG. + // The results are the same as the image sharp implementation. + // image.CompareToOriginal(provider, new MagickReferenceDecoder()); } [Theory] @@ -611,13 +543,11 @@ public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider p public void BmpDecoder_CanDecode_Os2BitmapArray(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(BmpDecoder)) - { - image.DebugSave(provider); + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); - } + // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // image.CompareToOriginal(provider); } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index dd59fb2795..5dd712c1ff 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -53,22 +53,16 @@ public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolut var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, options); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageMetadata meta = output.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } - } + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, options); + + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageMetadata meta = output.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -78,21 +72,15 @@ public void Encode_PreserveBitsPerPixel(string imagePath, BmpBitsPerPixel bmpBit var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, options); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - BmpMetadata meta = output.Metadata.GetBmpMetadata(); - - Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); - } - } - } + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, options); + + memStream.Position = 0; + using var output = Image.Load(memStream); + BmpMetadata meta = output.Metadata.GetBmpMetadata(); + + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); } [Theory] @@ -237,28 +225,26 @@ public void Encode_8BitColor_WithWuQuantizer(TestImageProvider p return; } - using (Image image = provider.GetImage()) + using Image image = provider.GetImage(); + var encoder = new BmpEncoder { - var encoder = new BmpEncoder - { - BitsPerPixel = BmpBitsPerPixel.Pixel8, - Quantizer = new WuQuantizer() - }; - string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); - - // Use the default decoder to test our encoded image. This verifies the content. - // We do not verify the reference image though as some are invalid. - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) - { - referenceImage.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.01f), - provider, - extension: "bmp", - appendPixelTypeToFileName: false, - decoder: new MagickReferenceDecoder(false)); - } - } + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new WuQuantizer() + }; + + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + + // Use the default decoder to test our encoded image. This verifies the content. + // We do not verify the reference image though as some are invalid. + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using FileStream stream = File.OpenRead(actualOutputFile); + using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default); + referenceImage.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.01f), + provider, + extension: "bmp", + appendPixelTypeToFileName: false, + decoder: new MagickReferenceDecoder(false)); } [Theory] @@ -271,28 +257,25 @@ public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider image = provider.GetImage()) + using Image image = provider.GetImage(); + var encoder = new BmpEncoder { - var encoder = new BmpEncoder - { - BitsPerPixel = BmpBitsPerPixel.Pixel8, - Quantizer = new OctreeQuantizer() - }; - string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); - - // Use the default decoder to test our encoded image. This verifies the content. - // We do not verify the reference image though as some are invalid. - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) - { - referenceImage.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.01f), - provider, - extension: "bmp", - appendPixelTypeToFileName: false, - decoder: new MagickReferenceDecoder(false)); - } - } + BitsPerPixel = BmpBitsPerPixel.Pixel8, + Quantizer = new OctreeQuantizer() + }; + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); + + // Use the default decoder to test our encoded image. This verifies the content. + // We do not verify the reference image though as some are invalid. + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + using FileStream stream = File.OpenRead(actualOutputFile); + using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default); + referenceImage.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.01f), + provider, + extension: "bmp", + appendPixelTypeToFileName: false, + decoder: new MagickReferenceDecoder(false)); } [Theory] @@ -306,26 +289,20 @@ public void Encode_PreservesAlpha(TestImageProvider provider, Bm public void Encode_PreservesColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image input = provider.GetImage(new BmpDecoder())) - { - ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; - byte[] expectedProfileBytes = expectedProfile.ToByteArray(); - - using (var memStream = new MemoryStream()) - { - input.Save(memStream, new BmpEncoder()); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; - byte[] actualProfileBytes = actualProfile.ToByteArray(); - - Assert.NotNull(actualProfile); - Assert.Equal(expectedProfileBytes, actualProfileBytes); - } - } - } + using Image input = provider.GetImage(new BmpDecoder(), new()); + ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; + byte[] expectedProfileBytes = expectedProfile.ToByteArray(); + + using var memStream = new MemoryStream(); + input.Save(memStream, new BmpEncoder()); + + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; + byte[] actualProfileBytes = actualProfile.ToByteArray(); + + Assert.NotNull(actualProfile); + Assert.Equal(expectedProfileBytes, actualProfileBytes); } [Theory] @@ -346,24 +323,23 @@ private static void TestBmpEncoderCore( ImageComparer customComparer = null) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) + using Image image = provider.GetImage(); + + // There is no alpha in bmp with less then 32 bits per pixels, so the reference image will be made opaque. + if (bitsPerPixel != BmpBitsPerPixel.Pixel32) { - // There is no alpha in bmp with less then 32 bits per pixels, so the reference image will be made opaque. - if (bitsPerPixel != BmpBitsPerPixel.Pixel32) - { - image.Mutate(c => c.MakeOpaque()); - } - - var encoder = new BmpEncoder - { - BitsPerPixel = bitsPerPixel, - SupportTransparency = supportTransparency, - Quantizer = quantizer ?? KnownQuantizers.Octree - }; - - // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, customComparer); + image.Mutate(c => c.MakeOpaque()); } + + var encoder = new BmpEncoder + { + BitsPerPixel = bitsPerPixel, + SupportTransparency = supportTransparency, + Quantizer = quantizer ?? KnownQuantizers.Octree + }; + + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, customComparer); } } } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index e936eef657..d19f4862be 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -45,12 +45,10 @@ public class GeneralFormatTests public void ResolutionShouldChange(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.Metadata.VerticalResolution = 150; - image.Metadata.HorizontalResolution = 150; - image.DebugSave(provider); - } + using Image image = provider.GetImage(); + image.Metadata.VerticalResolution = 150; + image.Metadata.HorizontalResolution = 150; + image.DebugSave(provider); } [Fact] @@ -60,11 +58,9 @@ public void ImageCanEncodeToString() foreach (TestFile file in Files) { - using (Image image = file.CreateRgba32Image()) - { - string filename = Path.Combine(path, $"{file.FileNameWithoutExtension}.txt"); - File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); - } + using Image image = file.CreateRgba32Image(); + string filename = Path.Combine(path, $"{file.FileNameWithoutExtension}.txt"); + File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); } } @@ -75,10 +71,8 @@ public void DecodeThenEncodeImageFromStreamShouldSucceed() foreach (TestFile file in Files) { - using (Image image = file.CreateRgba32Image()) - { - image.Save(Path.Combine(path, file.FileName)); - } + using Image image = file.CreateRgba32Image(); + image.Save(Path.Combine(path, file.FileName)); } } @@ -120,42 +114,40 @@ public void ImageCanConvertFormat() foreach (TestFile file in Files) { - using (Image image = file.CreateRgba32Image()) + using Image image = file.CreateRgba32Image(); + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp"))) + { + image.SaveAsBmp(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg"))) + { + image.SaveAsJpeg(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm"))) + { + image.SaveAsPbm(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.png"))) + { + image.SaveAsPng(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif"))) + { + image.SaveAsGif(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga"))) { - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.bmp"))) - { - image.SaveAsBmp(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.jpg"))) - { - image.SaveAsJpeg(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.pbm"))) - { - image.SaveAsPbm(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.png"))) - { - image.SaveAsPng(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.gif"))) - { - image.SaveAsGif(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tga"))) - { - image.SaveAsTga(output); - } - - using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff"))) - { - image.SaveAsTiff(output); - } + image.SaveAsTga(output); + } + + using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff"))) + { + image.SaveAsTiff(output); } } } @@ -176,10 +168,8 @@ public void ImageShouldPreservePixelByteOrderWhenSerialized() serialized = memoryStream.ToArray(); } - using (var image2 = Image.Load(serialized)) - { - image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}"); - } + using var image2 = Image.Load(serialized); + image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}"); } } @@ -213,39 +203,33 @@ public void ImageShouldPreservePixelByteOrderWhenSerialized() public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension) { - using (var image = Image.LoadPixelData(new Rgba32[width * height], width, height)) - { - using (var memoryStream = new MemoryStream()) - { - IImageFormat format = GetFormat(extension); - image.Save(memoryStream, format); - memoryStream.Position = 0; + using var image = Image.LoadPixelData(new Rgba32[width * height], width, height); + using var memoryStream = new MemoryStream(); + IImageFormat format = GetFormat(extension); + image.Save(memoryStream, format); + memoryStream.Position = 0; - IImageInfo imageInfo = Image.Identify(memoryStream); + IImageInfo imageInfo = Image.Identify(memoryStream); - Assert.Equal(imageInfo.Width, width); - Assert.Equal(imageInfo.Height, height); - memoryStream.Position = 0; + Assert.Equal(imageInfo.Width, width); + Assert.Equal(imageInfo.Height, height); + memoryStream.Position = 0; - imageInfo = Image.Identify(memoryStream, out IImageFormat detectedFormat); + imageInfo = Image.Identify(memoryStream, out IImageFormat detectedFormat); - Assert.Equal(format, detectedFormat); - } - } + Assert.Equal(format, detectedFormat); } [Fact] public void IdentifyReturnsNullWithInvalidStream() { - var invalid = new byte[10]; + byte[] invalid = new byte[10]; - using (var memoryStream = new MemoryStream(invalid)) - { - IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format); + using var memoryStream = new MemoryStream(invalid); + IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format); - Assert.Null(imageInfo); - Assert.Null(format); - } + Assert.Null(imageInfo); + Assert.Null(format); } private static IImageFormat GetFormat(string format) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 8fbac4a9e9..3a8ae6f111 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -4,10 +4,9 @@ using System; using System.IO; using Microsoft.DotNet.RemoteExecutor; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -35,11 +34,32 @@ public class GifDecoderTests public void Decode_VerifyAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void GifDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { - image.DebugSaveMultiFrame(provider); - image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); - } + TargetSize = new() { Width = 150, Height = 150 }, + MaxFrames = 1 + }; + + using Image image = provider.GetImage(GifDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.0001F), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); } [Fact] @@ -51,13 +71,9 @@ 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(Configuration.Default, stream, default)) - { - Assert.Equal((200, 200), (image.Width, image.Height)); - } - } + using var stream = new UnmanagedMemoryStream(data, length); + using Image image = GifDecoder.Decode(DecoderOptions.Default, stream); + Assert.Equal((200, 200), (image.Width, image.Height)); } } @@ -66,11 +82,9 @@ public unsafe void Decode_NonTerminatedFinalFrame() public void GifDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.DebugSave(provider); - image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); - } + using Image image = provider.GetImage(); + image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } [Theory] @@ -80,12 +94,10 @@ public void GifDecoder_IsNotBoundToSinglePixelType(TestImageProvider(TestImageProvider provider, int expectedFrameCount) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - Assert.Equal(expectedFrameCount, image.Frames.Count); - image.DebugSave(provider); - image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); - } + using Image image = provider.GetImage(); + Assert.Equal(expectedFrameCount, image.Frames.Count); + image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } [Theory] @@ -93,10 +105,9 @@ public void Decode_VerifyRootFrameAndFrameCount(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.First })) - { - Assert.Equal(1, image.Frames.Count); - } + DecoderOptions options = new() { MaxFrames = 1 }; + using Image image = provider.GetImage(new GifDecoder(), options); + Assert.Equal(1, image.Frames.Count); } [Theory] @@ -104,10 +115,8 @@ public void CanDecodeJustOneFrame(TestImageProvider provider) public void CanDecodeAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.All })) - { - Assert.True(image.Frames.Count > 1); - } + using Image image = provider.GetImage(new GifDecoder()); + Assert.True(image.Frames.Count > 1); } [Theory] @@ -118,10 +127,8 @@ public void CanDecodeAllFrames(TestImageProvider provider) public void DetectPixelSize(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); - } + using var stream = new MemoryStream(testFile.Bytes, false); + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } [Theory] @@ -146,11 +153,9 @@ 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)) - { - Assert.Equal(expectedWidth, image.Width); - Assert.Equal(expectedHeight, image.Height); - } + using Image image = provider.GetImage(GifDecoder); + Assert.Equal(expectedWidth, image.Width); + Assert.Equal(expectedHeight, image.Height); } [Fact] @@ -190,12 +195,10 @@ public void Issue1530_BadDescriptorDimensions(TestImageProvider public void Issue405_BadApplicationExtensionBlockLength(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.DebugSave(provider); + using Image image = provider.GetImage(); + image.DebugSave(provider); - image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); - } + image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } // https://github.com/SixLabors/ImageSharp/issues/1668 @@ -204,12 +207,10 @@ public void Issue405_BadApplicationExtensionBlockLength(TestImageProvide public void Issue1668_InvalidColorIndex(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.DebugSave(provider); + using Image image = provider.GetImage(); + image.DebugSave(provider); - image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); - } + image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 6b6f4fbc21..d2c667a6b8 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -58,51 +60,39 @@ public void CloneIsDeep() [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { - var options = new GifDecoder - { - IgnoreMetadata = false - }; - var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateRgba32Image(options)) - { - GifMetadata metadata = image.Metadata.GetGifMetadata(); - Assert.Equal(1, metadata.Comments.Count); - Assert.Equal("ImageSharp", metadata.Comments[0]); - } + using Image image = testFile.CreateRgba32Image(new GifDecoder()); + GifMetadata metadata = image.Metadata.GetGifMetadata(); + Assert.Equal(1, metadata.Comments.Count); + Assert.Equal("ImageSharp", metadata.Comments[0]); } [Fact] public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() { - var options = new GifDecoder + DecoderOptions options = new() { - IgnoreMetadata = true + SkipMetadata = true }; var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateRgba32Image(options)) - { - GifMetadata metadata = image.Metadata.GetGifMetadata(); - Assert.Equal(0, metadata.Comments.Count); - } + using Image image = testFile.CreateRgba32Image(new GifDecoder(), options); + GifMetadata metadata = image.Metadata.GetGifMetadata(); + Assert.Equal(0, metadata.Comments.Count); } [Fact] public void Decode_CanDecodeLargeTextComment() { - var options = new GifDecoder(); var testFile = TestFile.Create(TestImages.Gif.LargeComment); - using (Image image = testFile.CreateRgba32Image(options)) - { - GifMetadata metadata = image.Metadata.GetGifMetadata(); - Assert.Equal(2, metadata.Comments.Count); - Assert.Equal(new string('c', 349), metadata.Comments[0]); - Assert.Equal("ImageSharp", metadata.Comments[1]); - } + using Image image = testFile.CreateRgba32Image(new GifDecoder()); + GifMetadata metadata = image.Metadata.GetGifMetadata(); + Assert.Equal(2, metadata.Comments.Count); + Assert.Equal(new string('c', 349), metadata.Comments[0]); + Assert.Equal("ImageSharp", metadata.Comments[1]); } [Fact] @@ -111,20 +101,16 @@ public void Encode_PreservesTextData() var decoder = new GifDecoder(); var testFile = TestFile.Create(TestImages.Gif.LargeComment); - using (Image input = testFile.CreateRgba32Image(decoder)) - using (var memoryStream = new MemoryStream()) - { - input.Save(memoryStream, new GifEncoder()); - memoryStream.Position = 0; - - using (Image image = decoder.Decode(Configuration.Default, memoryStream, default)) - { - GifMetadata metadata = image.Metadata.GetGifMetadata(); - Assert.Equal(2, metadata.Comments.Count); - Assert.Equal(new string('c', 349), metadata.Comments[0]); - Assert.Equal("ImageSharp", metadata.Comments[1]); - } - } + using Image input = testFile.CreateRgba32Image(decoder); + using var memoryStream = new MemoryStream(); + input.Save(memoryStream, new GifEncoder()); + memoryStream.Position = 0; + + using Image image = decoder.Decode(DecoderOptions.Default, memoryStream); + GifMetadata metadata = image.Metadata.GetGifMetadata(); + Assert.Equal(2, metadata.Comments.Count); + Assert.Equal(new string('c', 349), metadata.Comments[0]); + Assert.Equal("ImageSharp", metadata.Comments[1]); } [Theory] @@ -132,15 +118,27 @@ public void Encode_PreservesTextData() public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new GifDecoder(); - IImageInfo image = decoder.Identify(Configuration.Default, stream, default); - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new GifDecoder(); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); + } + + [Theory] + [MemberData(nameof(RatioFiles))] + public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) + { + 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); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -148,17 +146,27 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new GifDecoder(); - using (Image image = decoder.Decode(Configuration.Default, stream, default)) - { - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new GifDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); + } + + [Theory] + [MemberData(nameof(RatioFiles))] + public async Task Decode_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) + { + 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); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -166,13 +174,11 @@ public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio 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(Configuration.Default, stream, default); - GifMetadata meta = image.Metadata.GetGifMetadata(); - Assert.Equal(repeatCount, meta.RepeatCount); - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new GifDecoder(); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + GifMetadata meta = image.Metadata.GetGifMetadata(); + Assert.Equal(repeatCount, meta.RepeatCount); } [Theory] @@ -180,15 +186,11 @@ public void Identify_VerifyRepeatCount(string imagePath, uint repeatCount) 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(Configuration.Default, stream, default)) - { - GifMetadata meta = image.Metadata.GetGifMetadata(); - Assert.Equal(repeatCount, meta.RepeatCount); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new GifDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream); + GifMetadata meta = image.Metadata.GetGifMetadata(); + Assert.Equal(repeatCount, meta.RepeatCount); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index d9cacfacb1..f6d9ba4a3e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Metadata; @@ -80,17 +81,13 @@ public void MetadataIsParsedCorrectly( public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new JpegDecoder(); - using (Image image = decoder.Decode(Configuration.Default, stream, default)) - { - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new JpegDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -98,15 +95,27 @@ public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new JpegDecoder(); - IImageInfo image = decoder.Identify(Configuration.Default, stream, default); - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new JpegDecoder(); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); + } + + [Theory] + [MemberData(nameof(RatioFiles))] + public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) + { + 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); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -114,13 +123,11 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut 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(Configuration.Default, stream, default); - JpegMetadata meta = image.Metadata.GetJpegMetadata(); - Assert.Equal(quality, meta.Quality); - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new JpegDecoder(); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); } [Theory] @@ -128,14 +135,21 @@ public void Identify_VerifyQuality(string imagePath, int quality) 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(Configuration.Default, stream, default)) - { - JpegMetadata meta = image.Metadata.GetJpegMetadata(); - Assert.Equal(quality, meta.Quality); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); + } + + [Theory] + [MemberData(nameof(QualityFiles))] + 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); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); } [Theory] @@ -149,12 +163,10 @@ public void Decode_VerifyQuality(string imagePath, int quality) public void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor expectedColorType) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo image = JpegDecoder.Identify(Configuration.Default, stream, default); - JpegMetadata meta = image.Metadata.GetJpegMetadata(); - Assert.Equal(expectedColorType, meta.ColorType); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(expectedColorType, meta.ColorType); } [Theory] @@ -166,28 +178,24 @@ 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)) - { - JpegMetadata meta = image.Metadata.GetJpegMetadata(); - Assert.Equal(expectedColorType, meta.ColorType); - } + using Image image = provider.GetImage(JpegDecoder); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(expectedColorType, meta.ColorType); } private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action test) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) + using var stream = new MemoryStream(testFile.Bytes, false); + if (useIdentify) { - if (useIdentify) - { - IImageInfo imageInfo = ((IImageInfoDetector)decoder).Identify(Configuration.Default, stream, default); - test(imageInfo); - } - else - { - using var img = decoder.Decode(Configuration.Default, stream, default); - test(img); - } + IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream, default); + test(imageInfo); + } + else + { + using Image img = decoder.Decode(DecoderOptions.Default, stream, default); + test(img); } } @@ -247,23 +255,21 @@ private static void TestMetadataImpl( [InlineData(true)] public void IgnoreMetadata_ControlsWhetherMetadataIsParsed(bool ignoreMetadata) { - var decoder = new JpegDecoder { IgnoreMetadata = ignoreMetadata }; + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; // Snake.jpg has both Exif and ICC profiles defined: var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); - using (Image image = testFile.CreateRgba32Image(decoder)) + using Image image = testFile.CreateRgba32Image(JpegDecoder, options); + if (ignoreMetadata) { - if (ignoreMetadata) - { - Assert.Null(image.Metadata.ExifProfile); - Assert.Null(image.Metadata.IccProfile); - } - else - { - Assert.NotNull(image.Metadata.ExifProfile); - Assert.NotNull(image.Metadata.IccProfile); - } + Assert.Null(image.Metadata.ExifProfile); + Assert.Null(image.Metadata.IccProfile); + } + else + { + Assert.NotNull(image.Metadata.ExifProfile); + Assert.NotNull(image.Metadata.IccProfile); } } @@ -313,7 +319,7 @@ public void Clone_WithNullRationalArrayTag_DoesNotThrowException(TestIma Exception ex = Record.Exception(() => { using Image image = provider.GetImage(JpegDecoder); - var clone = image.Metadata.ExifProfile.DeepClone(); + ExifProfile clone = image.Metadata.ExifProfile.DeepClone(); }); Assert.Null(ex); } @@ -356,11 +362,9 @@ public void EncodedStringTags_WriteAndRead() [Fact] public void EncodedStringTags_Read() { - using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings))) - { - ExifProfile exif = image.Metadata.ExifProfile; - VerifyEncodedStrings(exif); - } + using var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora_EncodedStrings)); + ExifProfile exif = image.Metadata.ExifProfile; + VerifyEncodedStrings(exif); } private static void VerifyEncodedStrings(ExifProfile exif) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 87179a0be0..684716dcfb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -6,10 +6,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -73,10 +75,11 @@ private static bool SkipTest(ITestImageProvider provider) [Fact] public void ParseStream_BasicPropertiesAreCorrect() { + JpegDecoderOptions options = new(); byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using var ms = new MemoryStream(bytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); - using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); + using var decoder = new JpegDecoderCore(options); using Image image = decoder.Decode(bufferedStream, cancellationToken: default); // I don't know why these numbers are different. All I know is that the decoder works @@ -118,6 +121,118 @@ public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + using Image image = provider.GetImage(JpegDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Tolerant(BaselineTolerance), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24)] + public void JpegDecoder_Decode_Resize_Bicubic(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + TargetSize = new() { Width = 150, Height = 150 }, + Sampler = KnownResamplers.Bicubic + }; + using Image image = provider.GetImage(JpegDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Tolerant(BaselineTolerance), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24)] + public void JpegDecoder_Decode_Specialized_IDCT_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + JpegDecoderOptions specializedOptions = new() + { + GeneralOptions = options, + ResizeMode = JpegDecoderResizeMode.IdctOnly + }; + + using Image image = provider.GetImage(JpegDecoder, specializedOptions); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Tolerant(BaselineTolerance), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24)] + public void JpegDecoder_Decode_Specialized_Scale_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + JpegDecoderOptions specializedOptions = new() + { + GeneralOptions = options, + ResizeMode = JpegDecoderResizeMode.ScaleOnly + }; + + using Image image = provider.GetImage(JpegDecoder, specializedOptions); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Tolerant(BaselineTolerance), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24)] + public void JpegDecoder_Decode_Specialized_Combined_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + JpegDecoderOptions specializedOptions = new() + { + GeneralOptions = options, + ResizeMode = JpegDecoderResizeMode.Combined + }; + + using Image image = provider.GetImage(JpegDecoder, specializedOptions); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Tolerant(BaselineTolerance), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Progressive.Festzug, PixelTypes.Rgba32)] @@ -148,17 +263,22 @@ 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(s => + pausedStream.OnWaiting(_ => { cts.Cancel(); pausedStream.Release(); }); - var config = Configuration.CreateDefaultInstance(); - config.FileSystem = new SingleStreamFileSystem(pausedStream); + var configuration = Configuration.CreateDefaultInstance(); + configuration.FileSystem = new SingleStreamFileSystem(pausedStream); + DecoderOptions options = new() + { + Configuration = configuration + }; + await Assert.ThrowsAsync(async () => { - using Image image = await Image.LoadAsync(config, "someFakeFile", cts.Token); + using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token); }); } @@ -169,28 +289,30 @@ public async Task Identify_IsCancellable() string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small); using var pausedStream = new PausedStream(file); - pausedStream.OnWaiting(s => + pausedStream.OnWaiting(_ => { cts.Cancel(); pausedStream.Release(); }); - var config = Configuration.CreateDefaultInstance(); - config.FileSystem = new SingleStreamFileSystem(pausedStream); + var configuration = Configuration.CreateDefaultInstance(); + configuration.FileSystem = new SingleStreamFileSystem(pausedStream); + DecoderOptions options = new() + { + Configuration = configuration + }; - await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token)); + 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(() => + => Assert.Throws(() => { using Image image = provider.GetImage(JpegDecoder); }); - } // https://github.com/SixLabors/ImageSharp/pull/1732 [Theory] @@ -198,11 +320,9 @@ public void ThrowsNotSupported_WithUnsupportedJpegs(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(JpegDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(JpegDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } // https://github.com/SixLabors/ImageSharp/issues/2057 @@ -211,11 +331,9 @@ public void Issue1732_DecodesWithRgbColorSpace(TestImageProvider public void Issue2057_DecodeWorks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(JpegDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(JpegDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } // https://github.com/SixLabors/ImageSharp/issues/2133 @@ -224,11 +342,9 @@ public void Issue2057_DecodeWorks(TestImageProvider provider) public void Issue2133_DeduceColorSpace(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(JpegDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } + using Image image = provider.GetImage(JpegDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } // https://github.com/SixLabors/ImageSharp/issues/2133 @@ -237,44 +353,9 @@ public void Issue2133_DeduceColorSpace(TestImageProvider provide public void Issue2136_DecodeWorks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(JpegDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider); - } - } - - // DEBUG ONLY! - // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" - // into "\tests\Images\ActualOutput\JpegDecoderTests\" - // [Theory] - // [WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32, "PdfJsOriginal_progress.png")] - public void ValidateProgressivePdfJsOutput( - TestImageProvider provider, - string pdfJsOriginalResultImage) - where TPixel : unmanaged, IPixel - { - // tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm - string pdfJsOriginalResultPath = Path.Combine( - provider.Utility.GetTestOutputDir(), - pdfJsOriginalResultImage); - - byte[] sourceBytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; - - provider.Utility.TestName = nameof(DecodeProgressiveJpegOutputName); - - var comparer = ImageComparer.Tolerant(0, 0); - - using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) - using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) - using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) - { - ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); - ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); - - this.Output.WriteLine($"Difference for PDF.js ORIGINAL: {originalReport.DifferencePercentageString}"); - this.Output.WriteLine($"Difference for PORT: {portReport.DifferencePercentageString}"); - } + using Image image = provider.GetImage(JpegDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 7ab7accdfa..db458cb7c5 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -49,8 +49,9 @@ public void Decoder_ParseStream_SaveSpectralResult(TestImageProvider(TestImageProvider provider // Calculating data from ImageSharp byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + JpegDecoderOptions options = new(); - using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); + using var decoder = new JpegDecoderCore(options); using var ms = new MemoryStream(sourceBytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs index af870ed442..f0e2d3ce11 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs @@ -18,12 +18,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public class SpectralToPixelConversionTests { public static readonly string[] BaselineTestJpegs = - { - TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, - TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.MultiScanBaselineCMYK - }; + { + TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK + }; public SpectralToPixelConversionTests(ITestOutputHelper output) => this.Output = output; @@ -40,8 +40,9 @@ public void Decoder_PixelBufferComparison(TestImageProvider prov using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); // Decoding + JpegDecoderOptions options = new(); using var converter = new SpectralConverter(Configuration.Default); - using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); + using var decoder = new JpegDecoderCore(options); var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default); decoder.ParseStream(bufferedStream, converter, cancellationToken: default); @@ -50,17 +51,15 @@ public void Decoder_PixelBufferComparison(TestImageProvider prov provider.Utility.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; // Comparison - using (var image = new Image(Configuration.Default, converter.GetPixelBuffer(CancellationToken.None), new ImageMetadata())) - using (Image referenceImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) - { - ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); + using var image = new Image(Configuration.Default, converter.GetPixelBuffer(CancellationToken.None), new ImageMetadata()); + using Image referenceImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false); + ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); - this.Output.WriteLine($"*** {provider.SourceFileOrDescription} ***"); - this.Output.WriteLine($"Difference: {report.DifferencePercentageString}"); + this.Output.WriteLine($"*** {provider.SourceFileOrDescription} ***"); + this.Output.WriteLine($"Difference: {report.DifferencePercentageString}"); - // ReSharper disable once PossibleInvalidOperationException - Assert.True(report.TotalNormalizedDifference.Value < 0.005f); - } + // ReSharper disable once PossibleInvalidOperationException + Assert.True(report.TotalNormalizedDifference.Value < 0.005f); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index ef74549d0c..ffb54fb0a9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -25,7 +25,7 @@ public JpegFixture(ITestOutputHelper output) // ReSharper disable once InconsistentNaming public static float[] Create8x8FloatData() { - var result = new float[64]; + float[] result = new float[64]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) @@ -40,7 +40,7 @@ public static float[] Create8x8FloatData() // ReSharper disable once InconsistentNaming public static int[] Create8x8IntData() { - var result = new int[64]; + int[] result = new int[64]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) @@ -55,7 +55,7 @@ public static int[] Create8x8IntData() // ReSharper disable once InconsistentNaming public static short[] Create8x8ShortData() { - var result = new short[64]; + short[] result = new short[64]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) @@ -77,7 +77,7 @@ public static short[] Create8x8ShortData() public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) { var rnd = new Random(seed); - var result = new int[64]; + int[] result = new int[64]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) @@ -222,7 +222,8 @@ internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDa using var ms = new MemoryStream(bytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); - var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); + JpegDecoderOptions options = new(); + var decoder = new JpegDecoderCore(options); if (metaDataOnly) { decoder.Identify(bufferedStream, cancellationToken: default); diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs index 570ede8cf1..0d37c6b815 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs @@ -1,9 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; using static SixLabors.ImageSharp.Tests.TestImages.Pbm; @@ -97,5 +100,27 @@ public void DecodeReferenceImage(TestImageProvider provider, str bool isGrayscale = extension is "pgm" or "pbm"; image.CompareToReferenceOutput(provider, grayscale: isGrayscale); } + + [Theory] + [WithFile(RgbPlain, PixelTypes.Rgb24)] + public void PbmDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + TargetSize = new() { Width = 150, Height = 150 } + }; + + using Image image = provider.GetImage(new PbmDecoder(), options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index f17d8584c6..9de9f32ec5 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -4,6 +4,7 @@ using System.Buffers.Binary; using System.IO; using System.Text; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; @@ -75,7 +76,7 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType var decoder = new PngDecoder(); ImageFormatException exception = - Assert.Throws(() => decoder.Decode(Configuration.Default, memStream, default)); + Assert.Throws(() => decoder.Decode(DecoderOptions.Default, memStream)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } @@ -83,18 +84,17 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType private static string GetChunkTypeName(uint value) { - var data = new byte[4]; + byte[] data = new byte[4]; BinaryPrimitives.WriteUInt32BigEndian(data, value); return Encoding.ASCII.GetString(data); } - private static void WriteHeaderChunk(MemoryStream memStream) - { + private static void WriteHeaderChunk(MemoryStream memStream) => + // Writes a 1x1 32bit png header chunk containing a single black pixel. memStream.Write(Raw1X1PngIhdrAndpHYs, 0, Raw1X1PngIhdrAndpHYs.Length); - } private static void WriteChunk(MemoryStream memStream, string chunkName) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 22454d2573..016517844f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -5,7 +5,7 @@ using System.IO; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -112,6 +112,28 @@ public void Decode(TestImageProvider provider) image.CompareToOriginal(provider, ImageComparer.Exact); } + [Theory] + [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] + public void PngDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + TargetSize = new() { Width = 150, Height = 150 } + }; + + using Image image = provider.GetImage(PngDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.0003F), // Magick decoder shows difference on Mac + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(TestImages.Png.AverageFilter3BytesPerPixel, PixelTypes.Rgba32)] [WithFile(TestImages.Png.AverageFilter4BytesPerPixel, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 509f1956bd..f0611e9c4d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -111,15 +111,13 @@ public static readonly TheoryData CompressionLevels [WithTestPatternImages(nameof(PngColorTypes), 7, 5, PixelTypes.Rgba32)] public void WorksWithDifferentSizes(TestImageProvider provider, PngColorType pngColorType) where TPixel : unmanaged, IPixel - { - TestPngEncoderCore( + => TestPngEncoderCore( provider, pngColorType, PngFilterMethod.Adaptive, PngBitDepth.Bit8, PngInterlaceMode.None, appendPngColorType: true); - } [Theory] [WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] @@ -199,7 +197,7 @@ public void WorksWithAllBitDepths(TestImageProvider provider, Pn return; } - foreach (var filterMethod in PngFilterMethods) + foreach (object[] filterMethod in PngFilterMethods) { foreach (PngInterlaceMode interlaceMode in InterlaceMode) { @@ -235,7 +233,7 @@ public void WorksWithAllBitDepths(TestImageProvider provider, Pn public void WorksWithAllBitDepthsAndExcludeAllFilter(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) where TPixel : unmanaged, IPixel { - foreach (var filterMethod in PngFilterMethods) + foreach (object[] filterMethod in PngFilterMethods) { foreach (PngInterlaceMode interlaceMode in InterlaceMode) { @@ -284,20 +282,18 @@ public void WorksWithAllBitDepthsAndExcludeAllFilter(TestImageProvider(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) where TPixel : unmanaged, IPixel { - using (Stream stream = new MemoryStream()) - { - PngEncoder.Encode(provider.GetImage(), stream); + using Stream stream = new MemoryStream(); + PngEncoder.Encode(provider.GetImage(), stream); - stream.Seek(0, SeekOrigin.Begin); + stream.Seek(0, SeekOrigin.Begin); - var decoder = new PngDecoder(); + var decoder = new PngDecoder(); - Image image = decoder.Decode(Configuration.Default, stream, default); + Image image = decoder.Decode(DecoderOptions.Default, stream); - PngMetadata metadata = image.Metadata.GetPngMetadata(); - Assert.Equal(pngColorType, metadata.ColorType); - Assert.Equal(pngBitDepth, metadata.BitDepth); - } + PngMetadata metadata = image.Metadata.GetPngMetadata(); + Assert.Equal(pngColorType, metadata.ColorType); + Assert.Equal(pngBitDepth, metadata.BitDepth); } [Theory] @@ -329,14 +325,13 @@ public void PaletteColorType_WuQuantizer(TestImageProvider provi public void WritesFileMarker(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - using (var ms = new MemoryStream()) - { - image.Save(ms, PngEncoder); + using Image image = provider.GetImage(); + using var ms = new MemoryStream(); + image.Save(ms, PngEncoder); - byte[] data = ms.ToArray().Take(8).ToArray(); - byte[] expected = - { + byte[] data = ms.ToArray().Take(8).ToArray(); + byte[] expected = + { 0x89, // Set the high bit. 0x50, // P 0x4E, // N @@ -347,8 +342,7 @@ public void WritesFileMarker(TestImageProvider provider) 0x0A // LF }; - Assert.Equal(expected, data); - } + Assert.Equal(expected, data); } [Theory] @@ -356,22 +350,16 @@ public void WritesFileMarker(TestImageProvider provider) public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, PngEncoder); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageMetadata meta = output.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } - } + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageMetadata meta = output.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -379,21 +367,15 @@ public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolut public void Encode_PreserveBits(string imagePath, PngBitDepth pngBitDepth) { var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, PngEncoder); + using Image input = testFile.CreateRgba32Image(); + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - PngMetadata meta = output.Metadata.GetPngMetadata(); + memStream.Position = 0; + using var output = Image.Load(memStream); + PngMetadata meta = output.Metadata.GetPngMetadata(); - Assert.Equal(pngBitDepth, meta.BitDepth); - } - } - } + Assert.Equal(pngBitDepth, meta.BitDepth); } [Theory] @@ -437,9 +419,9 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color memStream.Position = 0; using var actual = Image.Load(memStream); Rgba32 expectedColor = Color.Blue; - if (colorType == PngColorType.Grayscale || colorType == PngColorType.GrayscaleWithAlpha) + if (colorType is PngColorType.Grayscale or PngColorType.GrayscaleWithAlpha) { - var luminance = ColorNumerics.Get8BitBT709Luminance(expectedColor.R, expectedColor.G, expectedColor.B); + byte luminance = ColorNumerics.Get8BitBT709Luminance(expectedColor.R, expectedColor.G, expectedColor.B); expectedColor = new Rgba32(luminance, luminance, luminance); } @@ -467,51 +449,45 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) { var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) + using Image input = testFile.CreateRgba32Image(); + PngMetadata inMeta = input.Metadata.GetPngMetadata(); + Assert.True(inMeta.HasTransparency); + + using var memStream = new MemoryStream(); + input.Save(memStream, PngEncoder); + memStream.Position = 0; + using var output = Image.Load(memStream); + PngMetadata outMeta = output.Metadata.GetPngMetadata(); + Assert.True(outMeta.HasTransparency); + + switch (pngColorType) { - PngMetadata inMeta = input.Metadata.GetPngMetadata(); - Assert.True(inMeta.HasTransparency); + case PngColorType.Grayscale: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) + { + Assert.True(outMeta.TransparentL16.HasValue); + Assert.Equal(inMeta.TransparentL16, outMeta.TransparentL16); + } + else + { + Assert.True(outMeta.TransparentL8.HasValue); + Assert.Equal(inMeta.TransparentL8, outMeta.TransparentL8); + } - using (var memStream = new MemoryStream()) - { - input.Save(memStream, PngEncoder); - memStream.Position = 0; - using (var output = Image.Load(memStream)) + break; + case PngColorType.Rgb: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) { - PngMetadata outMeta = output.Metadata.GetPngMetadata(); - Assert.True(outMeta.HasTransparency); - - switch (pngColorType) - { - case PngColorType.Grayscale: - if (pngBitDepth.Equals(PngBitDepth.Bit16)) - { - Assert.True(outMeta.TransparentL16.HasValue); - Assert.Equal(inMeta.TransparentL16, outMeta.TransparentL16); - } - else - { - Assert.True(outMeta.TransparentL8.HasValue); - Assert.Equal(inMeta.TransparentL8, outMeta.TransparentL8); - } - - break; - case PngColorType.Rgb: - if (pngBitDepth.Equals(PngBitDepth.Bit16)) - { - Assert.True(outMeta.TransparentRgb48.HasValue); - Assert.Equal(inMeta.TransparentRgb48, outMeta.TransparentRgb48); - } - else - { - Assert.True(outMeta.TransparentRgb24.HasValue); - Assert.Equal(inMeta.TransparentRgb24, outMeta.TransparentRgb24); - } - - break; - } + Assert.True(outMeta.TransparentRgb48.HasValue); + Assert.Equal(inMeta.TransparentRgb48, outMeta.TransparentRgb48); } - } + else + { + Assert.True(outMeta.TransparentRgb24.HasValue); + Assert.Equal(inMeta.TransparentRgb24, outMeta.TransparentRgb24); + } + + break; } } @@ -591,41 +567,42 @@ private static void TestPngEncoderCore( PngChunkFilter optimizeMethod = PngChunkFilter.None) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) + using Image image = provider.GetImage(); + var encoder = new PngEncoder { - var encoder = new PngEncoder - { - ColorType = pngColorType, - FilterMethod = pngFilterMethod, - CompressionLevel = compressionLevel, - BitDepth = bitDepth, - Quantizer = new WuQuantizer(new QuantizerOptions { MaxColors = paletteSize }), - InterlaceMethod = interlaceMode, - ChunkFilter = optimizeMethod, - }; + ColorType = pngColorType, + FilterMethod = pngFilterMethod, + CompressionLevel = compressionLevel, + BitDepth = bitDepth, + Quantizer = new WuQuantizer(new QuantizerOptions { MaxColors = paletteSize }), + InterlaceMethod = interlaceMode, + ChunkFilter = optimizeMethod, + }; - string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; - string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; - string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; - string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; - string pngBitDepthInfo = appendPngBitDepth ? bitDepth.ToString() : string.Empty; - string pngInterlaceModeInfo = interlaceMode != PngInterlaceMode.None ? $"_{interlaceMode}" : string.Empty; + string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; + string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; + string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; + string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; + string pngBitDepthInfo = appendPngBitDepth ? bitDepth.ToString() : string.Empty; + string pngInterlaceModeInfo = interlaceMode != PngInterlaceMode.None ? $"_{interlaceMode}" : string.Empty; - string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}{pngBitDepthInfo}{pngInterlaceModeInfo}"; + string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}{pngBitDepthInfo}{pngInterlaceModeInfo}"; - string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + // Compare to the Magick reference decoder. + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - // We compare using both our decoder and the reference decoder as pixel transformation - // occurs within the encoder itself leaving the input image unaffected. - // This means we are benefiting from testing our decoder also. - using (var imageSharpImage = Image.Load(actualOutputFile, new PngDecoder())) - using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) - { - ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage); - } - } + // We compare using both our decoder and the reference decoder as pixel transformation + // 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); + + fileStream.Position = 0; + + using Image referenceImage = referenceDecoder.Decode(DecoderOptions.Default, fileStream, default); + ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage); } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 1c89e72ac0..74217c9821 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -56,11 +57,9 @@ public void CloneIsDeep() public void Decoder_CanReadTextData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new PngDecoder())) - { - PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - VerifyTextDataIsPresent(meta); - } + using Image image = provider.GetImage(new PngDecoder()); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + VerifyTextDataIsPresent(meta); } [Theory] @@ -69,18 +68,14 @@ public void Encoder_PreservesTextData(TestImageProvider provider where TPixel : unmanaged, IPixel { var decoder = new PngDecoder(); - using (Image input = provider.GetImage(decoder)) - using (var memoryStream = new MemoryStream()) - { - input.Save(memoryStream, new PngEncoder()); - - memoryStream.Position = 0; - using (Image image = decoder.Decode(Configuration.Default, memoryStream, default)) - { - PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - VerifyTextDataIsPresent(meta); - } - } + using Image input = provider.GetImage(decoder); + using var memoryStream = new MemoryStream(); + input.Save(memoryStream, new PngEncoder()); + + memoryStream.Position = 0; + using Image image = decoder.Decode(DecoderOptions.Default, memoryStream); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + VerifyTextDataIsPresent(meta); } [Theory] @@ -88,16 +83,14 @@ public void Encoder_PreservesTextData(TestImageProvider provider public void Decoder_IgnoresInvalidTextData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new PngDecoder())) - { - 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"); - Assert.DoesNotContain(meta.TextData, m => m.Value is "space"); - Assert.DoesNotContain(meta.TextData, m => m.Value is "empty"); - Assert.DoesNotContain(meta.TextData, m => m.Value is "invalid characters"); - Assert.DoesNotContain(meta.TextData, m => m.Value is "too large"); - } + using Image image = provider.GetImage(new PngDecoder()); + 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"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "space"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "empty"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "invalid characters"); + Assert.DoesNotContain(meta.TextData, m => m.Value is "too large"); } [Theory] @@ -106,30 +99,27 @@ public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works(T where TPixel : unmanaged, IPixel { var decoder = new PngDecoder(); - using (Image input = provider.GetImage(decoder)) - using (var memoryStream = new MemoryStream()) + using Image input = provider.GetImage(decoder); + using var memoryStream = new MemoryStream(); + + // This will be a zTXt chunk. + var expectedText = new PngTextData("large-text", new string('c', 100), string.Empty, string.Empty); + + // This will be a iTXt chunk. + var expectedTextNoneLatin = new PngTextData("large-text-non-latin", new string('Ф', 100), "language-tag", "translated-keyword"); + PngMetadata inputMetadata = input.Metadata.GetFormatMetadata(PngFormat.Instance); + inputMetadata.TextData.Add(expectedText); + inputMetadata.TextData.Add(expectedTextNoneLatin); + input.Save(memoryStream, new PngEncoder { - // This will be a zTXt chunk. - var expectedText = new PngTextData("large-text", new string('c', 100), string.Empty, string.Empty); - - // This will be a iTXt chunk. - var expectedTextNoneLatin = new PngTextData("large-text-non-latin", new string('Ф', 100), "language-tag", "translated-keyword"); - PngMetadata inputMetadata = input.Metadata.GetFormatMetadata(PngFormat.Instance); - inputMetadata.TextData.Add(expectedText); - inputMetadata.TextData.Add(expectedTextNoneLatin); - input.Save(memoryStream, new PngEncoder - { - TextCompressionThreshold = 50 - }); - - memoryStream.Position = 0; - using (Image image = decoder.Decode(Configuration.Default, memoryStream, default)) - { - PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.Contains(meta.TextData, m => m.Equals(expectedText)); - Assert.Contains(meta.TextData, m => m.Equals(expectedTextNoneLatin)); - } - } + TextCompressionThreshold = 50 + }); + + memoryStream.Position = 0; + using Image image = decoder.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)); } [Theory] @@ -137,17 +127,15 @@ public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works(T public void Decode_ReadsExifData(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var decoder = new PngDecoder + DecoderOptions options = new() { - IgnoreMetadata = false + SkipMetadata = false }; - using (Image image = provider.GetImage(decoder)) - { - Assert.NotNull(image.Metadata.ExifProfile); - ExifProfile exif = image.Metadata.ExifProfile; - VerifyExifDataIsPresent(exif); - } + using Image image = provider.GetImage(new PngDecoder(), options); + Assert.NotNull(image.Metadata.ExifProfile); + ExifProfile exif = image.Metadata.ExifProfile; + VerifyExifDataIsPresent(exif); } [Theory] @@ -155,53 +143,49 @@ public void Decode_ReadsExifData(TestImageProvider provider) public void Decode_IgnoresExifData_WhenIgnoreMetadataIsTrue(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var decoder = new PngDecoder + DecoderOptions options = new() { - IgnoreMetadata = true + SkipMetadata = true }; - using (Image image = provider.GetImage(decoder)) - { - Assert.Null(image.Metadata.ExifProfile); - } + PngDecoder decoder = new(); + + using Image image = provider.GetImage(decoder, options); + Assert.Null(image.Metadata.ExifProfile); } [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunkIsRead() { - var options = new PngDecoder + DecoderOptions options = new() { - IgnoreMetadata = false + SkipMetadata = false }; var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateRgba32Image(options)) - { - PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + using Image image = testFile.CreateRgba32Image(new PngDecoder(), options); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.Equal(1, meta.TextData.Count); - Assert.Equal("Software", meta.TextData[0].Keyword); - Assert.Equal("paint.net 4.0.6", meta.TextData[0].Value); - Assert.Equal(0.4545d, meta.Gamma, precision: 4); - } + Assert.Equal(1, meta.TextData.Count); + Assert.Equal("Software", meta.TextData[0].Keyword); + Assert.Equal("paint.net 4.0.6", meta.TextData[0].Value); + Assert.Equal(0.4545d, meta.Gamma, precision: 4); } [Fact] public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() { - var options = new PngDecoder + DecoderOptions options = new() { - IgnoreMetadata = true + SkipMetadata = true }; var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); - using (Image image = testFile.CreateRgba32Image(options)) - { - PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.Equal(0, meta.TextData.Count); - } + using Image image = testFile.CreateRgba32Image(new PngDecoder(), options); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + Assert.Equal(0, meta.TextData.Count); } [Theory] @@ -209,17 +193,13 @@ public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new PngDecoder(); - using (Image image = decoder.Decode(Configuration.Default, stream, default)) - { - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new PngDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -227,26 +207,20 @@ 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())) - { - ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; - byte[] expectedProfileBytes = expectedProfile.ToByteArray(); - - using (var memStream = new MemoryStream()) - { - input.Save(memStream, new PngEncoder()); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; - byte[] actualProfileBytes = actualProfile.ToByteArray(); - - Assert.NotNull(actualProfile); - Assert.Equal(expectedProfileBytes, actualProfileBytes); - } - } - } + using Image input = provider.GetImage(new PngDecoder()); + ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; + byte[] expectedProfileBytes = expectedProfile.ToByteArray(); + + using var memStream = new MemoryStream(); + input.Save(memStream, new PngEncoder()); + + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; + byte[] actualProfileBytes = actualProfile.ToByteArray(); + + Assert.NotNull(actualProfile); + Assert.Equal(expectedProfileBytes, actualProfileBytes); } [Theory] @@ -254,15 +228,13 @@ public void Encode_PreservesColorProfile(TestImageProvider provi public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - var decoder = new PngDecoder(); - IImageInfo image = decoder.Identify(Configuration.Default, stream, default); - ImageMetadata meta = image.Metadata; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); - } + using var stream = new MemoryStream(testFile.Bytes, false); + var decoder = new PngDecoder(); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -270,13 +242,11 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut public void Identify_ReadsTextData(string imagePath) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo imageInfo = Image.Identify(stream); - Assert.NotNull(imageInfo); - PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance); - VerifyTextDataIsPresent(meta); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance); + VerifyTextDataIsPresent(meta); } [Theory] @@ -284,14 +254,12 @@ public void Identify_ReadsTextData(string imagePath) public void Identify_ReadsExifData(string imagePath) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo imageInfo = Image.Identify(stream); - Assert.NotNull(imageInfo); - Assert.NotNull(imageInfo.Metadata.ExifProfile); - ExifProfile exif = imageInfo.Metadata.ExifProfile; - VerifyExifDataIsPresent(exif); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.NotNull(imageInfo.Metadata.ExifProfile); + ExifProfile exif = imageInfo.Metadata.ExifProfile; + VerifyExifDataIsPresent(exif); } private static void VerifyExifDataIsPresent(ExifProfile exif) @@ -323,28 +291,26 @@ private static void VerifyTextDataIsPresent(PngMetadata meta) public void Identify_ReadsLegacyExifData(string imagePath) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo imageInfo = Image.Identify(stream); - Assert.NotNull(imageInfo); - Assert.NotNull(imageInfo.Metadata.ExifProfile); - - PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance); - Assert.DoesNotContain(meta.TextData, t => t.Keyword.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase)); - - ExifProfile exif = imageInfo.Metadata.ExifProfile; - Assert.Equal(0, exif.InvalidTags.Count); - Assert.Equal(3, exif.Values.Count); - - Assert.Equal( - "A colorful tiling of blue, red, yellow, and green 4x4 pixel blocks.", - exif.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal( - "Duplicated from basn3p02.png, then image metadata modified with exiv2", - exif.GetValue(ExifTag.ImageHistory).Value); - - Assert.Equal(42, (int)exif.GetValue(ExifTag.ImageNumber).Value); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.NotNull(imageInfo.Metadata.ExifProfile); + + PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance); + Assert.DoesNotContain(meta.TextData, t => t.Keyword.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase)); + + ExifProfile exif = imageInfo.Metadata.ExifProfile; + Assert.Equal(0, exif.InvalidTags.Count); + Assert.Equal(3, exif.Values.Count); + + Assert.Equal( + "A colorful tiling of blue, red, yellow, and green 4x4 pixel blocks.", + exif.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal( + "Duplicated from basn3p02.png, then image metadata modified with exiv2", + exif.GetValue(ExifTag.ImageHistory).Value); + + Assert.Equal(42, (int)exif.GetValue(ExifTag.ImageNumber).Value); } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 60d4be4228..58843227d0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -19,84 +20,17 @@ public void GeneralTest(TestImageProvider provider) where TPixel : unmanaged, IPixel { // does saving a file then reopening mean both files are identical??? - using (Image image = provider.GetImage()) - using (var ms = new MemoryStream()) - { - // image.Save(provider.Utility.GetTestOutputFileName("bmp")); - image.Save(ms, new PngEncoder()); - ms.Position = 0; - using (var img2 = Image.Load(ms, new PngDecoder())) - { - ImageComparer.Tolerant().VerifySimilarity(image, img2); + using Image image = provider.GetImage(); + using var ms = new MemoryStream(); - // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - } - } - } - - /* JJS: Disabled for now as the decoder now correctly decodes the full pixel components if the - paletted image has alpha of 0 - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void CanSaveIndexedPng(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - // does saving a file then reopening mean both files are identical??? - using (Image image = provider.GetImage()) - using (MemoryStream ms = new MemoryStream()) - { - // image.Save(provider.Utility.GetTestOutputFileName("bmp")); - image.Save(ms, new PngEncoder() { PaletteSize = 256 }); - ms.Position = 0; - using (Image img2 = Image.Load(ms, new PngDecoder())) - { - ImageComparer.VerifySimilarity(image, img2, 0.03f); - } - } - }*/ + // image.Save(provider.Utility.GetTestOutputFileName("bmp")); + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms); + ImageComparer.Tolerant().VerifySimilarity(image, img2); - /* JJS: Commented out for now since the test does not take into lossy nature of indexing. - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Color)] - public void CanSaveIndexedPngTwice(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - // does saving a file then reopening mean both files are identical??? - using (Image source = provider.GetImage()) - using (MemoryStream ms = new MemoryStream()) - { - source.Metadata.Quality = 256; - source.Save(ms, new PngEncoder(), new PngEncoderOptions { - Threshold = 200 - }); - ms.Position = 0; - using (Image img1 = Image.Load(ms, new PngDecoder())) - { - using (MemoryStream ms2 = new MemoryStream()) - { - img1.Save(ms2, new PngEncoder(), new PngEncoderOptions - { - Threshold = 200 - }); - ms2.Position = 0; - using (Image img2 = Image.Load(ms2, new PngDecoder())) - { - using (PixelAccessor pixels1 = img1.Lock()) - using (PixelAccessor pixels2 = img2.Lock()) - { - for (int y = 0; y < img1.Height; y++) - { - for (int x = 0; x < img1.Width; x++) - { - Assert.Equal(pixels1[x, y], pixels2[x, y]); - } - } - } - } - } - } - } - }*/ + // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); + } [Theory] [WithTestPatternImages(300, 300, PixelTypes.Rgba32)] @@ -104,20 +38,17 @@ public void Resize(TestImageProvider provider) where TPixel : unmanaged, IPixel { // does saving a file then reopening mean both files are identical??? - using (Image image = provider.GetImage()) - using (var ms = new MemoryStream()) - { - // image.Save(provider.Utility.GetTestOutputFileName("png")); - image.Mutate(x => x.Resize(100, 100)); + using Image image = provider.GetImage(); + using var ms = new MemoryStream(); + + // image.Save(provider.Utility.GetTestOutputFileName("png")); + image.Mutate(x => x.Resize(100, 100)); - // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); - image.Save(ms, new PngEncoder()); - ms.Position = 0; - using (var img2 = Image.Load(ms, new PngDecoder())) - { - ImageComparer.Tolerant().VerifySimilarity(image, img2); - } - } + // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using Image img2 = new PngDecoder().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 57d8aeff51..5007aa3719 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -1,8 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; using Microsoft.DotNet.RemoteExecutor; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -747,6 +748,28 @@ public void TgaDecoder_CanDecode_LegacyFormat(TestImageProvider } } + [Theory] + [WithFile(Bit32RleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + TargetSize = new() { Width = 150, Height = 150 } + }; + + using Image image = provider.GetImage(TgaDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.0001F), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index eb1e42ad06..12e2926bd7 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -29,7 +29,7 @@ public void ImageLoad_WithNoValidTgaHeaderBytes_Throws_UnknownImageFormatExcepti Assert.Throws(() => { - using (Image.Load(Configuration.Default, stream, out IImageFormat _)) + using (Image.Load(DecoderOptions.Default, stream, out IImageFormat _)) { } }); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index ac775b7121..12f20cd300 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -23,16 +23,14 @@ public class DeflateTiffCompressionTests [InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence public void Compress_Decompress_Roundtrip_Works(byte[] data) { - using (BufferedReadStream stream = CreateCompressedStream(data)) - { - var buffer = new byte[data.Length]; + using BufferedReadStream stream = CreateCompressedStream(data); + byte[] buffer = new byte[data.Length]; - using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false); + using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false); - decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer); + decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); - Assert.Equal(data, buffer); - } + Assert.Equal(data, buffer); } private static BufferedReadStream CreateCompressedStream(byte[] data) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index b2bd316a41..197beade27 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -19,7 +19,7 @@ public class LzwTiffCompressionTests public void Compress_Works(byte[] inputData, byte[] expectedCompressedData) { - var compressedData = new byte[expectedCompressedData.Length]; + byte[] compressedData = new byte[expectedCompressedData.Length]; Stream streamData = CreateCompressedStream(inputData); streamData.Read(compressedData, 0, expectedCompressedData.Length); @@ -37,10 +37,10 @@ public void Compress_Works(byte[] inputData, byte[] expectedCompressedData) public void Compress_Decompress_Roundtrip_Works(byte[] data) { using BufferedReadStream stream = CreateCompressedStream(data); - var buffer = new byte[data.Length]; + byte[] buffer = new byte[data.Length]; using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false); - decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer); + decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); Assert.Equal(data, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index 33cac791e6..ca457d15e2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -22,7 +22,7 @@ public void Decompress_ReadsData(byte[] inputData, uint byteCount, byte[] expect byte[] buffer = new byte[expectedResult.Length]; using var decompressor = new NoneTiffCompression(default, default, default); - decompressor.Decompress(stream, 0, byteCount, 1, buffer); + decompressor.Decompress(stream, 0, byteCount, 1, buffer, default); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index 7f1452c418..4ed4330ec5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -31,7 +31,7 @@ public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult) byte[] buffer = new byte[expectedResult.Length]; using var decompressor = new PackBitsTiffCompression(MemoryAllocator.Create(), default, default); - decompressor.Decompress(stream, 0, (uint)inputData.Length, 1, buffer); + decompressor.Decompress(stream, 0, (uint)inputData.Length, 1, buffer, default); Assert.Equal(expectedResult, buffer); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 9ad0ed06f8..443e42700f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,6 +4,8 @@ // ReSharper disable InconsistentNaming using System; using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -649,10 +651,9 @@ public void TiffDecoder_CanDecode_PackBitsCompressed(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new TiffDecoder() { DecodingMode = FrameDecodingMode.First })) - { - Assert.Equal(1, image.Frames.Count); - } + DecoderOptions options = new() { MaxFrames = 1 }; + using Image image = provider.GetImage(new TiffDecoder(), options); + Assert.Equal(1, image.Frames.Count); } [Theory] @@ -710,5 +711,27 @@ public void DecodeMultiframe(TestImageProvider provider) image.DebugSaveMultiFrame(provider); image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); } + + [Theory] + [WithFile(Rgba3BitUnassociatedAlpha, PixelTypes.Rgba32)] + public void TiffDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + TargetSize = new() { Width = 150, Height = 150 } + }; + + using Image image = provider.GetImage(TiffDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 38fac249c9..c30a7b6c6f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Metadata; @@ -50,24 +51,22 @@ public void TiffMetadata_CloneIsDeep() public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) - { - TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); - var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); - VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); + using Image image = provider.GetImage(TiffDecoder); + TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone(); + VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData); - var clone = (TiffFrameMetadata)meta.DeepClone(); + var clone = (TiffFrameMetadata)meta.DeepClone(); - clone.BitsPerPixel = TiffBitsPerPixel.Bit8; - clone.Compression = TiffCompression.None; - clone.PhotometricInterpretation = TiffPhotometricInterpretation.CieLab; - clone.Predictor = TiffPredictor.Horizontal; + clone.BitsPerPixel = TiffBitsPerPixel.Bit8; + clone.Compression = TiffCompression.None; + clone.PhotometricInterpretation = TiffPhotometricInterpretation.CieLab; + clone.Predictor = TiffPredictor.Horizontal; - Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); - Assert.False(meta.Compression == clone.Compression); - Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); - Assert.False(meta.Predictor == clone.Predictor); - } + Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); + Assert.False(meta.Compression == clone.Compression); + Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); + Assert.False(meta.Predictor == clone.Predictor); } private static void VerifyExpectedTiffFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) @@ -119,23 +118,23 @@ public void Identify_DetectsCorrectByteOrder(string imagePath, ByteOrder expecte public void MetadataProfiles(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = ignoreMetadata })) + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; + using Image image = provider.GetImage(new TiffDecoder(), options); + TiffMetadata meta = image.Metadata.GetTiffMetadata(); + ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; + + Assert.NotNull(meta); + if (ignoreMetadata) + { + Assert.Null(rootFrameMetaData.XmpProfile); + Assert.Null(rootFrameMetaData.ExifProfile); + } + else { - TiffMetadata meta = image.Metadata.GetTiffMetadata(); - ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata; - Assert.NotNull(meta); - if (ignoreMetadata) - { - Assert.Null(rootFrameMetaData.XmpProfile); - Assert.Null(rootFrameMetaData.ExifProfile); - } - else - { - Assert.NotNull(rootFrameMetaData.XmpProfile); - Assert.NotNull(rootFrameMetaData.ExifProfile); - Assert.Equal(2599, rootFrameMetaData.XmpProfile.Data.Length); - Assert.Equal(26, rootFrameMetaData.ExifProfile.Values.Count); - } + Assert.NotNull(rootFrameMetaData.XmpProfile); + Assert.NotNull(rootFrameMetaData.ExifProfile); + Assert.Equal(2599, rootFrameMetaData.XmpProfile.Data.Length); + Assert.Equal(26, rootFrameMetaData.ExifProfile.Values.Count); } } @@ -158,63 +157,61 @@ public void CanDecodeImage_WithIptcDataAsLong(TestImageProvider public void BaselineTags(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) - { - ImageFrame rootFrame = image.Frames.RootFrame; - Assert.Equal(32, rootFrame.Width); - Assert.Equal(32, rootFrame.Height); - Assert.NotNull(rootFrame.Metadata.XmpProfile); - Assert.Equal(2599, rootFrame.Metadata.XmpProfile.Data.Length); - - ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; - TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); - Assert.NotNull(exifProfile); - - // The original exifProfile has 30 values, but 4 of those values will be stored in the TiffFrameMetaData - // and removed from the profile on decode. - Assert.Equal(26, exifProfile.Values.Count); - Assert.Equal(TiffBitsPerPixel.Bit4, tiffFrameMetadata.BitsPerPixel); - Assert.Equal(TiffCompression.Lzw, tiffFrameMetadata.Compression); - Assert.Equal("This is Название", exifProfile.GetValue(ExifTag.ImageDescription).Value); - Assert.Equal("This is Изготовитель камеры", exifProfile.GetValue(ExifTag.Make).Value); - Assert.Equal("This is Модель камеры", exifProfile.GetValue(ExifTag.Model).Value); - Assert.Equal("IrfanView", exifProfile.GetValue(ExifTag.Software).Value); - Assert.Null(exifProfile.GetValue(ExifTag.DateTime)?.Value); - Assert.Equal("This is author1;Author2", exifProfile.GetValue(ExifTag.Artist).Value); - Assert.Null(exifProfile.GetValue(ExifTag.HostComputer)?.Value); - Assert.Equal("This is Авторские права", exifProfile.GetValue(ExifTag.Copyright).Value); - Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value); - Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value); - var expectedResolution = new Rational(10000, 1000, simplify: false); - Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.XResolution).Value); - Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value); - Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); - Assert.Equal(new Number[] { 297u }, exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); - Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples)?.Value); - Assert.Equal(32u, exifProfile.GetValue(ExifTag.RowsPerStrip).Value); - Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat)); - Assert.Equal(TiffPredictor.None, tiffFrameMetadata.Predictor); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, UnitConverter.ExifProfileToResolutionUnit(exifProfile)); - ushort[] colorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; - Assert.NotNull(colorMap); - Assert.Equal(48, colorMap.Length); - Assert.Equal(10537, colorMap[0]); - Assert.Equal(14392, colorMap[1]); - Assert.Equal(58596, colorMap[46]); - Assert.Equal(3855, colorMap[47]); - Assert.Equal(TiffPhotometricInterpretation.PaletteColor, tiffFrameMetadata.PhotometricInterpretation); - Assert.Equal(1u, exifProfile.GetValue(ExifTag.SamplesPerPixel).Value); - - ImageMetadata imageMetaData = image.Metadata; - Assert.NotNull(imageMetaData); - Assert.Equal(PixelResolutionUnit.PixelsPerInch, imageMetaData.ResolutionUnits); - Assert.Equal(10, imageMetaData.HorizontalResolution); - Assert.Equal(10, imageMetaData.VerticalResolution); - - TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata(); - Assert.NotNull(tiffMetaData); - Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); - } + using Image image = provider.GetImage(TiffDecoder); + ImageFrame rootFrame = image.Frames.RootFrame; + Assert.Equal(32, rootFrame.Width); + Assert.Equal(32, rootFrame.Height); + Assert.NotNull(rootFrame.Metadata.XmpProfile); + Assert.Equal(2599, rootFrame.Metadata.XmpProfile.Data.Length); + + ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; + TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); + Assert.NotNull(exifProfile); + + // The original exifProfile has 30 values, but 4 of those values will be stored in the TiffFrameMetaData + // and removed from the profile on decode. + Assert.Equal(26, exifProfile.Values.Count); + Assert.Equal(TiffBitsPerPixel.Bit4, tiffFrameMetadata.BitsPerPixel); + Assert.Equal(TiffCompression.Lzw, tiffFrameMetadata.Compression); + Assert.Equal("This is Название", exifProfile.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal("This is Изготовитель камеры", exifProfile.GetValue(ExifTag.Make).Value); + Assert.Equal("This is Модель камеры", exifProfile.GetValue(ExifTag.Model).Value); + Assert.Equal("IrfanView", exifProfile.GetValue(ExifTag.Software).Value); + Assert.Null(exifProfile.GetValue(ExifTag.DateTime)?.Value); + Assert.Equal("This is author1;Author2", exifProfile.GetValue(ExifTag.Artist).Value); + Assert.Null(exifProfile.GetValue(ExifTag.HostComputer)?.Value); + Assert.Equal("This is Авторские права", exifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal(4, exifProfile.GetValue(ExifTag.Rating).Value); + Assert.Equal(75, exifProfile.GetValue(ExifTag.RatingPercent).Value); + var expectedResolution = new Rational(10000, 1000, simplify: false); + Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.XResolution).Value); + Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value); + Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); + Assert.Equal(new Number[] { 297u }, exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); + Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples)?.Value); + Assert.Equal(32u, exifProfile.GetValue(ExifTag.RowsPerStrip).Value); + Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat)); + Assert.Equal(TiffPredictor.None, tiffFrameMetadata.Predictor); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, UnitConverter.ExifProfileToResolutionUnit(exifProfile)); + ushort[] colorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; + Assert.NotNull(colorMap); + Assert.Equal(48, colorMap.Length); + Assert.Equal(10537, colorMap[0]); + Assert.Equal(14392, colorMap[1]); + Assert.Equal(58596, colorMap[46]); + Assert.Equal(3855, colorMap[47]); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, tiffFrameMetadata.PhotometricInterpretation); + Assert.Equal(1u, exifProfile.GetValue(ExifTag.SamplesPerPixel).Value); + + ImageMetadata imageMetaData = image.Metadata; + Assert.NotNull(imageMetaData); + Assert.Equal(PixelResolutionUnit.PixelsPerInch, imageMetaData.ResolutionUnits); + Assert.Equal(10, imageMetaData.HorizontalResolution); + Assert.Equal(10, imageMetaData.VerticalResolution); + + TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetaData); + Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder); } [Theory] @@ -222,23 +219,21 @@ public void BaselineTags(TestImageProvider provider) public void SubfileType(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(TiffDecoder)) - { - TiffMetadata meta = image.Metadata.GetTiffMetadata(); - Assert.NotNull(meta); + using Image image = provider.GetImage(TiffDecoder); + TiffMetadata meta = image.Metadata.GetTiffMetadata(); + Assert.NotNull(meta); - Assert.Equal(2, image.Frames.Count); + Assert.Equal(2, image.Frames.Count); - ExifProfile frame0Exif = image.Frames[0].Metadata.ExifProfile; - Assert.Equal(TiffNewSubfileType.FullImage, (TiffNewSubfileType)frame0Exif.GetValue(ExifTag.SubfileType).Value); - Assert.Equal(255, image.Frames[0].Width); - Assert.Equal(255, image.Frames[0].Height); + ExifProfile frame0Exif = image.Frames[0].Metadata.ExifProfile; + Assert.Equal(TiffNewSubfileType.FullImage, (TiffNewSubfileType)frame0Exif.GetValue(ExifTag.SubfileType).Value); + Assert.Equal(255, image.Frames[0].Width); + Assert.Equal(255, image.Frames[0].Height); - ExifProfile frame1Exif = image.Frames[1].Metadata.ExifProfile; - Assert.Equal(TiffNewSubfileType.Preview, (TiffNewSubfileType)frame1Exif.GetValue(ExifTag.SubfileType).Value); - Assert.Equal(255, image.Frames[1].Width); - Assert.Equal(255, image.Frames[1].Height); - } + ExifProfile frame1Exif = image.Frames[1].Metadata.ExifProfile; + Assert.Equal(TiffNewSubfileType.Preview, (TiffNewSubfileType)frame1Exif.GetValue(ExifTag.SubfileType).Value); + Assert.Equal(255, image.Frames[1].Width); + Assert.Equal(255, image.Frames[1].Height); } [Theory] @@ -247,7 +242,8 @@ public void Encode_PreservesMetadata(TestImageProvider provider) where TPixel : unmanaged, IPixel { // Load Tiff image - using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false }); + DecoderOptions options = new() { SkipMetadata = false }; + using Image image = provider.GetImage(new TiffDecoder(), options); ImageMetadata inputMetaData = image.Metadata; ImageFrame rootFrameInput = image.Frames.RootFrame; diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index f9b949e214..bbf35e51b5 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.PixelFormats; #if SUPPORTS_RUNTIME_INTRINSICS @@ -104,7 +103,6 @@ private static void RunColorSpaceTransformTestWithPeakImage() Assert.Equal(expectedData, transformData); } - // Test image: Input\Png\Bike.png private static void RunColorSpaceTransformTestWithBikeImage() { // arrange @@ -119,7 +117,7 @@ private static void RunColorSpaceTransformTestWithBikeImage() // Convert image pixels to bgra array. byte[] imgBytes = File.ReadAllBytes(TestImageFullPath(TestImages.Webp.Lossy.BikeSmall)); - using var image = Image.Load(imgBytes, new WebpDecoder()); + using var image = Image.Load(imgBytes); uint[] bgra = ToBgra(image); int colorTransformBits = 4; diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index fe5520cc6f..ac59189527 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Webp; -using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -44,14 +46,12 @@ public void Identify_DetectsCorrectDimensionsAndBitDepth( int expectedBitsPerPixel) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo imageInfo = Image.Identify(stream); - Assert.NotNull(imageInfo); - Assert.Equal(expectedWidth, imageInfo.Width); - Assert.Equal(expectedHeight, imageInfo.Height); - Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.Equal(expectedWidth, imageInfo.Width); + Assert.Equal(expectedHeight, imageInfo.Height); + Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel); } [Theory] @@ -67,11 +67,9 @@ public void Identify_DetectsCorrectDimensionsAndBitDepth( public void WebpDecoder_CanDecode_Lossy_WithoutFilter(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -83,11 +81,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -106,11 +102,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -121,11 +115,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -140,11 +132,9 @@ public void WebpDecoder_CanDecode_Lossy_VerySmall(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -154,11 +144,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -171,11 +159,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -196,11 +182,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -208,11 +192,9 @@ public void WebpDecoder_CanDecode_Lossy_WithAlpha(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -221,11 +203,9 @@ public void WebpDecoder_CanDecode_Lossless_WithAlpha(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -240,11 +220,9 @@ public void WebpDecoder_CanDecode_Lossless_WithSubtractGreenTransform( TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -256,11 +234,9 @@ public void WebpDecoder_CanDecode_Lossless_WithSubtractGreenTransform( public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -269,11 +245,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -282,11 +256,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -306,11 +278,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -325,11 +295,9 @@ 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)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -337,18 +305,16 @@ public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms(TestImage public void Decode_AnimatedLossless_VerifyAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); - WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); + using Image image = provider.GetImage(WebpDecoder); + WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); + WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); - image.DebugSaveMultiFrame(provider); - image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); - Assert.Equal(0, webpMetaData.AnimationLoopCount); - Assert.Equal(150U, frameMetaData.FrameDuration); - Assert.Equal(12, image.Frames.Count); - } + Assert.Equal(0, webpMetaData.AnimationLoopCount); + Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); } [Theory] @@ -356,18 +322,16 @@ public void Decode_AnimatedLossless_VerifyAllFrames(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); - WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); + using Image image = provider.GetImage(WebpDecoder); + WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); + WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata(); - image.DebugSaveMultiFrame(provider); - image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f)); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f)); - Assert.Equal(0, webpMetaData.AnimationLoopCount); - Assert.Equal(150U, frameMetaData.FrameDuration); - Assert.Equal(12, image.Frames.Count); - } + Assert.Equal(0, webpMetaData.AnimationLoopCount); + Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); } [Theory] @@ -375,10 +339,9 @@ public void Decode_AnimatedLossy_VerifyAllFrames(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First })) - { - Assert.Equal(1, image.Frames.Count); - } + DecoderOptions options = new() { MaxFrames = 1 }; + using Image image = provider.GetImage(new WebpDecoder(), options); + Assert.Equal(1, image.Frames.Count); } [Theory] @@ -389,10 +352,30 @@ 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); + image.DebugSave(provider); + } + + [Theory] + [WithFile(Lossless.BikeThreeTransforms, PixelTypes.Rgba32)] + public void WebpDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { - image.DebugSave(provider); - } + TargetSize = new() { Width = 150, Height = 150 } + }; + + using Image image = provider.GetImage(WebpDecoder, options); + + FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; + + image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.TolerantPercentage(0.0007F), + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); } // https://github.com/SixLabors/ImageSharp/issues/1594 @@ -401,11 +384,9 @@ public void WebpDecoder_CanDecode_Lossless_WithIssues(TestImageProvider< public void WebpDecoder_CanDecode_Issue1594(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Theory] @@ -424,41 +405,33 @@ public void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestIm private static void RunDecodeLossyWithHorizontalFilter() { var provider = TestImageProvider.File(TestImageLossyHorizontalFilterPath); - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } private static void RunDecodeLossyWithVerticalFilter() { var provider = TestImageProvider.File(TestImageLossyVerticalFilterPath); - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } private static void RunDecodeLossyWithSimpleFilterTest() { var provider = TestImageProvider.File(TestImageLossySimpleFilterPath); - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } private static void RunDecodeLossyWithComplexFilterTest() { var provider = TestImageProvider.File(TestImageLossyComplexFilterPath); - using (Image image = provider.GetImage(WebpDecoder)) - { - image.DebugSave(provider); - image.CompareToOriginal(provider, ReferenceDecoder); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); } [Fact] diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs index 05d0d6c5a8..1288538723 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp [Trait("Format", "Webp")] public class WebpMetaDataTests { - private static WebpDecoder WebpDecoder => new() { IgnoreMetadata = false }; + private static WebpDecoder WebpDecoder => new(); [Theory] [WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, false)] @@ -23,9 +24,8 @@ public class WebpMetaDataTests public void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLossyImage(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { - var decoder = new WebpDecoder { IgnoreMetadata = ignoreMetadata }; - - using Image image = provider.GetImage(decoder); + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; + using Image image = provider.GetImage(WebpDecoder, options); if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); @@ -45,9 +45,8 @@ public void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLossyImage(Te public void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLosslessImage(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { - var decoder = new WebpDecoder { IgnoreMetadata = ignoreMetadata }; - - using Image image = provider.GetImage(decoder); + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; + using Image image = provider.GetImage(WebpDecoder, options); if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); @@ -71,9 +70,8 @@ public void IgnoreMetadata_ControlsWhetherExifIsParsed_WithLosslessImage public void IgnoreMetadata_ControlsWhetherIccpIsParsed(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { - var decoder = new WebpDecoder { IgnoreMetadata = ignoreMetadata }; - - using Image image = provider.GetImage(decoder); + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; + using Image image = provider.GetImage(WebpDecoder, options); if (ignoreMetadata) { Assert.Null(image.Metadata.IccProfile); @@ -91,9 +89,8 @@ public void IgnoreMetadata_ControlsWhetherIccpIsParsed(TestImageProvider public async Task IgnoreMetadata_ControlsWhetherXmpIsParsed(TestImageProvider provider, bool ignoreMetadata) where TPixel : unmanaged, IPixel { - var decoder = new WebpDecoder { IgnoreMetadata = ignoreMetadata }; - - using Image image = await provider.GetImageAsync(decoder); + DecoderOptions options = new() { SkipMetadata = ignoreMetadata }; + using Image image = await provider.GetImageAsync(WebpDecoder, options); if (ignoreMetadata) { Assert.Null(image.Metadata.XmpProfile); @@ -178,29 +175,23 @@ public void EncodeLosslessWebp_PreservesExif(TestImageProvider p public void Encode_PreservesColorProfile(TestImageProvider provider, WebpFileFormatType fileFormat) where TPixel : unmanaged, IPixel { - using (Image input = provider.GetImage(new WebpDecoder())) + using Image input = provider.GetImage(WebpDecoder); + ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; + byte[] expectedProfileBytes = expectedProfile.ToByteArray(); + + using var memStream = new MemoryStream(); + input.Save(memStream, new WebpEncoder() { - ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; - byte[] expectedProfileBytes = expectedProfile.ToByteArray(); - - using (var memStream = new MemoryStream()) - { - input.Save(memStream, new WebpEncoder() - { - FileFormat = fileFormat - }); - - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; - byte[] actualProfileBytes = actualProfile.ToByteArray(); - - Assert.NotNull(actualProfile); - Assert.Equal(expectedProfileBytes, actualProfileBytes); - } - } - } + FileFormat = fileFormat + }); + + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; + byte[] actualProfileBytes = actualProfile.ToByteArray(); + + Assert.NotNull(actualProfile); + Assert.Equal(expectedProfileBytes, actualProfileBytes); } [Theory] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs index c9be2a74e1..d1848281f7 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs @@ -1,10 +1,10 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -15,101 +15,138 @@ public partial class ImageTests public class Decode_Cancellation : ImageLoadTestBase { private bool isTestStreamSeekable; - private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new SemaphoreSlim(0); - private readonly SemaphoreSlim continueSemaphore = new SemaphoreSlim(0); - private readonly CancellationTokenSource cts = new CancellationTokenSource(); + private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new(0); + private readonly SemaphoreSlim continueSemaphore = new(0); + private readonly CancellationTokenSource cts = new(); - public Decode_Cancellation() - { - this.TopLevelConfiguration.StreamProcessingBufferSize = 128; - } + public Decode_Cancellation() => this.TopLevelConfiguration.StreamProcessingBufferSize = 128; [Theory] [InlineData(false)] [InlineData(true)] - public async Task LoadAsync_Specific_Stream(bool isInputStreamSeekable) + public Task LoadAsync_Specific_Stream(bool isInputStreamSeekable) { this.isTestStreamSeekable = isInputStreamSeekable; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.LoadAsync(this.TopLevelConfiguration, this.DataStream, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.DataStream, this.cts.Token)); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task LoadAsync_Agnostic_Stream(bool isInputStreamSeekable) + public Task LoadAsync_Agnostic_Stream(bool isInputStreamSeekable) { this.isTestStreamSeekable = isInputStreamSeekable; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.LoadAsync(this.TopLevelConfiguration, this.DataStream, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.DataStream, this.cts.Token)); } [Fact] - public async Task LoadAsync_Agnostic_Path() + public Task LoadAsync_Agnostic_Path() { this.isTestStreamSeekable = true; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.LoadAsync(this.TopLevelConfiguration, this.MockFilePath, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token)); } [Fact] - public async Task LoadAsync_Specific_Path() + public Task LoadAsync_Specific_Path() { this.isTestStreamSeekable = true; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.LoadAsync(this.TopLevelConfiguration, this.MockFilePath, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token)); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task IdentifyAsync_Stream(bool isInputStreamSeekable) + public Task IdentifyAsync_Stream(bool isInputStreamSeekable) { this.isTestStreamSeekable = isInputStreamSeekable; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.IdentifyAsync(this.TopLevelConfiguration, this.DataStream, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.IdentifyAsync(options, this.DataStream, this.cts.Token)); } [Fact] - public async Task IdentifyAsync_CustomConfiguration_Path() + public Task IdentifyAsync_CustomConfiguration_Path() { this.isTestStreamSeekable = true; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.IdentifyAsync(this.TopLevelConfiguration, this.MockFilePath, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.IdentifyAsync(options, this.MockFilePath, this.cts.Token)); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task IdentifyWithFormatAsync_CustomConfiguration_Stream(bool isInputStreamSeekable) + public Task IdentifyWithFormatAsync_CustomConfiguration_Stream(bool isInputStreamSeekable) { this.isTestStreamSeekable = isInputStreamSeekable; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(this.TopLevelConfiguration, this.DataStream, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(options, this.DataStream, this.cts.Token)); } [Fact] - public async Task IdentifyWithFormatAsync_CustomConfiguration_Path() + public Task IdentifyWithFormatAsync_CustomConfiguration_Path() { this.isTestStreamSeekable = true; _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(this.TopLevelConfiguration, this.MockFilePath, this.cts.Token)); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(options, this.MockFilePath, this.cts.Token)); } [Fact] - public async Task IdentifyWithFormatAsync_DefaultConfiguration_Stream() + public Task IdentifyWithFormatAsync_DefaultConfiguration_Stream() { _ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); - await Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(this.DataStream, this.cts.Token)); + return Assert.ThrowsAsync(() => Image.IdentifyWithFormatAsync(this.DataStream, this.cts.Token)); } private async Task DoCancel() diff --git a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs index 446d36c062..dab6000f3c 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -29,26 +29,23 @@ public class DetectFormat : ImageLoadTestBase private static readonly IImageFormat ExpectedGlobalFormat = Configuration.Default.ImageFormatsManager.FindFormatByFileExtension("bmp"); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void FromBytes_GlobalConfiguration(bool useSpan) + [Fact] + public void FromBytes_GlobalConfiguration() { - IImageFormat type = useSpan - ? Image.DetectFormat(this.ActualImageSpan) - : Image.DetectFormat(this.ActualImageBytes); + IImageFormat type = Image.DetectFormat(this.ActualImageSpan); Assert.Equal(ExpectedGlobalFormat, type); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void FromBytes_CustomConfiguration(bool useSpan) + [Fact] + public void FromBytes_CustomConfiguration() { - IImageFormat type = useSpan - ? Image.DetectFormat(this.LocalConfiguration, this.ByteArray.AsSpan()) - : Image.DetectFormat(this.LocalConfiguration, this.ByteArray); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageFormat type = Image.DetectFormat(options, this.ByteArray); Assert.Equal(this.LocalImageFormat, type); } @@ -63,7 +60,12 @@ public void FromFileSystemPath_GlobalConfiguration() [Fact] public void FromFileSystemPath_CustomConfiguration() { - IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.MockFilePath); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageFormat type = Image.DetectFormat(options, this.MockFilePath); Assert.Equal(this.LocalImageFormat, type); } @@ -80,14 +82,24 @@ public void FromStream_GlobalConfiguration() [Fact] public void FromStream_CustomConfiguration() { - IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageFormat type = Image.DetectFormat(options, this.DataStream); Assert.Equal(this.LocalImageFormat, type); } [Fact] public void WhenNoMatchingFormatFound_ReturnsNull() { - IImageFormat type = Image.DetectFormat(new Configuration(), this.DataStream); + DecoderOptions options = new() + { + Configuration = new() + }; + + IImageFormat type = Image.DetectFormat(options, this.DataStream); Assert.Null(type); } @@ -104,14 +116,24 @@ public async Task FromStreamAsync_GlobalConfiguration() [Fact] public async Task FromStreamAsync_CustomConfiguration() { - IImageFormat type = await Image.DetectFormatAsync(this.LocalConfiguration, new AsyncStreamWrapper(this.DataStream, () => false)); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false)); Assert.Equal(this.LocalImageFormat, type); } [Fact] public async Task WhenNoMatchingFormatFoundAsync_ReturnsNull() { - IImageFormat type = await Image.DetectFormatAsync(new Configuration(), new AsyncStreamWrapper(this.DataStream, () => false)); + DecoderOptions options = new() + { + Configuration = new() + }; + + IImageFormat type = await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false)); Assert.Null(type); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs index 5a50ad96e1..e7f5b52ac5 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -20,7 +20,7 @@ public class Identify : ImageLoadTestBase { private static readonly string ActualImagePath = TestFile.GetInputFileFullPath(TestImages.Bmp.F); - private static readonly Size ExpectedImageSize = new Size(108, 202); + private static readonly Size ExpectedImageSize = new(108, 202); private static byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; @@ -43,7 +43,12 @@ public void FromBytes_GlobalConfiguration() [Fact] public void FromBytes_CustomConfiguration() { - IImageInfo info = Image.Identify(this.LocalConfiguration, this.ByteArray, out IImageFormat type); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageInfo info = Image.Identify(options, this.ByteArray, out IImageFormat type); Assert.Equal(this.LocalImageInfo, info); Assert.Equal(this.LocalImageFormat, type); @@ -61,7 +66,12 @@ public void FromFileSystemPath_GlobalConfiguration() [Fact] public void FromFileSystemPath_CustomConfiguration() { - IImageInfo info = Image.Identify(this.LocalConfiguration, this.MockFilePath, out IImageFormat type); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageInfo info = Image.Identify(options, this.MockFilePath, out IImageFormat type); Assert.Equal(this.LocalImageInfo, info); Assert.Equal(this.LocalImageFormat, type); @@ -70,24 +80,20 @@ public void FromFileSystemPath_CustomConfiguration() [Fact] public void FromStream_GlobalConfiguration() { - using (var stream = new MemoryStream(ActualImageBytes)) - { - IImageInfo info = Image.Identify(stream, out IImageFormat type); + using var stream = new MemoryStream(ActualImageBytes); + IImageInfo info = Image.Identify(stream, out IImageFormat type); - Assert.NotNull(info); - Assert.Equal(ExpectedGlobalFormat, type); - } + Assert.NotNull(info); + Assert.Equal(ExpectedGlobalFormat, type); } [Fact] public void FromStream_GlobalConfiguration_NoFormat() { - using (var stream = new MemoryStream(ActualImageBytes)) - { - IImageInfo info = Image.Identify(stream); + using var stream = new MemoryStream(ActualImageBytes); + IImageInfo info = Image.Identify(stream); - Assert.NotNull(info); - } + Assert.NotNull(info); } [Fact] @@ -116,7 +122,12 @@ public void FromNonSeekableStream_GlobalConfiguration_NoFormat() [Fact] public void FromStream_CustomConfiguration() { - IImageInfo info = Image.Identify(this.LocalConfiguration, this.DataStream, out IImageFormat type); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type); Assert.Equal(this.LocalImageInfo, info); Assert.Equal(this.LocalImageFormat, type); @@ -125,7 +136,12 @@ public void FromStream_CustomConfiguration() [Fact] public void FromStream_CustomConfiguration_NoFormat() { - IImageInfo info = Image.Identify(this.LocalConfiguration, this.DataStream); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageInfo info = Image.Identify(options, this.DataStream); Assert.Equal(this.LocalImageInfo, info); } @@ -133,7 +149,12 @@ public void FromStream_CustomConfiguration_NoFormat() [Fact] public void WhenNoMatchingFormatFound_ReturnsNull() { - IImageInfo info = Image.Identify(new Configuration(), this.DataStream, out IImageFormat type); + DecoderOptions options = new() + { + Configuration = new() + }; + + IImageInfo info = Image.Identify(options, this.DataStream, out IImageFormat type); Assert.Null(info); Assert.Null(type); @@ -168,26 +189,22 @@ public void FromStream_ZeroLength_ReturnsNull() [Fact] public async Task FromStreamAsync_GlobalConfiguration_NoFormat() { - using (var stream = new MemoryStream(ActualImageBytes)) - { - var asyncStream = new AsyncStreamWrapper(stream, () => false); - IImageInfo info = await Image.IdentifyAsync(asyncStream); + using var stream = new MemoryStream(ActualImageBytes); + var asyncStream = new AsyncStreamWrapper(stream, () => false); + IImageInfo info = await Image.IdentifyAsync(asyncStream); - Assert.NotNull(info); - } + Assert.NotNull(info); } [Fact] public async Task FromStreamAsync_GlobalConfiguration() { - using (var stream = new MemoryStream(ActualImageBytes)) - { - var asyncStream = new AsyncStreamWrapper(stream, () => false); - (IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream); + using var stream = new MemoryStream(ActualImageBytes); + var asyncStream = new AsyncStreamWrapper(stream, () => false); + (IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream); - Assert.Equal(ExpectedImageSize, res.ImageInfo.Size()); - Assert.Equal(ExpectedGlobalFormat, res.Format); - } + Assert.Equal(ExpectedImageSize, res.ImageInfo.Size()); + Assert.Equal(ExpectedGlobalFormat, res.Format); } [Fact] @@ -244,14 +261,24 @@ public async Task FromStreamAsync_ZeroLength_ReturnsNull() [Fact] public async Task FromPathAsync_CustomConfiguration() { - IImageInfo info = await Image.IdentifyAsync(this.LocalConfiguration, this.MockFilePath); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + IImageInfo info = await Image.IdentifyAsync(options, this.MockFilePath); Assert.Equal(this.LocalImageInfo, info); } [Fact] public async Task IdentifyWithFormatAsync_FromPath_CustomConfiguration() { - (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(this.LocalConfiguration, this.MockFilePath); + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + + (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(options, this.MockFilePath); Assert.NotNull(info.ImageInfo); Assert.Equal(this.LocalImageFormat, info.Format); } @@ -276,8 +303,13 @@ public async Task FromPathAsync_GlobalConfiguration() [Fact] public async Task FromStreamAsync_CustomConfiguration() { + DecoderOptions options = new() + { + Configuration = this.LocalConfiguration + }; + var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false); - (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(this.LocalConfiguration, asyncStream); + (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(options, asyncStream); Assert.Equal(this.LocalImageInfo, info.ImageInfo); Assert.Equal(this.LocalImageFormat, info.Format); @@ -286,8 +318,13 @@ public async Task FromStreamAsync_CustomConfiguration() [Fact] public async Task WhenNoMatchingFormatFoundAsync_ReturnsNull() { + DecoderOptions options = new() + { + Configuration = new() + }; + var asyncStream = new AsyncStreamWrapper(this.DataStream, () => false); - (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(new Configuration(), asyncStream); + (IImageInfo ImageInfo, IImageFormat Format) info = await Image.IdentifyWithFormatAsync(options, asyncStream); Assert.Null(info.ImageInfo); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index 134387ae6b..1cfc744d00 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -63,28 +63,27 @@ protected ImageLoadTestBase() this.localImageFormatMock = new Mock(); var detector = new Mock(); - detector.Setup(x => x.Identify(It.IsAny(), It.IsAny(), It.IsAny())).Returns(this.localImageInfoMock.Object); + detector.Setup(x => x.Identify(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(this.localImageInfoMock.Object); this.localDecoder = detector.As(); - this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((c, s, ct) => + this.localDecoder + .Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((c, s, ct) => { - using (var ms = new MemoryStream()) - { - s.CopyTo(ms); - this.DecodedData = ms.ToArray(); - } + using var ms = new MemoryStream(); + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); }) .Returns(this.localStreamReturnImageRgba32); - this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((c, s, ct) => + this.localDecoder + .Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((c, s, ct) => { - using (var ms = new MemoryStream()) - { - s.CopyTo(ms); - this.DecodedData = ms.ToArray(); - } + using var ms = new MemoryStream(); + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); }) .Returns(this.localStreamReturnImageAgnostic); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs index 8546b73a33..df68eda0fa 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs @@ -16,7 +16,12 @@ public class Load_FileSystemPath_PassLocalConfiguration : ImageLoadTestBase [Fact] public void Configuration_Path_Specific() { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.MockFilePath); Assert.NotNull(img); Assert.Equal(this.TestFormat.Sample(), img); @@ -27,7 +32,12 @@ public void Configuration_Path_Specific() [Fact] public void Configuration_Path_Agnostic() { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.MockFilePath); Assert.NotNull(img); Assert.Equal(this.TestFormat.SampleAgnostic(), img); @@ -35,28 +45,15 @@ public void Configuration_Path_Agnostic() this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); } - [Fact] - public void Configuration_Path_Decoder_Specific() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream, default)); - } - - [Fact] - public void Configuration_Path_Decoder_Agnostic() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream, default)); - } - [Fact] public void Configuration_Path_OutFormat_Specific() { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.MockFilePath, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); @@ -67,7 +64,12 @@ public void Configuration_Path_OutFormat_Specific() [Fact] public void Configuration_Path_OutFormat_Agnostic() { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.MockFilePath, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); @@ -77,23 +79,28 @@ public void Configuration_Path_OutFormat_Agnostic() [Fact] public void WhenFileNotFound_Throws() - { - Assert.Throws( + => Assert.Throws( () => { - Image.Load(this.TopLevelConfiguration, Guid.NewGuid().ToString()); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + Image.Load(options, Guid.NewGuid().ToString()); }); - } [Fact] public void WhenPathIsNull_Throws() - { - Assert.Throws( + => Assert.Throws( () => { - Image.Load(this.TopLevelConfiguration, (string)null); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + Image.Load(options, (string)null); }); - } } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs index 0b0cf9f712..1ce8fd4eba 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System; +using System.IO; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; @@ -17,10 +18,7 @@ public class Load_FileSystemPath_UseDefaultConfiguration { private string Path { get; } = TestFile.GetInputFileFullPath(TestImages.Bmp.Bit8); - private static void VerifyDecodedImage(Image img) - { - Assert.Equal(new Size(127, 64), img.Size()); - } + private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size()); [Fact] public void Path_Specific() @@ -39,49 +37,21 @@ public void Path_Agnostic() [Fact] public async Task Path_Agnostic_Async() { - using var img = await Image.LoadAsync(this.Path); + using Image img = await Image.LoadAsync(this.Path); VerifyDecodedImage(img); } [Fact] public async Task Path_Specific_Async() { - using var img = await Image.LoadAsync(this.Path); + using Image img = await Image.LoadAsync(this.Path); VerifyDecodedImage(img); } [Fact] public async Task Path_Agnostic_Configuration_Async() { - using var img = await Image.LoadAsync(this.Path); - VerifyDecodedImage(img); - } - - [Fact] - public void Path_Decoder_Specific() - { - using var img = Image.Load(this.Path, new BmpDecoder()); - VerifyDecodedImage(img); - } - - [Fact] - public void Path_Decoder_Agnostic() - { - using var img = Image.Load(this.Path, new BmpDecoder()); - VerifyDecodedImage(img); - } - - [Fact] - public async Task Path_Decoder_Agnostic_Async() - { - using var img = await Image.LoadAsync(this.Path, new BmpDecoder()); - VerifyDecodedImage(img); - } - - [Fact] - public async Task Path_Decoder_Specific_Async() - { - using var img = await Image.LoadAsync(this.Path, new BmpDecoder()); + using Image img = await Image.LoadAsync(this.Path); VerifyDecodedImage(img); } @@ -103,37 +73,19 @@ public void Path_OutFormat_Agnostic() [Fact] public void WhenFileNotFound_Throws() - { - Assert.Throws( - () => - { - Image.Load(Guid.NewGuid().ToString()); - }); - } + => Assert.Throws(() => Image.Load(Guid.NewGuid().ToString())); [Fact] public void WhenPathIsNull_Throws() - { - Assert.Throws( - () => - { - Image.Load((string)null); - }); - } + => Assert.Throws(() => Image.Load((string)null)); [Fact] public Task Async_WhenFileNotFound_Throws() - { - return Assert.ThrowsAsync( - () => Image.LoadAsync(Guid.NewGuid().ToString())); - } + => Assert.ThrowsAsync(() => Image.LoadAsync(Guid.NewGuid().ToString())); [Fact] public Task Async_WhenPathIsNull_Throws() - { - return Assert.ThrowsAsync( - () => Image.LoadAsync((string)null)); - } + => Assert.ThrowsAsync(() => Image.LoadAsync((string)null)); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs index aa3f38f629..d4e8adf114 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs @@ -15,14 +15,15 @@ public class Load_FromBytes_PassLocalConfiguration : ImageLoadTestBase { private ReadOnlySpan ByteSpan => this.ByteArray.AsSpan(); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_Specific(bool useSpan) + [Fact] + public void Configuration_Bytes_Specific() { - var img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.ByteSpan); Assert.NotNull(img); Assert.Equal(this.TestFormat.Sample(), img); @@ -30,14 +31,15 @@ public void Configuration_Bytes_Specific(bool useSpan) this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_Agnostic(bool useSpan) + [Fact] + public void Configuration_Bytes_Agnostic() { - var img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.ByteSpan); Assert.NotNull(img); Assert.Equal(this.TestFormat.SampleAgnostic(), img); @@ -45,45 +47,15 @@ public void Configuration_Bytes_Agnostic(bool useSpan) this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_Decoder_Specific(bool useSpan) - { - var localFormat = new TestFormat(); - - var img = useSpan ? - Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : - Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); - - Assert.NotNull(img); - localFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_Decoder_Agnostic(bool useSpan) + [Fact] + public void Configuration_Bytes_OutFormat_Specific() { - var localFormat = new TestFormat(); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; - var img = useSpan ? - Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : - Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); - - Assert.NotNull(img); - localFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_OutFormat_Specific(bool useSpan) - { - IImageFormat format; - var img = useSpan ? - Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : - Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + var img = Image.Load(options, this.ByteSpan, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); @@ -91,15 +63,15 @@ public void Configuration_Bytes_OutFormat_Specific(bool useSpan) this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Configuration_Bytes_OutFormat_Agnostic(bool useSpan) + [Fact] + public void Configuration_Bytes_OutFormat_Agnostic() { - IImageFormat format; - var img = useSpan ? - Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : - Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.ByteSpan, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index b9435f487a..2b57f9aa13 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -16,81 +16,38 @@ public class Load_FromBytes_UseGlobalConfiguration { private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.Bit8).Bytes; - private static Span ByteSpan => new Span(ByteArray); + private static Span ByteSpan => new(ByteArray); - private static void VerifyDecodedImage(Image img) - { - Assert.Equal(new Size(127, 64), img.Size()); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_Specific(bool useSpan) - { - using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) - { - VerifyDecodedImage(img); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_Agnostic(bool useSpan) - { - using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) - { - VerifyDecodedImage(img); - } - } + private static void VerifyDecodedImage(Image img) => Assert.Equal(new Size(127, 64), img.Size()); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_Decoder_Specific(bool useSpan) + [Fact] + public void Bytes_Specific() { - using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) - { - VerifyDecodedImage(img); - } + using var img = Image.Load(ByteSpan); + VerifyDecodedImage(img); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_Decoder_Agnostic(bool useSpan) + [Fact] + public void Bytes_Agnostic() { - using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) - { - VerifyDecodedImage(img); - } + using var img = Image.Load(ByteSpan); + VerifyDecodedImage(img); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_OutFormat_Specific(bool useSpan) + [Fact] + public void Bytes_OutFormat_Specific() { - IImageFormat format; - using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) - { - VerifyDecodedImage(img); - Assert.IsType(format); - } + using var img = Image.Load(ByteSpan, out IImageFormat format); + VerifyDecodedImage(img); + Assert.IsType(format); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Bytes_OutFormat_Agnostic(bool useSpan) + [Fact] + public void Bytes_OutFormat_Agnostic() { - IImageFormat format; - using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) - { - VerifyDecodedImage(img); - Assert.IsType(format); - } + using var img = Image.Load(ByteSpan, out IImageFormat format); + VerifyDecodedImage(img); + Assert.IsType(format); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs index c33a932c6d..8931ab46c3 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -17,7 +16,12 @@ public class Load_FromStream_PassLocalConfiguration : ImageLoadTestBase [Fact] public void Configuration_Stream_Specific() { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.DataStream); Assert.NotNull(img); Assert.Equal(this.TestFormat.Sample(), img); @@ -28,7 +32,12 @@ public void Configuration_Stream_Specific() [Fact] public void Configuration_Stream_Agnostic() { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.DataStream); Assert.NotNull(img); Assert.Equal(this.TestFormat.SampleAgnostic(), img); @@ -39,8 +48,13 @@ public void Configuration_Stream_Agnostic() [Fact] public void NonSeekableStream() { + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + var stream = new NonSeekableStream(this.DataStream); - var img = Image.Load(this.TopLevelConfiguration, stream); + var img = Image.Load(options, stream); Assert.NotNull(img); @@ -50,38 +64,28 @@ public void NonSeekableStream() [Fact] public async Task NonSeekableStreamAsync() { + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + var stream = new NonSeekableStream(this.DataStream); - Image img = await Image.LoadAsync(this.TopLevelConfiguration, stream); + Image img = await Image.LoadAsync(options, stream); Assert.NotNull(img); this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); } - [Fact] - public void Configuration_Stream_Decoder_Specific() - { - var stream = new MemoryStream(); - var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream, default)); - } - - [Fact] - public void Configuration_Stream_Decoder_Agnostic() - { - var stream = new MemoryStream(); - var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream, default)); - } - [Fact] public void Configuration_Stream_OutFormat_Specific() { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.DataStream, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); @@ -92,7 +96,12 @@ public void Configuration_Stream_OutFormat_Specific() [Fact] public void Configuration_Stream_OutFormat_Agnostic() { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + var img = Image.Load(options, this.DataStream, out IImageFormat format); Assert.NotNull(img); Assert.Equal(this.TestFormat, format); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs index b65d0071ea..7ad660c68c 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs @@ -20,30 +20,23 @@ public class Load_FromStream_Throws : IDisposable [Fact] public void Image_Load_Throws_UnknownImageFormatException() - { - Assert.Throws(() => + => Assert.Throws(() => { - using (Image.Load(Configuration.Default, this.Stream, out IImageFormat format)) + using (Image.Load(DecoderOptions.Default, this.Stream, out IImageFormat format)) { } }); - } [Fact] public void Image_Load_T_Throws_UnknownImageFormatException() - { - Assert.Throws(() => + => Assert.Throws(() => { - using (Image.Load(Configuration.Default, this.Stream, out IImageFormat format)) + using (Image.Load(DecoderOptions.Default, this.Stream, out IImageFormat format)) { } }); - } - public void Dispose() - { - this.Stream?.Dispose(); - } + public void Dispose() => this.Stream?.Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs index 74799eb30f..aacc0cee9a 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -31,71 +31,43 @@ public Load_FromStream_UseDefaultConfiguration() } private static void VerifyDecodedImage(Image img) - { - Assert.Equal(new Size(127, 64), img.Size()); - } + => Assert.Equal(new Size(127, 64), img.Size()); [Fact] public void Stream_Specific() { - using (var img = Image.Load(this.Stream)) - { - VerifyDecodedImage(img); - } + using var img = Image.Load(this.Stream); + VerifyDecodedImage(img); } [Fact] public void Stream_Agnostic() { - using (var img = Image.Load(this.Stream)) - { - VerifyDecodedImage(img); - } + using var img = Image.Load(this.Stream); + VerifyDecodedImage(img); } [Fact] public void Stream_OutFormat_Specific() { - using (var img = Image.Load(this.Stream, out IImageFormat format)) - { - VerifyDecodedImage(img); - Assert.IsType(format); - } - } - - [Fact] - public void Stream_Decoder_Specific() - { - using (var img = Image.Load(this.Stream, new BmpDecoder())) - { - VerifyDecodedImage(img); - } - } - - [Fact] - public void Stream_Decoder_Agnostic() - { - using (var img = Image.Load(this.Stream, new BmpDecoder())) - { - VerifyDecodedImage(img); - } + using var img = Image.Load(this.Stream, out IImageFormat format); + VerifyDecodedImage(img); + Assert.IsType(format); } [Fact] public void Stream_OutFormat_Agnostic() { - using (var img = Image.Load(this.Stream, out IImageFormat format)) - { - VerifyDecodedImage(img); - Assert.IsType(format); - } + using var img = Image.Load(this.Stream, out IImageFormat format); + VerifyDecodedImage(img); + Assert.IsType(format); } [Fact] public async Task Async_Stream_OutFormat_Agnostic() { this.AllowSynchronousIO = false; - var formattedImage = await Image.LoadWithFormatAsync(this.Stream); + (Image Image, IImageFormat Format) formattedImage = await Image.LoadWithFormatAsync(this.Stream); using (formattedImage.Image) { VerifyDecodedImage(formattedImage.Image); @@ -107,27 +79,23 @@ public async Task Async_Stream_OutFormat_Agnostic() public async Task Async_Stream_Specific() { this.AllowSynchronousIO = false; - using (var img = await Image.LoadAsync(this.Stream)) - { - VerifyDecodedImage(img); - } + using Image img = await Image.LoadAsync(this.Stream); + VerifyDecodedImage(img); } [Fact] public async Task Async_Stream_Agnostic() { this.AllowSynchronousIO = false; - using (var img = await Image.LoadAsync(this.Stream)) - { - VerifyDecodedImage(img); - } + using Image img = await Image.LoadAsync(this.Stream); + VerifyDecodedImage(img); } [Fact] public async Task Async_Stream_OutFormat_Specific() { this.AllowSynchronousIO = false; - var formattedImage = await Image.LoadWithFormatAsync(this.Stream); + (Image Image, IImageFormat Format) formattedImage = await Image.LoadWithFormatAsync(this.Stream); using (formattedImage.Image) { VerifyDecodedImage(formattedImage.Image); @@ -135,30 +103,7 @@ public async Task Async_Stream_OutFormat_Specific() } } - [Fact] - public async Task Async_Stream_Decoder_Specific() - { - this.AllowSynchronousIO = false; - using (var img = await Image.LoadAsync(this.Stream, new BmpDecoder())) - { - VerifyDecodedImage(img); - } - } - - [Fact] - public async Task Async_Stream_Decoder_Agnostic() - { - this.AllowSynchronousIO = false; - using (var img = await Image.LoadAsync(this.Stream, new BmpDecoder())) - { - VerifyDecodedImage(img); - } - } - - public void Dispose() - { - this.BaseStream?.Dispose(); - } + public void Dispose() => this.BaseStream?.Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs index c5a94ec4db..6d4198c595 100644 --- a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs +++ b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs @@ -6,7 +6,6 @@ using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; @@ -62,13 +61,18 @@ static void RunTest(string formatInner) IImageEncoder encoder = configuration.ImageFormatsManager.FindEncoder( configuration.ImageFormatsManager.FindFormatByFileExtension(formatInner)); string dir = TestEnvironment.CreateOutputDirectory(".Temp"); - string path = Path.Combine(dir, $"{Guid.NewGuid().ToString()}.{formatInner}"); + string path = Path.Combine(dir, $"{Guid.NewGuid()}.{formatInner}"); using (Image temp = new(2048, 2048)) { temp.Save(path, encoder); } - using var image = Image.Load(configuration, path); + DecoderOptions options = new() + { + Configuration = configuration + }; + + using var image = Image.Load(options, path); File.Delete(path); Assert.Equal(1, image.GetPixelMemoryGroup().Count); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index c4ad11cbfc..a101e6e706 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -42,7 +42,7 @@ public enum TestImageWriteFormat WebpLossy } - private static readonly Dictionary TestProfileValues = new Dictionary + private static readonly Dictionary TestProfileValues = new() { { ExifTag.Software, "Software" }, { ExifTag.Copyright, "Copyright" }, @@ -125,42 +125,40 @@ public void ConstructorCopy() [InlineData(TestImageWriteFormat.WebpLossy)] public void WriteFraction(TestImageWriteFormat imageFormat) { - using (var memStream = new MemoryStream()) - { - double exposureTime = 1.0 / 1600; + using var memStream = new MemoryStream(); + double exposureTime = 1.0 / 1600; - ExifProfile profile = GetExifProfile(); + ExifProfile profile = GetExifProfile(); - profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); + profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); - var image = new Image(1, 1); - image.Metadata.ExifProfile = profile; + var image = new Image(1, 1); + image.Metadata.ExifProfile = profile; - image = WriteAndRead(image, imageFormat); + image = WriteAndRead(image, imageFormat); - profile = image.Metadata.ExifProfile; - Assert.NotNull(profile); + profile = image.Metadata.ExifProfile; + Assert.NotNull(profile); - IExifValue value = profile.GetValue(ExifTag.ExposureTime); - Assert.NotNull(value); - Assert.NotEqual(exposureTime, value.Value.ToDouble()); + IExifValue value = profile.GetValue(ExifTag.ExposureTime); + Assert.NotNull(value); + Assert.NotEqual(exposureTime, value.Value.ToDouble()); - memStream.Position = 0; - profile = GetExifProfile(); + memStream.Position = 0; + profile = GetExifProfile(); - profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true)); - image.Metadata.ExifProfile = profile; + profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true)); + image.Metadata.ExifProfile = profile; - image = WriteAndRead(image, imageFormat); + image = WriteAndRead(image, imageFormat); - profile = image.Metadata.ExifProfile; - Assert.NotNull(profile); + profile = image.Metadata.ExifProfile; + Assert.NotNull(profile); - value = profile.GetValue(ExifTag.ExposureTime); - Assert.Equal(exposureTime, value.Value.ToDouble()); + value = profile.GetValue(ExifTag.ExposureTime); + Assert.Equal(exposureTime, value.Value.ToDouble()); - image.Dispose(); - } + image.Dispose(); } [Theory] @@ -553,38 +551,32 @@ private static Image WriteAndRead(Image image, TestImageWriteFor private static Image WriteAndReadJpeg(Image image) { - using (var memStream = new MemoryStream()) - { - image.SaveAsJpeg(memStream); - image.Dispose(); + using var memStream = new MemoryStream(); + image.SaveAsJpeg(memStream); + image.Dispose(); - memStream.Position = 0; - return Image.Load(memStream); - } + memStream.Position = 0; + return Image.Load(memStream); } private static Image WriteAndReadPng(Image image) { - using (var memStream = new MemoryStream()) - { - image.SaveAsPng(memStream); - image.Dispose(); + using var memStream = new MemoryStream(); + image.SaveAsPng(memStream); + image.Dispose(); - memStream.Position = 0; - return Image.Load(memStream); - } + memStream.Position = 0; + return Image.Load(memStream); } private static Image WriteAndReadWebp(Image image, WebpFileFormatType fileFormat) { - using (var memStream = new MemoryStream()) - { - image.SaveAsWebp(memStream, new WebpEncoder() { FileFormat = fileFormat }); - image.Dispose(); + using var memStream = new MemoryStream(); + image.SaveAsWebp(memStream, new WebpEncoder() { FileFormat = fileFormat }); + image.Dispose(); - memStream.Position = 0; - return Image.Load(memStream, new WebpDecoder()); - } + memStream.Position = 0; + return Image.Load(memStream); } private static void TestProfile(ExifProfile profile) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 70b08b9ec8..7d486f0af8 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { public class IptcProfileTests { - private static JpegDecoder JpegDecoder => new() { IgnoreMetadata = false }; + private static JpegDecoder JpegDecoder => new(); - private static TiffDecoder TiffDecoder => new() { IgnoreMetadata = false }; + private static TiffDecoder TiffDecoder => new(); public static IEnumerable AllIptcTags() { @@ -49,7 +49,7 @@ public void IptcProfile_SetValue_WithStrictEnabled_Works(IptcTag tag) { // arrange var profile = new IptcProfile(); - var value = new string('s', tag.MaxLength() + 1); + string value = new('s', tag.MaxLength() + 1); int expectedLength = tag.MaxLength(); // act @@ -66,7 +66,7 @@ public void IptcProfile_SetValue_WithStrictDisabled_Works(IptcTag tag) { // arrange var profile = new IptcProfile(); - var value = new string('s', tag.MaxLength() + 1); + string value = new('s', tag.MaxLength() + 1); int expectedLength = value.Length; // act @@ -180,8 +180,8 @@ public void IptcProfile_ToAndFromByteArray_Works() { // arrange var profile = new IptcProfile(); - var expectedCaptionWriter = "unittest"; - var expectedCaption = "test"; + const string expectedCaptionWriter = "unittest"; + const string expectedCaption = "test"; profile.SetValue(IptcTag.CaptionWriter, expectedCaptionWriter); profile.SetValue(IptcTag.Caption, expectedCaption); @@ -201,8 +201,8 @@ public void IptcProfile_CloneIsDeep() { // arrange var profile = new IptcProfile(); - var captionWriter = "unittest"; - var caption = "test"; + const string captionWriter = "unittest"; + const string caption = "test"; profile.SetValue(IptcTag.CaptionWriter, captionWriter); profile.SetValue(IptcTag.Caption, caption); @@ -238,8 +238,8 @@ public void WritingImage_PreservesIptcProfile() // arrange var image = new Image(1, 1); image.Metadata.IptcProfile = new IptcProfile(); - var expectedCaptionWriter = "unittest"; - var expectedCaption = "test"; + const string expectedCaptionWriter = "unittest"; + const string expectedCaption = "test"; image.Metadata.IptcProfile.SetValue(IptcTag.CaptionWriter, expectedCaptionWriter); image.Metadata.IptcProfile.SetValue(IptcTag.Caption, expectedCaption); @@ -273,8 +273,8 @@ public void IptcProfile_AddRepeatable_Works(IptcTag tag) { // arrange var profile = new IptcProfile(); - var expectedValue1 = "test"; - var expectedValue2 = "another one"; + const string expectedValue1 = "test"; + const string expectedValue2 = "another one"; profile.SetValue(tag, expectedValue1, false); // act @@ -325,7 +325,7 @@ public void IptcProfile_AddNoneRepeatable_DoesOverrideOldValue(IptcTag tag) { // arrange var profile = new IptcProfile(); - var expectedValue = "another one"; + const string expectedValue = "another one"; profile.SetValue(tag, "test", false); // act @@ -346,7 +346,7 @@ public void IptcProfile_RemoveByTag_RemovesAllEntrys() profile.SetValue(IptcTag.Byline, "test2"); // act - var result = profile.RemoveValue(IptcTag.Byline); + bool result = profile.RemoveValue(IptcTag.Byline); // assert Assert.True(result, "removed result should be true"); @@ -362,7 +362,7 @@ public void IptcProfile_RemoveByTagAndValue_Works() profile.SetValue(IptcTag.Byline, "test2"); // act - var result = profile.RemoveValue(IptcTag.Byline, "test2"); + bool result = profile.RemoveValue(IptcTag.Byline, "test2"); // assert Assert.True(result, "removed result should be true"); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs index 5a901a5cde..8a40ad8ad1 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs @@ -20,15 +20,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Xmp { public class XmpProfileTests { - private static GifDecoder GifDecoder => new() { IgnoreMetadata = false }; + private static GifDecoder GifDecoder => new(); - private static JpegDecoder JpegDecoder => new() { IgnoreMetadata = false }; + private static JpegDecoder JpegDecoder => new(); - private static PngDecoder PngDecoder => new() { IgnoreMetadata = false }; + private static PngDecoder PngDecoder => new(); - private static TiffDecoder TiffDecoder => new() { IgnoreMetadata = false }; + private static TiffDecoder TiffDecoder => new(); - private static WebpDecoder WebpDecoder => new() { IgnoreMetadata = false }; + private static WebpDecoder WebpDecoder => new(); [Theory] [WithFile(TestImages.Gif.Receipt, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs index 82425d50d9..869530f013 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Processing; using Xunit; @@ -23,23 +24,26 @@ public void LoadResizeSave(string imagePath) var configuration = Configuration.CreateDefaultInstance(); configuration.MaxDegreeOfParallelism = 1; + DecoderOptions options = new() + { + Configuration = configuration + }; + byte[] imageBytes = TestFile.Create(imagePath).Bytes; - using (var ms = new MemoryStream()) - { - this.Measure( - 30, - () => + using var ms = new MemoryStream(); + this.Measure( + 30, + () => + { + using (var image = Image.Load(options, imageBytes)) { - using (var image = Image.Load(configuration, imageBytes)) - { - image.Mutate(x => x.Resize(image.Size() / 4)); - image.SaveAsJpeg(ms); - } - - ms.Seek(0, SeekOrigin.Begin); - }); - } + image.Mutate(x => x.Resize(image.Size() / 4)); + image.SaveAsJpeg(ms); + } + + ms.Seek(0, SeekOrigin.Begin); + }); } } } diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 825f0263cb..ed5c4a0f09 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -13,18 +13,18 @@ namespace SixLabors.ImageSharp.Tests /// /// A test image file. /// - public class TestFile + public sealed class TestFile { /// /// The test file cache. /// - private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary Cache = new(); /// /// The "Formats" directory, as lazy value /// // ReSharper disable once InconsistentNaming - private static readonly Lazy InputImagesDirectoryValue = new Lazy(() => TestEnvironment.InputImagesDirectoryFullPath); + private static readonly Lazy InputImagesDirectoryValue = new(() => TestEnvironment.InputImagesDirectoryFullPath); /// /// The image (lazy initialized value) @@ -40,15 +40,12 @@ public class TestFile /// Initializes a new instance of the class. /// /// The file. - private TestFile(string file) - { - this.FullPath = file; - } + private TestFile(string file) => this.FullPath = file; /// /// Gets the image bytes. /// - public byte[] Bytes => this.bytes ?? (this.bytes = File.ReadAllBytes(this.FullPath)); + public byte[] Bytes => this.bytes ??= File.ReadAllBytes(this.FullPath); /// /// Gets the full path to file. @@ -68,7 +65,7 @@ private TestFile(string file) /// /// Gets the image with lazy initialization. /// - private Image Image => this.image ?? (this.image = ImageSharp.Image.Load(this.Bytes)); + private Image Image => this.image ??= ImageSharp.Image.Load(this.Bytes); /// /// Gets the input image directory. @@ -85,9 +82,7 @@ private TestFile(string file) /// The . /// public static string GetInputFileFullPath(string file) - { - return Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); - } + => Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); /// /// Creates a new test file or returns one from the cache. @@ -97,9 +92,7 @@ public static string GetInputFileFullPath(string file) /// The . /// public static TestFile Create(string file) - { - return Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(file))); - } + => Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(fileName))); /// /// Gets the file name. @@ -109,9 +102,7 @@ public static TestFile Create(string file) /// The . /// public string GetFileName(object value) - { - return $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; - } + => $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; /// /// Gets the file name without extension. @@ -121,30 +112,37 @@ public string GetFileName(object value) /// The . /// public string GetFileNameWithoutExtension(object value) - { - return this.FileNameWithoutExtension + "-" + value; - } + => this.FileNameWithoutExtension + "-" + value; /// - /// Creates a new image. + /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateRgba32Image() - { - return this.Image.Clone(); - } + => this.Image.Clone(); /// - /// Creates a new image. + /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateRgba32Image(IImageDecoder decoder) + => this.CreateRgba32Image(decoder, new()); + + /// + /// Creates a new image. + /// + /// + /// The . + /// + public Image CreateRgba32Image(IImageDecoder decoder, DecoderOptions options) { - return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); + options.Configuration = this.Image.GetConfiguration(); + using MemoryStream stream = new(this.Bytes); + return decoder.Decode(options, stream, default); } } } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 8060183240..9ec7d8f0be 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests /// public class TestFormat : IConfigurationModule, IImageFormat { - private readonly Dictionary sampleImages = new Dictionary(); + private readonly Dictionary sampleImages = new(); // We should not change Configuration.Default in individual tests! // Create new configuration instances with new Configuration(TestFormat.GlobalTestFormat) instead! @@ -56,7 +56,7 @@ public MemoryStream CreateStream(byte[] marker = null) public Stream CreateAsyncSemaphoreStream(SemaphoreSlim notifyWaitPositionReachedSemaphore, SemaphoreSlim continueSemaphore, bool seeakable, int size = 1024, int waitAfterPosition = 512) { - var buffer = new byte[size]; + byte[] buffer = new byte[size]; this.header.CopyTo(buffer, 0); var semaphoreStream = new SemaphoreReadMemoryStream(buffer, waitAfterPosition, notifyWaitPositionReachedSemaphore, continueSemaphore); return seeakable ? semaphoreStream : new AsyncStreamWrapper(semaphoreStream, () => false); @@ -67,7 +67,7 @@ public void VerifySpecificDecodeCall(byte[] marker, Configuration config { DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TPixel))).ToArray(); - Assert.True(discovered.Any(), "No calls to decode on this format with the provided options happened"); + Assert.True(discovered.Length > 0, "No calls to decode on this format with the provided options happened"); foreach (DecodeOperation d in discovered) { @@ -79,7 +79,7 @@ public void VerifyAgnosticDecodeCall(byte[] marker, Configuration config) { DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TestPixelForAgnosticDecode))).ToArray(); - Assert.True(discovered.Any(), "No calls to decode on this format with the provided options happened"); + Assert.True(discovered.Length > 0, "No calls to decode on this format with the provided options happened"); foreach (DecodeOperation d in discovered) { @@ -194,7 +194,7 @@ public IImageFormat DetectFormat(ReadOnlySpan header) public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : IImageDecoder, IImageInfoDetector + public class TestDecoder : IImageDecoderSpecialized { private readonly TestFormat testFormat; @@ -206,20 +206,29 @@ public class TestDecoder : IImageDecoder, IImageInfoDetector public int HeaderSize => this.testFormat.HeaderSize; - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + 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); + + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel - => this.DecodeImpl(configuration, stream); + => ((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); - private Image DecodeImpl(Configuration config, Stream stream) + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { + Configuration configuration = options.GeneralOptions.Configuration; var ms = new MemoryStream(); - stream.CopyTo(ms, config.StreamProcessingBufferSize); + stream.CopyTo(ms, configuration.StreamProcessingBufferSize); byte[] marker = ms.ToArray().Skip(this.testFormat.header.Length).ToArray(); this.testFormat.DecodeCalls.Add(new DecodeOperation { Marker = marker, - Config = config, + Config = configuration, PixelType = typeof(TPixel) }); @@ -227,12 +236,13 @@ private Image DecodeImpl(Configuration config, Stream stream) return this.testFormat.Sample(); } - public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + } - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) => - this.DecodeImpl(configuration, stream); + public class TestDecoderOptions : ISpecializedDecoderOptions + { + public DecoderOptions GeneralOptions { get; set; } = new(); } public class TestEncoder : IImageEncoder diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index e77dce8612..8857471d03 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -24,8 +24,7 @@ private static string StringifyReports(IEnumerable report sb.Append(Environment.NewLine); - // TODO: We should add macOS. - sb.AppendFormat("Test Environment OS : {0}", TestEnvironment.IsWindows ? "Windows" : "Linux"); + sb.AppendFormat("Test Environment OS : {0}", GetEnvironmentName()); sb.Append(Environment.NewLine); sb.AppendFormat("Test Environment is CI : {0}", TestEnvironment.RunsOnCI); @@ -34,10 +33,13 @@ private static string StringifyReports(IEnumerable report sb.AppendFormat("Test Environment is .NET Core : {0}", !TestEnvironment.IsFramework); sb.Append(Environment.NewLine); + sb.AppendFormat("Test Environment is Mono : {0}", TestEnvironment.IsMono); + sb.Append(Environment.NewLine); + int i = 0; foreach (ImageSimilarityReport r in reports) { - sb.Append($"Report ImageFrame {i}: "); + sb.Append("Report ImageFrame {i}: "); sb.Append(r); sb.Append(Environment.NewLine); i++; @@ -45,5 +47,25 @@ private static string StringifyReports(IEnumerable report return sb.ToString(); } + + private static string GetEnvironmentName() + { + if (TestEnvironment.IsMacOS) + { + return "MacOS"; + } + + if (TestEnvironment.IsMacOS) + { + return "Linux"; + } + + if (TestEnvironment.IsWindows) + { + return "Windows"; + } + + return "Unknown"; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index cb19802ceb..733e3a31f5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -7,7 +7,6 @@ using System.IO; using System.Reflection; using System.Threading.Tasks; -using SixLabors.ImageSharp.Diagnostics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -29,38 +28,73 @@ private class Key : IEquatable private readonly Dictionary decoderParameters; - public Key(PixelTypes pixelType, string filePath, IImageDecoder customDecoder) + public Key( + PixelTypes pixelType, + string filePath, + IImageDecoder customDecoder, + DecoderOptions options, + ISpecializedDecoderOptions specialized) { Type customType = customDecoder?.GetType(); this.commonValues = new Tuple( pixelType, filePath, customType); - this.decoderParameters = GetDecoderParameters(customDecoder); + this.decoderParameters = GetDecoderParameters(options, specialized); } - private static Dictionary GetDecoderParameters(IImageDecoder customDecoder) + private static Dictionary GetDecoderParameters( + DecoderOptions options, + ISpecializedDecoderOptions specialized) { - Type type = customDecoder.GetType(); + Type type = options.GetType(); var data = new Dictionary(); while (type != null && type != typeof(object)) { - PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo p in properties) + foreach (PropertyInfo p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { string key = $"{type.FullName}.{p.Name}"; - object value = p.GetValue(customDecoder); - data[key] = value; + data[key] = p.GetValue(options); } type = type.GetTypeInfo().BaseType; } + GetSpecializedDecoderParameters(data, specialized); + return data; } + private static void GetSpecializedDecoderParameters( + Dictionary data, + ISpecializedDecoderOptions options) + { + if (options is null) + { + return; + } + + Type type = options.GetType(); + + while (type != null && type != typeof(object)) + { + foreach (PropertyInfo p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (p.PropertyType == typeof(DecoderOptions)) + { + continue; + } + + string key = $"{type.FullName}.{p.Name}"; + data[key] = p.GetValue(options); + } + + type = type.GetTypeInfo().BaseType; + } + } + public bool Equals(Key other) { if (other is null) @@ -90,7 +124,7 @@ public bool Equals(Key other) return false; } - if (!object.Equals(kv.Value, otherVal)) + if (!Equals(kv.Value, otherVal)) { return false; } @@ -126,7 +160,7 @@ public override bool Equals(object obj) public static bool operator !=(Key left, Key right) => !Equals(left, right); } - private static readonly ConcurrentDictionary> Cache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> Cache = new(); // Needed for deserialization! // ReSharper disable once UnusedMember.Local @@ -149,35 +183,78 @@ public override Image GetImage() return this.GetImage(decoder); } - public override Image GetImage(IImageDecoder decoder) + public override Image GetImage(IImageDecoder decoder, DecoderOptions options) { Guard.NotNull(decoder, nameof(decoder)); + Guard.NotNull(options, nameof(options)); // Do not cache with 64 bits or if image has been created with non-default MemoryAllocator if (!TestEnvironment.Is64BitProcess || this.Configuration.MemoryAllocator != MemoryAllocator.Default) { - return this.LoadImage(decoder); + return this.DecodeImage(decoder, options); } // do not cache so we can track allocation correctly when validating memory if (MemoryAllocatorValidator.MonitoringAllocations) { - return this.LoadImage(decoder); + return this.DecodeImage(decoder, options); } - var key = new Key(this.PixelType, this.FilePath, decoder); - Image cachedImage = Cache.GetOrAdd(key, _ => this.LoadImage(decoder)); + var key = new Key(this.PixelType, this.FilePath, decoder, options, null); + Image cachedImage = Cache.GetOrAdd(key, _ => this.DecodeImage(decoder, options)); return cachedImage.Clone(this.Configuration); } - public override Task> GetImageAsync(IImageDecoder decoder) + public override Task> GetImageAsync(IImageDecoder decoder, DecoderOptions options) { Guard.NotNull(decoder, nameof(decoder)); + Guard.NotNull(options, nameof(options)); + + options.Configuration = this.Configuration; // Used in small subset of decoder tests, no caching. + // TODO: Check Path here. Why combined? string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath); - return Image.LoadAsync(this.Configuration, path, decoder); + using Stream stream = System.IO.File.OpenRead(path); + return Task.FromResult(decoder.Decode(options, stream, default)); + } + + public override Image GetImage(IImageDecoderSpecialized decoder, T options) + { + Guard.NotNull(decoder, nameof(decoder)); + Guard.NotNull(options, nameof(options)); + + // Do not cache with 64 bits or if image has been created with non-default MemoryAllocator + if (!TestEnvironment.Is64BitProcess || this.Configuration.MemoryAllocator != MemoryAllocator.Default) + { + return this.DecodeImage(decoder, options); + } + + // do not cache so we can track allocation correctly when validating memory + if (MemoryAllocatorValidator.MonitoringAllocations) + { + return this.DecodeImage(decoder, options); + } + + var key = new Key(this.PixelType, this.FilePath, decoder, options.GeneralOptions, options); + Image cachedImage = Cache.GetOrAdd(key, _ => this.DecodeImage(decoder, options)); + + return cachedImage.Clone(this.Configuration); + } + + public override Task> GetImageAsync(IImageDecoderSpecialized decoder, T options) + { + Guard.NotNull(decoder, nameof(decoder)); + Guard.NotNull(options, nameof(options)); + + options.GeneralOptions.Configuration = this.Configuration; + + // Used in small subset of decoder tests, no caching. + // 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)); } public override void Deserialize(IXunitSerializationInfo info) @@ -193,10 +270,23 @@ public override void Serialize(IXunitSerializationInfo info) info.AddValue("path", this.FilePath); } - private Image LoadImage(IImageDecoder decoder) + private Image DecodeImage(IImageDecoder decoder, DecoderOptions options) { + options.Configuration = this.Configuration; + + var testFile = TestFile.Create(this.FilePath); + using Stream stream = new MemoryStream(testFile.Bytes); + return decoder.Decode(options, stream, default); + } + + private Image DecodeImage(IImageDecoderSpecialized decoder, T options) + where T : class, ISpecializedDecoderOptions, new() + { + options.GeneralOptions.Configuration = this.Configuration; + var testFile = TestFile.Create(this.FilePath); - return Image.Load(this.Configuration, testFile.Bytes, decoder); + using Stream stream = new MemoryStream(testFile.Bytes); + return decoder.Decode(options, stream, default); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index d6d867ab7e..bb0e196499 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -53,19 +53,17 @@ public static TestImageProvider TestPattern( => new TestPatternProvider(width, height).Init(testMethod, pixelTypeOverride); public static TestImageProvider Blank( - int width, - int height, - MethodInfo testMethod = null, - PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new BlankProvider(width, height).Init(testMethod, pixelTypeOverride); + int width, + int height, + MethodInfo testMethod = null, + PixelTypes pixelTypeOverride = PixelTypes.Undefined) + => new BlankProvider(width, height).Init(testMethod, pixelTypeOverride); public static TestImageProvider File( string filePath, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - { - return new FileProvider(filePath).Init(testMethod, pixelTypeOverride); - } + => new FileProvider(filePath).Init(testMethod, pixelTypeOverride); public static TestImageProvider Lambda( string declaringTypeName, @@ -83,9 +81,7 @@ public static TestImageProvider Solid( byte a = 255, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - { - return new SolidProvider(width, height, r, g, b, a).Init(testMethod, pixelTypeOverride); - } + => new SolidProvider(width, height, r, g, b, a).Init(testMethod, pixelTypeOverride); /// /// Returns an instance to the test case with the necessary traits. @@ -93,15 +89,25 @@ public static TestImageProvider Solid( /// A test image. public abstract Image GetImage(); - public virtual Image GetImage(IImageDecoder decoder) - { - throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); - } + public Image GetImage(IImageDecoder decoder) + => this.GetImage(decoder, new()); - public virtual Task> GetImageAsync(IImageDecoder decoder) - { - throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); - } + public Task> GetImageAsync(IImageDecoder decoder) + => this.GetImageAsync(decoder, new()); + + public virtual Image GetImage(IImageDecoder decoder, DecoderOptions options) + => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); + + public virtual Task> GetImageAsync(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) + 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) + 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. @@ -163,14 +169,13 @@ protected TestImageProvider Init( protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) { - string subfolder = testMethod?.DeclaringType.GetAttribute()?.Subfolder - ?? string.Empty; + string subfolder = + testMethod?.DeclaringType.GetAttribute()?.Subfolder ?? string.Empty; + return this.Init(testMethod?.DeclaringType.Name, testMethod?.Name, subfolder, pixelTypeOverride); } public override string ToString() - { - return $"{this.SourceFileOrDescription}[{this.PixelType}]"; - } + => $"{this.SourceFileOrDescription}[{this.PixelType}]"; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index fe60867ca9..1f1d7febe0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -28,39 +28,10 @@ public MagickReferenceDecoder() public static MagickReferenceDecoder Instance { get; } = new(); - private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel - { - Span sourcePixels = MemoryMarshal.Cast(rgbaBytes); - foreach (Memory m in destinationGroup) - { - Span destBuffer = m.Span; - PixelOperations.Instance.FromRgba32( - configuration, - sourcePixels.Slice(0, destBuffer.Length), - destBuffer); - sourcePixels = sourcePixels.Slice(destBuffer.Length); - } - } - - private static void FromRgba64Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel - { - foreach (Memory m in destinationGroup) - { - Span destBuffer = m.Span; - PixelOperations.Instance.FromRgba64Bytes( - configuration, - rgbaBytes, - destBuffer, - destBuffer.Length); - rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 8); - } - } - - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { + Configuration configuration = options.Configuration; var bmpReadDefines = new BmpReadDefines { IgnoreFileSize = !this.validate @@ -100,6 +71,40 @@ public Image Decode(Configuration configuration, Stream stream, return new Image(configuration, new ImageMetadata(), framesList); } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + public 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 + { + Span sourcePixels = MemoryMarshal.Cast(rgbaBytes); + foreach (Memory m in destinationGroup) + { + Span destBuffer = m.Span; + PixelOperations.Instance.FromRgba32( + configuration, + sourcePixels.Slice(0, destBuffer.Length), + destBuffer); + sourcePixels = sourcePixels.Slice(destBuffer.Length); + } + } + + private static void FromRgba64Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) + where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + { + foreach (Memory m in destinationGroup) + { + Span destBuffer = m.Span; + PixelOperations.Instance.FromRgba64Bytes( + configuration, + rgbaBytes, + destBuffer, + destBuffer.Length); + rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 8); + } + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 743a62797b..e14866f818 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -6,52 +6,49 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SDBitmap = System.Drawing.Bitmap; +using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector + public class SystemDrawingReferenceDecoder : IImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public 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 Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + using var sourceBitmap = new SDBitmap(stream); + if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) { - if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) - { - return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sourceBitmap); - } - - using (var convertedBitmap = new System.Drawing.Bitmap( - sourceBitmap.Width, - sourceBitmap.Height, - System.Drawing.Imaging.PixelFormat.Format32bppArgb)) - { - using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) - { - g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; - g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; - g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; - g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; - - g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); - } - - return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); - } + return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sourceBitmap); } - } - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) - { - using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + using var convertedBitmap = new SDBitmap( + sourceBitmap.Width, + sourceBitmap.Height, + System.Drawing.Imaging.PixelFormat.Format32bppArgb); + using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) { - var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); - return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); + g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + + g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); } + + return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 97fc00514e..7e4797c5ab 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests { public static partial class TestEnvironment { - private static readonly Lazy ConfigurationLazy = new Lazy(CreateDefaultConfiguration); + private static readonly Lazy ConfigurationLazy = new(CreateDefaultConfiguration); internal static Configuration Configuration => ConfigurationLazy.Value; @@ -63,8 +63,8 @@ private static Configuration CreateDefaultConfiguration() new WebpConfigurationModule(), new TiffConfigurationModule()); - IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new ImageSharpPngEncoderWithDefaultConfiguration(); - IImageEncoder bmpEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Bmp : new BmpEncoder(); + IImageEncoder pngEncoder = IsWindows ? SystemDrawingReferenceEncoder.Png : new ImageSharpPngEncoderWithDefaultConfiguration(); + IImageEncoder bmpEncoder = IsWindows ? SystemDrawingReferenceEncoder.Bmp : new BmpEncoder(); // Magick codecs should work on all platforms cfg.ConfigureCodecs( @@ -75,7 +75,7 @@ private static Configuration CreateDefaultConfiguration() cfg.ConfigureCodecs( BmpFormat.Instance, - IsWindows ? (IImageDecoder)SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, + IsWindows ? SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, bmpEncoder, new BmpImageFormatDetector()); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 0bb0cf2626..a4956ffb7d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -327,7 +327,8 @@ public static Image GetReferenceOutputImage( decoder ??= TestEnvironment.GetReferenceDecoder(referenceOutputFile); - return Image.Load(referenceOutputFile, decoder); + using FileStream stream = File.OpenRead(referenceOutputFile); + return decoder.Decode(DecoderOptions.Default, stream, default); } public static Image GetReferenceOutputImageMultiFrame( @@ -355,7 +356,8 @@ public static Image GetReferenceOutputImageMultiFrame( throw new Exception("Reference output file missing: " + path); } - var tempImage = Image.Load(path, decoder); + using FileStream stream = File.OpenRead(path); + Image tempImage = decoder.Decode(DecoderOptions.Default, stream, default); temporaryFrameImages.Add(tempImage); } @@ -383,13 +385,11 @@ public static IEnumerable GetReferenceOutputSimilarityRep bool appendPixelTypeToFileName = true) where TPixel : unmanaged, IPixel { - using (Image referenceImage = provider.GetReferenceOutputImage( + using Image referenceImage = provider.GetReferenceOutputImage( testOutputDetails, extension, - appendPixelTypeToFileName)) - { - return comparer.CompareImages(referenceImage, image); - } + appendPixelTypeToFileName); + return comparer.CompareImages(referenceImage, image); } public static Image ComparePixelBufferTo( @@ -534,9 +534,10 @@ public static Image CompareToOriginal( var testFile = TestFile.Create(path); - referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(path); + referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); - using (var original = Image.Load(testFile.Bytes, referenceDecoder)) + using var stream = new MemoryStream(testFile.Bytes); + using (Image original = referenceDecoder.Decode(DecoderOptions.Default, stream, default)) { comparer.VerifySimilarity(original, image); } @@ -559,9 +560,10 @@ public static Image CompareToOriginalMultiFrame( var testFile = TestFile.Create(path); - referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(path); + referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); - using (var original = Image.Load(testFile.Bytes, referenceDecoder)) + using var stream = new MemoryStream(testFile.Bytes); + using (Image original = referenceDecoder.Decode(DecoderOptions.Default, stream, default)) { comparer.VerifySimilarity(original, image); } @@ -584,23 +586,21 @@ internal static void VerifyOperation( bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - operation(image); - - image.DebugSave( - provider, - testOutputDetails, - appendPixelTypeToFileName: appendPixelTypeToFileName, - appendSourceFileOrDescription: appendSourceFileOrDescription); - - image.CompareToReferenceOutput( - comparer, - provider, - testOutputDetails, - appendPixelTypeToFileName: appendPixelTypeToFileName, - appendSourceFileOrDescription: appendSourceFileOrDescription); - } + using Image image = provider.GetImage(); + operation(image); + + image.DebugSave( + provider, + testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName, + appendSourceFileOrDescription: appendSourceFileOrDescription); + + image.CompareToReferenceOutput( + comparer, + provider, + testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName, + appendSourceFileOrDescription: appendSourceFileOrDescription); } /// @@ -683,11 +683,11 @@ internal static string VerifyEncoder( referenceDecoder ??= TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var encodedImage = Image.Load(actualOutputFile, referenceDecoder)) - { - ImageComparer comparer = customComparer ?? ImageComparer.Exact; - comparer.VerifySimilarity(encodedImage, image); - } + using FileStream stream = File.OpenRead(actualOutputFile); + using Image encodedImage = referenceDecoder.Decode(DecoderOptions.Default, stream, default); + + ImageComparer comparer = customComparer ?? ImageComparer.Exact; + comparer.VerifySimilarity(encodedImage, image); return actualOutputFile; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index 81ec462f1a..f97d1341f3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -1,17 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; +using Xunit.Abstractions; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { - using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; - - using Xunit.Abstractions; - public class MagickReferenceCodecTests { public MagickReferenceCodecTests(ITestOutputHelper output) => this.Output = output; @@ -39,17 +39,19 @@ public void MagickDecode_8BitDepthImage_IsEquivalentTo_SystemDrawingResult(path, magickDecoder)) - using (var sdImage = Image.Load(path, sdDecoder)) - { - ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); + 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); + + ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); - mImage.DebugSave(dummyProvider); + mImage.DebugSave(dummyProvider); - if (TestEnvironment.IsWindows) - { - Assert.True(report.IsEmpty); - } + if (TestEnvironment.IsWindows) + { + Assert.True(report.IsEmpty); } } @@ -69,18 +71,17 @@ public void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawi // 1020 == 4 * 255 (Equivalent to manhattan distance of 1+1+1+1=4 in Rgba32 space) 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); + ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); - using (var mImage = Image.Load(path, magickDecoder)) - using (var sdImage = Image.Load(path, sdDecoder)) - { - ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); - - mImage.DebugSave(dummyProvider); + mImage.DebugSave(dummyProvider); - if (TestEnvironment.IsWindows) - { - Assert.True(report.IsEmpty); - } + if (TestEnvironment.IsWindows) + { + Assert.True(report.IsEmpty); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs index 573dbd9b02..e7e215d708 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -16,24 +18,17 @@ public class SystemDrawingReferenceCodecTests { private ITestOutputHelper Output { get; } - public SystemDrawingReferenceCodecTests(ITestOutputHelper output) - { - this.Output = output; - } + public SystemDrawingReferenceCodecTests(ITestOutputHelper output) => this.Output = output; [Theory] [WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Bgra32)] public void To32bppArgbSystemDrawingBitmap(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image)) - { - string fileName = provider.Utility.GetTestOutputFileName("png"); - sdBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); - } - } + using Image image = provider.GetImage(); + using System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image); + string fileName = provider.Utility.GetTestOutputFileName("png"); + sdBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); } [Theory] @@ -43,28 +38,22 @@ public void From32bppArgbSystemDrawingBitmap(TestImageProvider d { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); - using (var sdBitmap = new System.Drawing.Bitmap(path)) - { - using (Image image = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap)) - { - image.DebugSave(dummyProvider); - } - } + using var sdBitmap = new System.Drawing.Bitmap(path); + using Image image = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap); + image.DebugSave(dummyProvider); } private static string SavePng(TestImageProvider provider, PngColorType pngColorType) where TPixel : unmanaged, IPixel { - using (Image sourceImage = provider.GetImage()) + using Image sourceImage = provider.GetImage(); + if (pngColorType != PngColorType.RgbWithAlpha) { - if (pngColorType != PngColorType.RgbWithAlpha) - { - sourceImage.Mutate(c => c.MakeOpaque()); - } - - var encoder = new PngEncoder { ColorType = pngColorType }; - return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); + sourceImage.Mutate(c => c.MakeOpaque()); } + + var encoder = new PngEncoder { ColorType = pngColorType }; + return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); } [Theory] @@ -79,15 +68,11 @@ public void From32bppArgbSystemDrawingBitmap2(TestImageProvider string path = SavePng(provider, PngColorType.RgbWithAlpha); - using (var sdBitmap = new System.Drawing.Bitmap(path)) - { - using (Image original = provider.GetImage()) - using (Image resaved = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap)) - { - ImageComparer comparer = ImageComparer.Exact; - comparer.VerifySimilarity(original, resaved); - } - } + using var sdBitmap = new System.Drawing.Bitmap(path); + using Image original = provider.GetImage(); + using Image resaved = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap); + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); } [Theory] @@ -97,17 +82,11 @@ public void From24bppRgbSystemDrawingBitmap(TestImageProvider pr { string path = SavePng(provider, PngColorType.Rgb); - using (Image original = provider.GetImage()) - { - using (var sdBitmap = new System.Drawing.Bitmap(path)) - { - using (Image resaved = SystemDrawingBridge.From24bppRgbSystemDrawingBitmap(sdBitmap)) - { - ImageComparer comparer = ImageComparer.Exact; - comparer.VerifySimilarity(original, resaved); - } - } - } + using Image original = provider.GetImage(); + using var sdBitmap = new System.Drawing.Bitmap(path); + using Image resaved = SystemDrawingBridge.From24bppRgbSystemDrawingBitmap(sdBitmap); + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); } [Theory] @@ -116,10 +95,9 @@ public void OpenWithReferenceDecoder(TestImageProvider dummyProv where TPixel : unmanaged, IPixel { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); - using (var image = Image.Load(path, SystemDrawingReferenceDecoder.Instance)) - { - image.DebugSave(dummyProvider); - } + using FileStream stream = File.OpenRead(path); + using Image image = SystemDrawingReferenceDecoder.Instance.Decode(DecoderOptions.Default, stream, default); + image.DebugSave(dummyProvider); } [Theory] @@ -127,10 +105,8 @@ public void OpenWithReferenceDecoder(TestImageProvider dummyProv public void SaveWithReferenceEncoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - provider.Utility.SaveTestOutputFile(image, "png", SystemDrawingReferenceEncoder.Png); - } + using Image image = provider.GetImage(); + provider.Utility.SaveTestOutputFile(image, "png", SystemDrawingReferenceEncoder.Png); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index a272303fa9..46e8b34bc1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -18,13 +18,13 @@ namespace SixLabors.ImageSharp.Tests { public class TestImageProviderTests { - public static readonly TheoryData BasicData = new TheoryData + public static readonly TheoryData BasicData = new() { TestImageProvider.Blank(10, 20), TestImageProvider.Blank(10, 20), }; - public static readonly TheoryData FileData = new TheoryData + public static readonly TheoryData FileData = new() { TestImageProvider.File(TestImages.Bmp.Car), TestImageProvider.File(TestImages.Bmp.F) @@ -43,7 +43,7 @@ public class TestImageProviderTests /// A test image. public static Image CreateTestImage() where TPixel : unmanaged, IPixel => - new Image(3, 3); + new(3, 3); [Theory] [MemberData(nameof(BasicData))] @@ -109,19 +109,31 @@ public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParam TestDecoderWithParameters.DoTestThreadSafe( () => { - string testName = nameof(this + const string testName = nameof(this .GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); - var decoder1 = new TestDecoderWithParameters { Param1 = "Lol", Param2 = 42 }; + TestDecoderWithParameters decoder1 = new(); + TestDecoderWithParametersOptions options1 = new() + { + Param1 = "Lol", + Param2 = 42 + }; + decoder1.InitCaller(testName); - var decoder2 = new TestDecoderWithParameters { Param1 = "LoL", Param2 = 42 }; + TestDecoderWithParameters decoder2 = new(); + TestDecoderWithParametersOptions options2 = new() + { + Param1 = "LoL", + Param2 = 42 + }; + decoder2.InitCaller(testName); - provider.GetImage(decoder1); + provider.GetImage(decoder1, options1); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); - provider.GetImage(decoder2); + provider.GetImage(decoder2, options2); Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); }); } @@ -143,19 +155,31 @@ public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParamete TestDecoderWithParameters.DoTestThreadSafe( () => { - string testName = nameof(this + const string testName = nameof(this .GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); - var decoder1 = new TestDecoderWithParameters { Param1 = "Lol", Param2 = 666 }; + TestDecoderWithParameters decoder1 = new(); + TestDecoderWithParametersOptions options1 = new() + { + Param1 = "Lol", + Param2 = 666 + }; + decoder1.InitCaller(testName); - var decoder2 = new TestDecoderWithParameters { Param1 = "Lol", Param2 = 666 }; + TestDecoderWithParameters decoder2 = new(); + TestDecoderWithParametersOptions options2 = new() + { + Param1 = "Lol", + Param2 = 666 + }; + decoder2.InitCaller(testName); - provider.GetImage(decoder1); + provider.GetImage(decoder1, options1); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); - provider.GetImage(decoder2); + provider.GetImage(decoder2, options2); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); }); } @@ -179,16 +203,14 @@ public void PixelType_PropertyValueIsCorrect(TestImageProvider p public void SaveTestOutputFileMultiFrame(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); + using Image image = provider.GetImage(); + string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); - Assert.True(files.Length > 2); - foreach (string path in files) - { - this.Output.WriteLine(path); - Assert.True(File.Exists(path)); - } + Assert.True(files.Length > 2); + foreach (string path in files) + { + this.Output.WriteLine(path); + Assert.True(File.Exists(path)); } } @@ -199,10 +221,8 @@ public void SaveTestOutputFileMultiFrame(TestImageProvider provi public void Use_WithBasicTestPatternImages(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image img = provider.GetImage()) - { - img.DebugSave(provider); - } + using Image img = provider.GetImage(); + img.DebugSave(provider); } [Theory] @@ -238,24 +258,20 @@ public void Use_WithFileAttribute(TestImageProvider provider, in where TPixel : unmanaged, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - using (Image img = provider.GetImage()) - { - Assert.True(img.Width * img.Height > 0); + using Image img = provider.GetImage(); + Assert.True(img.Width * img.Height > 0); - Assert.Equal(123, yo); + Assert.Equal(123, yo); - string fn = provider.Utility.GetTestOutputFileName("jpg"); - this.Output.WriteLine(fn); - } + string fn = provider.Utility.GetTestOutputFileName("jpg"); + this.Output.WriteLine(fn); } [Theory] [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] public void Use_WithFileAttribute_CustomConfig(TestImageProvider provider) where TPixel : unmanaged, IPixel - { - EnsureCustomConfigurationIsApplied(provider); - } + => EnsureCustomConfigurationIsApplied(provider); [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)] @@ -263,10 +279,8 @@ public void Use_WithFileCollection(TestImageProvider provider) where TPixel : unmanaged, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - using (Image image = provider.GetImage()) - { - provider.Utility.SaveTestOutputFile(image, "png"); - } + using Image image = provider.GetImage(); + provider.Utility.SaveTestOutputFile(image, "png"); } [Theory] @@ -312,19 +326,15 @@ public void Use_WithSolidFilledImagesAttribute(TestImageProvider public void Use_WithTestPatternImages(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image img = provider.GetImage()) - { - img.DebugSave(provider); - } + using Image img = provider.GetImage(); + img.DebugSave(provider); } [Theory] [WithTestPatternImages(20, 20, PixelTypes.Rgba32)] public void Use_WithTestPatternImages_CustomConfiguration(TestImageProvider provider) where TPixel : unmanaged, IPixel - { - EnsureCustomConfigurationIsApplied(provider); - } + => EnsureCustomConfigurationIsApplied(provider); private static void EnsureCustomConfigurationIsApplied(TestImageProvider provider) where TPixel : unmanaged, IPixel @@ -334,22 +344,19 @@ private static void EnsureCustomConfigurationIsApplied(TestImageProvider var customConfiguration = Configuration.CreateDefaultInstance(); provider.Configuration = customConfiguration; - using (Image image2 = provider.GetImage()) - using (Image image3 = provider.GetImage()) - { - Assert.Same(customConfiguration, image2.GetConfiguration()); - Assert.Same(customConfiguration, image3.GetConfiguration()); - } + using Image image2 = provider.GetImage(); + using Image image3 = provider.GetImage(); + Assert.Same(customConfiguration, image2.GetConfiguration()); + Assert.Same(customConfiguration, image3.GetConfiguration()); } } - private class TestDecoder : IImageDecoder + private class TestDecoder : IImageDecoderSpecialized { // Couldn't make xUnit happy without this hackery: - private static readonly ConcurrentDictionary InvocationCounts = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InvocationCounts = new(); - private static readonly object Monitor = new object(); + private static readonly object Monitor = new(); private string callerName; @@ -361,13 +368,26 @@ public static void DoTestThreadSafe(Action action) } } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderOptions)(new() { GeneralOptions = 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 { InvocationCounts[this.callerName]++; return new Image(42, 42); } + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -375,23 +395,16 @@ internal void InitCaller(string name) this.callerName = name; InvocationCounts[name] = 0; } - - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); } - private class TestDecoderWithParameters : IImageDecoder + private class TestDecoderWithParameters : IImageDecoderSpecialized { - private static readonly ConcurrentDictionary InvocationCounts = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InvocationCounts = new(); - private static readonly object Monitor = new object(); + private static readonly object Monitor = new(); private string callerName; - public string Param1 { get; set; } - - public int Param2 { get; set; } - public static void DoTestThreadSafe(Action action) { lock (Monitor) @@ -400,13 +413,26 @@ public static void DoTestThreadSafe(Action action) } } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderWithParametersOptions)(new() { GeneralOptions = 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 { InvocationCounts[this.callerName]++; return new Image(42, 42); } + public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -414,8 +440,20 @@ internal void InitCaller(string name) this.callerName = name; InvocationCounts[name] = 0; } + } + + private class TestDecoderOptions : ISpecializedDecoderOptions + { + public DecoderOptions GeneralOptions { get; set; } = new(); + } + + private class TestDecoderWithParametersOptions : ISpecializedDecoderOptions + { + public string Param1 { get; set; } + + public int Param2 { get; set; } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + public DecoderOptions GeneralOptions { get; set; } = new(); } } } diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png new file mode 100644 index 0000000000..87c1fb7286 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfd3afda359646aa3d46e1cffbfa7060395fda4c36c419ed3a2d8865284d090d +size 4031 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png new file mode 100644 index 0000000000..e982d9034d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bb6ed717a2af582d60ccd6c1c9c1ac92df0f8662755530b7e9063724835b23b +size 27709 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png new file mode 100644 index 0000000000..65aac22e90 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5 +size 28178 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png new file mode 100644 index 0000000000..65aac22e90 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5 +size 28178 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png new file mode 100644 index 0000000000..abe31b2be7 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc67170d70378ad8b8c0e1c1695b5c268341f0d26a6c788d1a8dffa8c90482a0 +size 102165 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png new file mode 100644 index 0000000000..87087adc51 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae7f6ebfd9f2ddd85611827fda13eaf316d36d5187900458568f80b929effb9b +size 28291 diff --git a/tests/Images/External/ReferenceOutput/PbmDecoderTests/PbmDecoder_Decode_Resize_rgb_plain_150_150.png b/tests/Images/External/ReferenceOutput/PbmDecoderTests/PbmDecoder_Decode_Resize_rgb_plain_150_150.png new file mode 100644 index 0000000000..b7e848d841 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PbmDecoderTests/PbmDecoder_Decode_Resize_rgb_plain_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccd9636f787948659673936798e594a8a100909312350ec9d15da0a3317e6c6b +size 200 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png new file mode 100644 index 0000000000..c697cd4c29 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c87a09b28b3f857e13a2bb420d16caa131a67c45b0fe31404756042f30de6d0 +size 28139 diff --git a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png new file mode 100644 index 0000000000..f3b7b0e809 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb67006d156cff0fa8d4d1b131ac0eaa98279ae6dcc477818b2fc65f2dfb77aa +size 23396 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png new file mode 100644 index 0000000000..412af0ac7a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53b9dabffaae6a9250bf16f201ef8c9e933327874e8be91785c4fb6cd7e787a8 +size 10767 diff --git a/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png new file mode 100644 index 0000000000..35b99df985 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d73ae8bfad75c8b169fb050653eb4f8351edf173d1cc121ff1e23e3f1e594a97 +size 36342