From 4099205e5997b9922e3b9c8fce1b3c833f7a4068 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 21:11:58 +1000 Subject: [PATCH 01/54] Stub out options types. --- src/ImageSharp/Formats/Bmp/BmpDecoder2.cs | 30 +++++ .../Formats/Bmp/BmpDecoderOptions.cs | 20 +++ src/ImageSharp/Formats/DecoderOptions.cs | 33 +++++ src/ImageSharp/Formats/IImageDecoder2.cs | 37 ++++++ src/ImageSharp/Formats/IImageInfoDetector2.cs | 24 ++++ .../Formats/ISpecializedDecoderOptions.cs | 16 +++ src/ImageSharp/Formats/ImageDecoder{T}.cs | 119 ++++++++++++++++++ 7 files changed, 279 insertions(+) create mode 100644 src/ImageSharp/Formats/Bmp/BmpDecoder2.cs create mode 100644 src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/DecoderOptions.cs create mode 100644 src/ImageSharp/Formats/IImageDecoder2.cs create mode 100644 src/ImageSharp/Formats/IImageInfoDetector2.cs create mode 100644 src/ImageSharp/Formats/ISpecializedDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/ImageDecoder{T}.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs new file mode 100644 index 000000000..97439182d --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Image decoder for generating an image out of a Windows bitmap stream. + /// + public class BmpDecoder2 : ImageDecoder + { + /// + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + /// + public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => throw new NotImplementedException(); + } +} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs new file mode 100644 index 000000000..74509d68f --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Image decoder options for decoding Windows bitmap streams. + /// + public sealed class BmpDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + + /// + /// Gets the value indicating how to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. + /// + public RleSkippedPixelHandling RleSkippedPixelHandling { get; } + } +} diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs new file mode 100644 index 000000000..a690b0607 --- /dev/null +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Provides general configuration options for decoding image formats. + /// + public sealed class DecoderOptions + { + /// + /// 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 a value indicating whether to ignore encoded metadata when decoding. + /// + public bool SkipMetadata { get; set; } = false; + + /// + /// Gets or sets the number of image frames to decode. + /// Leave to decode all frames. + /// + // supported decoders may handle this internally but we will fallback to removeing additional frames after decode. + public int? MaxFrames { get; set; } = null; + } +} diff --git a/src/ImageSharp/Formats/IImageDecoder2.cs b/src/ImageSharp/Formats/IImageDecoder2.cs new file mode 100644 index 000000000..76d1a754d --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoder2.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Encapsulates properties and methods required for decoding an image from a stream. + /// + public interface IImageDecoder2 + { + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// 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 general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/IImageInfoDetector2.cs b/src/ImageSharp/Formats/IImageInfoDetector2.cs new file mode 100644 index 000000000..3d340f06e --- /dev/null +++ b/src/ImageSharp/Formats/IImageInfoDetector2.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Encapsulates methods used for detecting the raw image information without fully decoding it. + /// + public interface IImageInfoDetector2 + { + /// + /// Reads the raw image information from the specified stream. + /// + /// The general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The object. + /// Thrown if the encoded image contains errors. + 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 000000000..b79938f08 --- /dev/null +++ b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +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/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs new file mode 100644 index 000000000..96080b3be --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoder{T}.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all image decoders. + /// + /// The type of specialized decoder options. + public abstract class ImageDecoder : IImageInfoDetector2, IImageDecoder2 + where T : ISpecializedDecoderOptions, new() + { + /// + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + T specializedOptions = new() { GeneralOptions = options }; + return this.IdentifySpecialized(specializedOptions, stream, cancellationToken); + } + + /// + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + T specializedOptions = new() { GeneralOptions = options }; + Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); + + Resize(options, image); + + return image; + } + + /// + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + T specializedOptions = new() { GeneralOptions = options }; + Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); + + Resize(options, image); + + return image; + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The object. + /// Thrown if the encoded image contains errors. + public abstract IImageInfo IdentifySpecialized(T options, Stream stream, CancellationToken cancellationToken); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); + + /// + /// Performs a resize operation against the decoded image. If the target size is not set, or the image size + /// already matches the target size, the image is untouched. + /// + /// The decoder options. + /// The decoded image. + protected static void Resize(DecoderOptions options, Image image) + { + if (ShouldResize(options, image)) + { + ResizeOptions resizeOptions = new() + { + Size = options.TargetSize.Value, + Sampler = KnownResamplers.Box, + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(resizeOptions)); + } + } + + /// + /// Determines whether the decoded image should be resized. + /// + /// The decoder options. + /// The decoded image. + /// if the image should be resized, otherwise; . + private static bool ShouldResize(DecoderOptions options, Image image) + { + if (options.TargetSize is null) + { + return false; + } + + Size targetSize = options.TargetSize.Value; + Size currentSize = image.Size(); + return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; + } + } +} From dfe04e36e3349e2cef9e6548d889c732e9d817ce Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 22:12:13 +1000 Subject: [PATCH 02/54] Refactor Bmp and Gif --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 40 ++++------ src/ImageSharp/Formats/Bmp/BmpDecoder2.cs | 30 -------- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 41 +++++------ .../Formats/Bmp/BmpDecoderOptions.cs | 2 +- .../Formats/Bmp/IBmpDecoderOptions.cs | 16 ---- src/ImageSharp/Formats/DecoderOptions.cs | 6 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 35 ++++----- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 73 ++++++++++--------- .../Formats/Gif/GifDecoderOptions.cs | 14 ++++ ...ernals.cs => IImageDecoderInternals{T}.cs} | 10 ++- .../Formats/ImageDecoderUtilities.cs | 17 +++-- 11 files changed, 119 insertions(+), 165 deletions(-) delete mode 100644 src/ImageSharp/Formats/Bmp/BmpDecoder2.cs delete mode 100644 src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Gif/GifDecoderOptions.cs rename src/ImageSharp/Formats/{IImageDecoderInternals.cs => IImageDecoderInternals{T}.cs} (87%) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index e76448938..326ba0b74 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System.IO; @@ -10,43 +10,31 @@ 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 : ImageDecoder { - /// - /// 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 + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - var decoder = new BmpDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + BmpDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); + + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - return new BmpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + return new BmpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs deleted file mode 100644 index 97439182d..000000000 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Bmp -{ - /// - /// Image decoder for generating an image out of a Windows bitmap stream. - /// - public class BmpDecoder2 : ImageDecoder - { - /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => throw new NotImplementedException(); - } -} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 26687ff16..f00d4cd03 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// A useful decoding source example can be found at /// - internal sealed class BmpDecoderCore : IImageDecoderInternals + internal sealed class BmpDecoderCore : IImageDecoderInternals { /// /// The default mask for the red part of the color for 16 bit rgb bitmaps. @@ -90,33 +90,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp private BmpInfoHeader infoHeader; /// - /// Used for allocating memory during processing operations. + /// The global configuration. /// - private readonly MemoryAllocator memoryAllocator; + private readonly Configuration configuration; /// - /// The bitmap decoder options. + /// Used for allocating memory during processing operations. /// - private readonly IBmpDecoderOptions options; + private readonly MemoryAllocator memoryAllocator; /// /// 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.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; + this.Options = options; } /// - public Configuration Configuration { get; } + public BmpDecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); /// @@ -128,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { 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 +322,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte colorIdx = bufferRow[x]; if (undefinedPixelsSpan[rowStartIdx + x]) { - switch (this.options.RleSkippedPixelHandling) + switch (this.Options.RleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); @@ -397,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int idx = rowStartIdx + (x * 3); if (undefinedPixelsSpan[yMulWidth + x]) { - switch (this.options.RleSkippedPixelHandling) + switch (this.Options.RleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); @@ -943,7 +940,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -971,7 +968,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -1006,7 +1003,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.stream.Read(rowSpan); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, bgraRowSpan, width); @@ -1042,7 +1039,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.Configuration, + this.configuration, rowSpan, pixelSpan, width); @@ -1056,7 +1053,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { 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 index 74509d68f..535f819d2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { /// - /// Image decoder options for decoding Windows bitmap streams. + /// Configuration options for decoding Windows Bitmap images. /// public sealed class BmpDecoderOptions : ISpecializedDecoderOptions { diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs deleted file mode 100644 index ff88d15a3..000000000 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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 index a690b0607..6b35e2614 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -24,10 +24,8 @@ namespace SixLabors.ImageSharp.Formats public bool SkipMetadata { get; set; } = false; /// - /// Gets or sets the number of image frames to decode. - /// Leave to decode all frames. + /// Gets or sets the maximum number of image frames to decode, inclusive. /// - // supported decoders may handle this internally but we will fallback to removeing additional frames after decode. - public int? MaxFrames { get; set; } = null; + public int MaxFrames { get; set; } = int.MaxValue; } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 6d6cfc079..5550c419e 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,29 @@ 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 : ImageDecoder { - /// - /// 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 + public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - var decoder = new GifDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + GifDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); + + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - var decoder = new GifDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + return new GifDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 2932cafe2..c63a3e08e 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Performs the gif decoding operation. /// - internal sealed class GifDecoderCore : IImageDecoderInternals + internal sealed class GifDecoderCore : IImageDecoderInternals { /// /// The temp buffer used to reduce allocations. @@ -56,6 +56,26 @@ namespace SixLabors.ImageSharp.Formats.Gif /// 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. + /// + private readonly int maxFrames; + + /// + /// Whether to skip metadata during decode. + /// + private readonly bool skipMetadata; + /// /// The abstract metadata. /// @@ -69,39 +89,26 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) + public GifDecoderCore(GifDecoderOptions options) { - this.IgnoreMetadata = options.IgnoreMetadata; - this.DecodingMode = options.DecodingMode; - this.Configuration = configuration ?? Configuration.Default; + this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; + this.maxFrames = options.GeneralOptions.MaxFrames; } /// - 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 GifDecoderOptions 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 { + int frameCount = 0; Image image = null; ImageFrame previousFrame = null; try @@ -114,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (nextFlag == GifConstants.ImageLabel) { - if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + if (previousFrame != null && frameCount++ <= this.maxFrames) { break; } @@ -277,9 +284,9 @@ namespace SixLabors.ImageSharp.Formats.Gif 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 +353,13 @@ namespace SixLabors.ImageSharp.Formats.Gif 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 +392,11 @@ namespace SixLabors.ImageSharp.Formats.Gif 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 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Gif 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 +458,12 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (!transFlag) { - image = new Image(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 +682,7 @@ namespace SixLabors.ImageSharp.Formats.Gif 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/GifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs new file mode 100644 index 000000000..429c1fee1 --- /dev/null +++ b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Gif +{ + /// + /// Configuration options for decoding Gif images. + /// + public sealed class GifDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs similarity index 87% rename from src/ImageSharp/Formats/IImageDecoderInternals.cs rename to src/ImageSharp/Formats/IImageDecoderInternals{T}.cs index e190f7add..ffc839d34 100644 --- a/src/ImageSharp/Formats/IImageDecoderInternals.cs +++ b/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs @@ -9,14 +9,16 @@ using SixLabors.ImageSharp.PixelFormats; 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 + /// The type of specialized decoder options. + internal interface IImageDecoderInternals + where T : ISpecializedDecoderOptions { /// - /// Gets the associated configuration. + /// Gets the specialized decoder options. /// - Configuration Configuration { get; } + T Options { get; } /// /// Gets the dimensions of the image being decoded. diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index 71ecda893..fc78f14b3 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -12,11 +12,12 @@ namespace SixLabors.ImageSharp.Formats { internal static class ImageDecoderUtilities { - public static IImageInfo Identify( - this IImageDecoderInternals decoder, + public static IImageInfo Identify( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) + where T : ISpecializedDecoderOptions { using var bufferedReadStream = new BufferedReadStream(configuration, stream); @@ -30,20 +31,22 @@ namespace SixLabors.ImageSharp.Formats } } - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) + where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel - => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); + => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, Func largeImageExceptionFactory, CancellationToken cancellationToken) + where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel { using var bufferedReadStream = new BufferedReadStream(configuration, stream); From d4539222509b646d624df00e88ac3de00681a229 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 22:32:33 +1000 Subject: [PATCH 03/54] Port Pbm decoder --- .../Formats/Gif/IGifDecoderOptions.cs | 23 ---- src/ImageSharp/Formats/Pbm/PbmDecoder.cs | 24 ++-- src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs | 105 ++++++++++-------- .../Formats/Pbm/PbmDecoderOptions.cs | 14 +++ 4 files changed, 83 insertions(+), 83 deletions(-) delete mode 100644 src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs deleted file mode 100644 index 56bb6d651..000000000 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs index 97a9cb7d7..90752a637 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs @@ -26,29 +26,29 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// The specification of these images is found at . /// - public sealed class PbmDecoder : IImageDecoder, IImageInfoDetector + public sealed class PbmDecoder : ImageDecoder { - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Guard.NotNull(stream, nameof(stream)); + PbmDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); - var decoder = new PbmDecoderCore(configuration); - return decoder.Decode(configuration, stream, cancellationToken); + return image; } /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - var decoder = new PbmDecoderCore(configuration); - return decoder.Identify(configuration, stream, cancellationToken); + return new PbmDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 749fc0292..568c1ab4c 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -14,48 +14,55 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// Performs the PBM decoding operation. /// - internal sealed class PbmDecoderCore : IImageDecoderInternals + 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(PbmDecoderOptions options) + { + this.Options = options; + this.configuration = options.GeneralOptions.Configuration; + } /// - Size IImageDecoderInternals.Dimensions => this.PixelSize; + public PbmDecoderOptions 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 @@ namespace SixLabors.ImageSharp.Formats.Pbm { 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 @@ namespace SixLabors.ImageSharp.Formats.Pbm 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 @@ namespace SixLabors.ImageSharp.Formats.Pbm { 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 @@ namespace SixLabors.ImageSharp.Formats.Pbm 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/Pbm/PbmDecoderOptions.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs new file mode 100644 index 000000000..c0b885650 --- /dev/null +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Pbm +{ + /// + /// Configuration options for decoding Pbm images. + /// + public sealed class PbmDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} From c9beb02c0e8ef29ad5bdd99bd366283be61888da Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 23:06:29 +1000 Subject: [PATCH 04/54] Port Tga and Png --- .../Formats/Png/IPngDecoderOptions.cs | 16 ----- src/ImageSharp/Formats/Png/PngDecoder.cs | 59 ++++++++++--------- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 57 +++++++++--------- .../Formats/Png/PngDecoderOptions.cs | 14 +++++ .../Formats/Tga/ITgaDecoderOptions.cs | 12 ---- src/ImageSharp/Formats/Tga/TgaDecoder.cs | 23 ++++---- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 43 +++++++------- .../Formats/Tga/TgaDecoderOptions.cs | 14 +++++ 8 files changed, 119 insertions(+), 119 deletions(-) delete mode 100644 src/ImageSharp/Formats/Png/IPngDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Png/PngDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs deleted file mode 100644 index 4b09c5b1c..000000000 --- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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 0b233848a..a48b281c5 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -10,24 +10,24 @@ 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 : ImageDecoder { /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PngDecoderCore decoder = new(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + PngDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); + + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + /// + public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PngDecoderCore decoder = new(configuration, true); - IImageInfo info = decoder.Identify(configuration, stream, cancellationToken); + PngDecoderCore decoder = new(options, true); + IImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); stream.Position = 0; PngMetadata meta = info.Metadata.GetPngMetadata(); @@ -39,49 +39,50 @@ namespace SixLabors.ImageSharp.Formats.Png if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); case PngColorType.Palette: - return this.Decode(configuration, stream, cancellationToken); + return this.DecodeSpecialized(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(configuration, stream, cancellationToken) - : this.Decode(configuration, stream, cancellationToken); + ? this.DecodeSpecialized(options, stream, cancellationToken) + : this.DecodeSpecialized(options, stream, cancellationToken); default: - return this.Decode(configuration, stream, cancellationToken); + return this.DecodeSpecialized(options, stream, cancellationToken); } } /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PngDecoderCore decoder = new(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + Guard.NotNull(stream, nameof(stream)); + + return new PngDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f46b5058a..ae01a2f56 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Buffers.Binary; -using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; @@ -28,17 +27,22 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Performs the png decoding operation. /// - internal sealed class PngDecoderCore : IImageDecoderInternals + internal sealed class PngDecoderCore : IImageDecoderInternals { /// /// Reusable buffer. /// 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. @@ -118,29 +122,26 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) + public PngDecoderCore(PngDecoderOptions options) { - this.Configuration = configuration ?? Configuration.Default; - this.memoryAllocator = this.Configuration.MemoryAllocator; - this.ignoreMetadata = options.IgnoreMetadata; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; + this.skipMetadata = options.GeneralOptions.SkipMetadata; } - internal PngDecoderCore(Configuration configuration, bool colorMetadataOnly) + internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) { - this.Configuration = configuration ?? Configuration.Default; - this.memoryAllocator = this.Configuration.MemoryAllocator; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; this.colorMetadataOnly = colorMetadataOnly; - this.ignoreMetadata = true; + this.skipMetadata = true; } /// - public Configuration Configuration { get; } + public PngDecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// + /// public Size Dimensions => new(this.header.Width, this.header.Height); /// @@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Png 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); @@ -336,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Png break; } - if (!this.ignoreMetadata) + if (!this.skipMetadata) { byte[] exifData = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(exifData); @@ -469,7 +470,7 @@ namespace SixLabors.ImageSharp.Formats.Png where TPixel : unmanaged, IPixel { image = Image.CreateUninitialized( - this.Configuration, + this.configuration, this.header.Width, this.header.Height, metadata); @@ -485,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.Png 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); } /// @@ -798,7 +799,7 @@ namespace SixLabors.ImageSharp.Formats.Png case PngColorType.Rgb: PngScanlineProcessor.ProcessRgbScanline( - this.Configuration, + this.configuration, this.header, scanlineSpan, rowSpan, @@ -812,7 +813,7 @@ namespace SixLabors.ImageSharp.Formats.Png case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessRgbaScanline( - this.Configuration, + this.configuration, this.header, scanlineSpan, rowSpan, @@ -1001,7 +1002,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing the data. private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1036,7 +1037,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing the data. private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1222,10 +1223,10 @@ namespace SixLabors.ImageSharp.Formats.Png { 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(); @@ -1324,7 +1325,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing the data. private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan data) { - if (this.ignoreMetadata) + if (this.skipMetadata) { return; } @@ -1563,7 +1564,7 @@ namespace SixLabors.ImageSharp.Formats.Png 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/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs new file mode 100644 index 000000000..f8b3364bb --- /dev/null +++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Png +{ + /// + /// Configuration options for decoding Png images. + /// + public class PngDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} diff --git a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs deleted file mode 100644 index 240b8b9b3..000000000 --- a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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 bb0a0d548..60616f9cd 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -10,28 +10,29 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Image decoder for Truevision TGA images. /// - public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector + public sealed class TgaDecoder : ImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Guard.NotNull(stream, nameof(stream)); + TgaDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); - var decoder = new TgaDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - return new TgaDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + return new TgaDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index d101ccd94..421c0224a 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -15,13 +15,18 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Performs the tga decoding operation. /// - internal sealed class TgaDecoderCore : IImageDecoderInternals + internal sealed class TgaDecoderCore : IImageDecoderInternals { /// /// A scratch buffer to reduce allocations. /// private readonly byte[] scratchBuffer = new byte[4]; + /// + /// General configuration options. + /// + private readonly Configuration configuration; + /// /// The metadata. /// @@ -47,11 +52,6 @@ namespace SixLabors.ImageSharp.Formats.Tga /// private BufferedReadStream currentStream; - /// - /// The bitmap decoder options. - /// - private readonly ITgaDecoderOptions options; - /// /// Indicates whether there is a alpha channel present. /// @@ -60,22 +60,19 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Initializes a new instance of the class. /// - /// The configuration. /// The options. - public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) + public TgaDecoderCore(TgaDecoderOptions options) { - this.Configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; + this.Options = options; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public Configuration Configuration { get; } + public TgaDecoderOptions Options { get; } - /// - /// Gets the dimensions of the image. - /// - public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height); + /// + public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) @@ -87,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Tga this.currentStream.Skip(this.fileHeader.IdLength); // Parse the color map, if present. - if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1) + if (this.fileHeader.ColorMapType is not 0 and not 1) { TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found"); } @@ -97,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Tga 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) @@ -451,11 +448,11 @@ namespace SixLabors.ImageSharp.Formats.Tga 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); } } } @@ -655,7 +652,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { this.currentStream.Read(row); Span pixelSpan = pixels.DangerousGetRowSpan(y); - PixelOperations.Instance.FromL8Bytes(this.Configuration, row, pixelSpan, width); + PixelOperations.Instance.FromL8Bytes(this.configuration, row, pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -682,7 +679,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { this.currentStream.Read(row); Span pixelSpan = pixels.DangerousGetRowSpan(y); - PixelOperations.Instance.FromBgr24Bytes(this.Configuration, row, pixelSpan, width); + PixelOperations.Instance.FromBgr24Bytes(this.configuration, row, pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -701,7 +698,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { this.currentStream.Read(row); 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/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs new file mode 100644 index 000000000..abce31c66 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Configuration options for decoding Png images. + /// + public class TgaDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} From 3917b123d1c1d63b5366262a6abde676282a3623 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 23:23:37 +1000 Subject: [PATCH 05/54] Port Tiff, fix constructors --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 7 +-- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 8 +-- .../Formats/Tga/TgaDecoderOptions.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 33 ++++-------- .../Formats/Tiff/TiffDecoderCore.cs | 50 ++++++++++--------- .../Formats/Tiff/TiffDecoderOptions.cs | 14 ++++++ 7 files changed, 63 insertions(+), 53 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index f00d4cd03..a9f779bfa 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -105,9 +105,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The options. public BmpDecoderCore(BmpDecoderOptions options) { + this.Options = options; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; - this.Options = options; } /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index c63a3e08e..c084f57ce 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Gif private readonly MemoryAllocator memoryAllocator; /// - /// The maximum number of frames to decode. + /// The maximum number of frames to decode. Inclusive. /// private readonly int maxFrames; @@ -92,10 +92,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The decoder options. public GifDecoderCore(GifDecoderOptions options) { - this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.Options = options; this.configuration = options.GeneralOptions.Configuration; - this.memoryAllocator = this.configuration.MemoryAllocator; + this.skipMetadata = options.GeneralOptions.SkipMetadata; this.maxFrames = options.GeneralOptions.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ae01a2f56..383a3ffeb 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -125,17 +125,19 @@ namespace SixLabors.ImageSharp.Formats.Png /// The decoder options. public PngDecoderCore(PngDecoderOptions options) { + this.Options = options; this.configuration = options.GeneralOptions.Configuration; - this.memoryAllocator = this.configuration.MemoryAllocator; this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.memoryAllocator = this.configuration.MemoryAllocator; } internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) { - this.configuration = options.GeneralOptions.Configuration; - this.memoryAllocator = this.configuration.MemoryAllocator; + this.Options = options; this.colorMetadataOnly = colorMetadataOnly; this.skipMetadata = true; + this.configuration = options.GeneralOptions.Configuration; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs index abce31c66..f5c0fb128 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { /// - /// Configuration options for decoding Png images. + /// Configuration options for decoding Tga images. /// public class TgaDecoderOptions : ISpecializedDecoderOptions { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index c4a9f3b22..e503c1af1 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,29 @@ 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 : ImageDecoder { - /// - /// 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 + public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Guard.NotNull(stream, nameof(stream)); + TiffDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); - var decoder = new TiffDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + return image; } /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - var decoder = new TiffDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + return new TiffDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index c595246a5..0bb01fcdc 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -20,8 +20,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the tiff decoding operation. /// - internal class TiffDecoderCore : IImageDecoderInternals + internal class TiffDecoderCore : IImageDecoderInternals { + /// + /// General configuration options. + /// + private readonly Configuration configuration; + /// /// Used for allocating memory during processing operations. /// @@ -30,12 +35,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// 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 the decoding mode for multi-frame images + /// The maximum number of frames to decode. Inclusive. /// - private readonly FrameDecodingMode decodingMode; + private readonly int maxFrames; /// /// The stream to decode from. @@ -55,16 +60,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Initializes a new instance of the class. /// - /// The configuration. /// The decoder options. - public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) + public TiffDecoderCore(TiffDecoderOptions 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.GeneralOptions.Configuration; + this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.maxFrames = options.GeneralOptions.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; } /// @@ -148,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffPredictor Predictor { get; set; } /// - public Configuration Configuration { get; } + public TiffDecoderOptions Options { get; } /// public Size Dimensions { get; private set; } @@ -161,25 +164,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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; + int 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - return new Image(this.Configuration, metadata, frames); + return new Image(this.configuration, metadata, frames); } catch { @@ -209,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff where TPixel : unmanaged, IPixel { var imageFrameMetaData = new ImageFrameMetadata(); - if (!this.ignoreMetadata) + if (!this.skipMetadata) { imageFrameMetaData.ExifProfile = tags; } @@ -245,7 +249,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff } using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Configuration, + this.configuration, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -449,7 +453,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Configuration, + this.configuration, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -463,7 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.byteOrder); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create( - this.Configuration, + this.configuration, this.memoryAllocator, this.ColorType, this.BitsPerSample, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs new file mode 100644 index 000000000..ac0195251 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// Configuration options for decoding Tiff images. + /// + public class TiffDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} From 48a81062c3437f87f7966da03bf238f1ba8f5de9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 23:48:50 +1000 Subject: [PATCH 06/54] Port WebP and cleanup options --- src/ImageSharp/Formats/DecoderOptions.cs | 6 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 6 +- .../Formats/Png/PngDecoderOptions.cs | 2 +- .../Formats/Tga/TgaDecoderOptions.cs | 2 +- .../Formats/Tiff/ITiffDecoderOptions.cs | 23 ---- .../Formats/Tiff/TiffDecoderCore.cs | 8 +- .../Formats/Tiff/TiffDecoderOptions.cs | 2 +- .../Formats/Webp/IWebpDecoderOptions.cs | 23 ---- .../Formats/Webp/WebpAnimationDecoder.cs | 39 ++++--- src/ImageSharp/Formats/Webp/WebpDecoder.cs | 44 ++----- .../Formats/Webp/WebpDecoderCore.cs | 110 +++++++++--------- .../Formats/Webp/WebpDecoderOptions.cs | 14 +++ 12 files changed, 116 insertions(+), 163 deletions(-) delete mode 100644 src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 6b35e2614..6ed66db92 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats { /// @@ -8,6 +10,8 @@ namespace SixLabors.ImageSharp.Formats /// public sealed class DecoderOptions { + private uint maxFrames = uint.MaxValue; + /// /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. /// @@ -26,6 +30,6 @@ namespace SixLabors.ImageSharp.Formats /// /// Gets or sets the maximum number of image frames to decode, inclusive. /// - public int MaxFrames { get; set; } = int.MaxValue; + public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Min(Math.Max(value, 1), uint.MaxValue); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index c084f57ce..938a8f360 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The maximum number of frames to decode. Inclusive. /// - private readonly int maxFrames; + private readonly uint maxFrames; /// /// Whether to skip metadata during decode. @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Gif public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - int frameCount = 0; + uint frameCount = 0; Image image = null; ImageFrame previousFrame = null; try @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (nextFlag == GifConstants.ImageLabel) { - if (previousFrame != null && frameCount++ <= this.maxFrames) + if (previousFrame != null && frameCount++ > this.maxFrames) { break; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs index f8b3364bb..9619bbe7f 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Configuration options for decoding Png images. /// - public class PngDecoderOptions : ISpecializedDecoderOptions + public sealed class PngDecoderOptions : ISpecializedDecoderOptions { /// public DecoderOptions GeneralOptions { get; set; } = new(); diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs index f5c0fb128..7483618c4 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Configuration options for decoding Tga images. /// - public class TgaDecoderOptions : ISpecializedDecoderOptions + public sealed class TgaDecoderOptions : ISpecializedDecoderOptions { /// public DecoderOptions GeneralOptions { get; set; } = new(); diff --git a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs deleted file mode 100644 index d6d1bb8a4..000000000 --- a/src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 0bb01fcdc..1f644fa82 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -33,14 +33,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff 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 skipMetadata; /// /// The maximum number of frames to decode. Inclusive. /// - private readonly int maxFrames; + private readonly uint maxFrames; /// /// The stream to decode from. @@ -170,14 +170,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.byteOrder = reader.ByteOrder; this.isBigTiff = reader.IsBigTiff; - int frameCount = 0; + uint frameCount = 0; foreach (ExifProfile ifd in directories) { cancellationToken.ThrowIfCancellationRequested(); ImageFrame frame = this.DecodeFrame(ifd, cancellationToken); frames.Add(frame); - if (frameCount++ <= this.maxFrames) + if (frameCount++ > this.maxFrames) { break; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs index ac0195251..ec3b43797 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Configuration options for decoding Tiff images. /// - public class TiffDecoderOptions : ISpecializedDecoderOptions + public sealed class TiffDecoderOptions : ISpecializedDecoderOptions { /// public DecoderOptions GeneralOptions { get; set; } = new(); diff --git a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs deleted file mode 100644 index cf607ef69..000000000 --- a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -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 09653fd4c..8e6967311 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -33,6 +33,11 @@ namespace SixLabors.ImageSharp.Formats.Webp /// private readonly Configuration configuration; + /// + /// The maximum number of frames to decode. Inclusive. + /// + private readonly uint maxFrames; + /// /// The area to restore. /// @@ -48,29 +53,24 @@ namespace SixLabors.ImageSharp.Formats.Webp /// 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 @@ namespace SixLabors.ImageSharp.Formats.Webp this.webpMetadata = this.metadata.GetWebpMetadata(); this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount; + uint frameCount = 0; int remainingBytes = (int)completeDataSize; while (remainingBytes > 0) { @@ -110,7 +111,7 @@ namespace SixLabors.ImageSharp.Formats.Webp break; } - if (stream.Position == stream.Length || this.DecodingMode is FrameDecodingMode.First) + if (stream.Position == stream.Length || frameCount++ > this.maxFrames) { break; } @@ -224,14 +225,14 @@ namespace SixLabors.ImageSharp.Formats.Webp /// 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 @@ namespace SixLabors.ImageSharp.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Formats.Webp } /// - 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 2f6b593ee..64544c5e6 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,29 @@ 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 : ImageDecoder { - /// - /// 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 + public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Guard.NotNull(stream, nameof(stream)); + WebpDecoderCore decoder = new(options); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - using var decoder = new WebpDecoderCore(configuration, this); + Resize(options.GeneralOptions, image); - 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); - } + return image; } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); + /// + public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - return new WebpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); + return new WebpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 979ac5582..70ca6900b 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; @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Performs the webp decoding operation. /// - internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable + internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable { /// /// Reusable buffer. @@ -29,65 +28,68 @@ namespace SixLabors.ImageSharp.Formats.Webp 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(WebpDecoderOptions options) + { + this.Options = options; + this.configuration = options.GeneralOptions.Configuration; + this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.maxFrames = options.GeneralOptions.MaxFrames; + this.memoryAllocator = this.configuration.MemoryAllocator; + } + + /// + public WebpDecoderOptions 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 @@ namespace SixLabors.ImageSharp.Formats.Webp Image image = null; try { - this.Metadata = new ImageMetadata(); + this.metadata = new ImageMetadata(); this.currentStream = stream; uint fileSize = this.ReadImageHeader(); @@ -105,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Webp { 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 @@ namespace SixLabors.ImageSharp.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Formats.Webp /// 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 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The webp features. private void ParseOptionalChunks(WebpFeatures features) { - if (this.IgnoreMetadata || (features.ExifProfile == false && features.XmpMetaData == false)) + if (this.skipMetadata || (features.ExifProfile == false && features.XmpMetaData == false)) { return; } @@ -287,11 +289,11 @@ namespace SixLabors.ImageSharp.Formats.Webp { // 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 @@ namespace SixLabors.ImageSharp.Formats.Webp private void ReadExifProfile() { uint exifChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)exifChunkSize); } @@ -325,7 +327,7 @@ namespace SixLabors.ImageSharp.Formats.Webp } var profile = new ExifProfile(exifData); - this.Metadata.ExifProfile = profile; + this.metadata.ExifProfile = profile; } } @@ -335,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Webp private void ReadXmpProfile() { uint xmpChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)xmpChunkSize); } @@ -350,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.Webp } var profile = new XmpProfile(xmpData); - this.Metadata.XmpProfile = profile; + this.metadata.XmpProfile = profile; } } @@ -360,7 +362,7 @@ namespace SixLabors.ImageSharp.Formats.Webp private void ReadIccProfile() { uint iccpChunkSize = this.ReadChunkSize(); - if (this.IgnoreMetadata) + if (this.skipMetadata) { this.currentStream.Skip((int)iccpChunkSize); } @@ -376,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Webp var profile = new IccProfile(iccpData); if (profile.CheckIsValid()) { - this.Metadata.IccProfile = profile; + this.metadata.IccProfile = profile; } } } @@ -419,8 +421,8 @@ namespace SixLabors.ImageSharp.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Formats.Webp } /// - public void Dispose() => this.AlphaData?.Dispose(); + public void Dispose() => this.alphaData?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs new file mode 100644 index 000000000..e574b0c72 --- /dev/null +++ b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Webp +{ + /// + /// Configuration options for decoding Webp images. + /// + public sealed class WebpDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} From 22421dbfcb9fbf7165dd8f461ff96f2fb97b74ab Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Jun 2022 23:54:07 +1000 Subject: [PATCH 07/54] Limit to collection max --- src/ImageSharp/Formats/DecoderOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 6ed66db92..95b02a0ae 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats /// public sealed class DecoderOptions { - private uint maxFrames = uint.MaxValue; + private uint maxFrames = int.MaxValue; /// /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. @@ -30,6 +30,6 @@ namespace SixLabors.ImageSharp.Formats /// /// Gets or sets the maximum number of image frames to decode, inclusive. /// - public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Min(Math.Max(value, 1), uint.MaxValue); } + public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Min(Math.Max(value, 1), int.MaxValue); } } } From d53ad0efa04fcdc96cba11611ce0acff2d73cbd1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 17 Jul 2022 19:39:42 +1000 Subject: [PATCH 08/54] Refactor jpeg decoder and general load --- src/ImageSharp/Advanced/AotCompilerTools.cs | 24 +- .../Formats/Bmp/BmpDecoderOptions.cs | 2 +- src/ImageSharp/Formats/DecoderOptions.cs | 9 +- .../Formats/Gif/GifDecoderOptions.cs | 2 +- src/ImageSharp/Formats/IImageDecoder.cs | 12 +- src/ImageSharp/Formats/IImageDecoder2.cs | 37 -- src/ImageSharp/Formats/IImageInfoDetector.cs | 7 +- src/ImageSharp/Formats/IImageInfoDetector2.cs | 24 - .../Formats/ISpecializedDecoderOptions.cs | 2 +- src/ImageSharp/Formats/ImageDecoder{T}.cs | 4 +- .../Formats/Jpeg/IJpegDecoderOptions.cs | 16 - src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 78 +--- .../Formats/Jpeg/JpegDecoderCore.cs | 90 ++-- .../Formats/Jpeg/JpegDecoderOptions.cs | 14 + .../Formats/Pbm/PbmDecoderOptions.cs | 2 +- .../Formats/Png/PngDecoderOptions.cs | 2 +- .../Formats/Tga/TgaDecoderOptions.cs | 2 +- .../Decompressors/JpegTiffCompression.cs | 20 +- .../Compression/TiffDecompressorsFactory.cs | 4 +- .../Formats/Tiff/TiffDecoderCore.cs | 4 +- .../Formats/Tiff/TiffDecoderOptions.cs | 2 +- .../Formats/Webp/WebpDecoderOptions.cs | 2 +- src/ImageSharp/Image.Decode.cs | 38 +- src/ImageSharp/Image.FromBytes.cs | 430 ++---------------- src/ImageSharp/Image.FromFile.cs | 295 +++--------- src/ImageSharp/Image.FromStream.cs | 333 ++++---------- src/ImageSharp/Image.LoadPixelData.cs | 68 +-- .../LoadResizeSaveStressRunner.cs | 35 +- 28 files changed, 350 insertions(+), 1208 deletions(-) delete mode 100644 src/ImageSharp/Formats/IImageDecoder2.cs delete mode 100644 src/ImageSharp/Formats/IImageInfoDetector2.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs create mode 100644 src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 7db2b7f96..5e8b899a4 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Advanced 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); } @@ -210,21 +210,21 @@ namespace SixLabors.ImageSharp.Advanced } /// - /// This method pre-seeds the all in the AoT compiler. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. [Preserve] 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 @@ namespace SixLabors.ImageSharp.Advanced 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/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index 535f819d2..b01d17980 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp { diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 95b02a0ae..5e03efb6e 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. using System; @@ -10,8 +10,15 @@ namespace SixLabors.ImageSharp.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. + /// + public static DecoderOptions Default { get; } = LazyOptions.Value; + /// /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs index 429c1fee1..3c5f7bddc 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Gif { diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 82c7804d5..cdec1e928 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -16,22 +16,22 @@ namespace SixLabors.ImageSharp.Formats /// Decodes the image from the specified stream to an of a specific pixel type. /// /// 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. + /// 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/IImageDecoder2.cs b/src/ImageSharp/Formats/IImageDecoder2.cs deleted file mode 100644 index 76d1a754d..000000000 --- a/src/ImageSharp/Formats/IImageDecoder2.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// Encapsulates properties and methods required for decoding an image from a stream. - /// - public interface IImageDecoder2 - { - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The pixel format. - /// The general decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// 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 general decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// Thrown if the encoded image contains errors. - Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); - } -} diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 2d30af5ce..443e8b980 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -14,10 +14,11 @@ namespace SixLabors.ImageSharp.Formats /// /// Reads the raw image information from the specified stream. /// - /// The configuration for the image. + /// 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/IImageInfoDetector2.cs b/src/ImageSharp/Formats/IImageInfoDetector2.cs deleted file mode 100644 index 3d340f06e..000000000 --- a/src/ImageSharp/Formats/IImageInfoDetector2.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using System.Threading; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// Encapsulates methods used for detecting the raw image information without fully decoding it. - /// - public interface IImageInfoDetector2 - { - /// - /// Reads the raw image information from the specified stream. - /// - /// The general decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The object. - /// Thrown if the encoded image contains errors. - IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); - } -} diff --git a/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs index b79938f08..eacb90183 100644 --- a/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs +++ b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats { diff --git a/src/ImageSharp/Formats/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs index 96080b3be..3a7559579 100644 --- a/src/ImageSharp/Formats/ImageDecoder{T}.cs +++ b/src/ImageSharp/Formats/ImageDecoder{T}.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. using System.IO; using System.Threading; @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats /// The base class for all image decoders. /// /// The type of specialized decoder options. - public abstract class ImageDecoder : IImageInfoDetector2, IImageDecoder2 + public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder where T : ISpecializedDecoderOptions, new() { /// diff --git a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs deleted file mode 100644 index 76874b7ff..000000000 --- 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 7f23d1ac8..1675fd6d7 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -3,77 +3,45 @@ 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 : ImageDecoder { /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + /// Unlike , when + /// is passed, the codec may not be able to scale efficiently to + /// the exact scale factor requested, so returns a size that approximates that scale. + /// Upscaling is not supported, so the original size will be returned. + /// + public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Guard.NotNull(stream, nameof(stream)); - - using var decoder = new JpegDecoderCore(configuration, this); - return decoder.Decode(configuration, stream, cancellationToken); + using JpegDecoderCore decoder = new(options); + return decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); } - /// - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - => this.Decode(configuration, stream, cancellationToken); - - /// - /// Placeholder summary. - /// - /// Placeholder2 - /// Placeholder3 - /// Placeholder4 - /// Placeholder5 - /// Placeholder6 - internal Image DecodeInto(Configuration configuration, Stream stream, Size targetSize, CancellationToken cancellationToken) - => this.DecodeInto(configuration, stream, targetSize, 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 - { - 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) - { - throw new InvalidImageContentException(((IImageDecoderInternals)decoder).Dimensions, ex); - } - } + /// + /// + /// Unlike , when + /// is passed, the codec may not be able to scale efficiently to + /// the exact scale factor requested, so returns a size that approximates that scale. + /// Upscaling is not supported, so the original size will be returned. + /// + public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); /// - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); - using var decoder = new JpegDecoderCore(configuration, this); - return decoder.Identify(configuration, stream, cancellationToken); + using JpegDecoderCore decoder = new(options); + return decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index d5997e412..4be75f2a1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals + internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals { /// /// The only supported precision @@ -116,31 +116,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg 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 + /// Initializes a new instance of the class. /// - public JpegFrame Frame { get; private set; } + /// The decoder options. + public JpegDecoderCore(JpegDecoderOptions options) + { + this.Options = options; + this.configuration = options.GeneralOptions.Configuration; + this.skipMetadata = options.GeneralOptions.SkipMetadata; + } + + /// + public JpegDecoderOptions 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 +206,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// 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.Options.GeneralOptions.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 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } 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); @@ -809,7 +799,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { 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); @@ -847,7 +837,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg 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); @@ -889,7 +879,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { // 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; @@ -930,7 +920,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// 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; @@ -1310,8 +1300,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } 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; @@ -1348,7 +1338,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } 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 000000000..80e680cd0 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs @@ -0,0 +1,14 @@ +// 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 + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + } +} diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs index c0b885650..1253d72b3 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Pbm { diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs index 9619bbe7f..722d80325 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Png { diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs index 7483618c4..89ec90430 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Tga { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs index d0543917a..50698d64d 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// 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,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors TiffPhotometricInterpretation photometricInterpretation) : base(memoryAllocator, width, bitsPerPixel) { - this.configuration = configuration; + this.options = options; this.jpegTables = jpegTables; this.photometricInterpretation = photometricInterpretation; } @@ -52,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 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); + using SpectralConverter spectralConverterGray = new GrayJpegSpectralConverter(configuration); var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None); jpegDecoder.LoadTables(this.jpegTables, scanDecoderGray); jpegDecoder.ParseStream(stream, spectralConverterGray, CancellationToken.None); @@ -73,8 +73,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors case TiffPhotometricInterpretation.YCbCr: case TiffPhotometricInterpretation.Rgb: { - using SpectralConverter spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ? - new RgbJpegSpectralConverter(this.configuration) : new SpectralConverter(this.configuration); + using SpectralConverter spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr + ? new RgbJpegSpectralConverter(this.options.GeneralOptions.Configuration) + : new SpectralConverter(this.options.GeneralOptions.Configuration); + var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); jpegDecoder.LoadTables(this.jpegTables, scanDecoder); jpegDecoder.ParseStream(stream, spectralConverter, CancellationToken.None); diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 5d6793660..086db4b0a 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,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression 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); default: throw TiffThrowHelper.NotSupportedDecompressor(nameof(method)); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 06a950e73..cec199736 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.configuration, + this.Options.GeneralOptions, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -453,7 +453,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.configuration, + this.Options.GeneralOptions, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs index ec3b43797..b22cf001e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Tiff { diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs index e574b0c72..8e8218472 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Webp { diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 3fcd44d3a..efa337d6d 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -44,14 +44,14 @@ namespace SixLabors.ImageSharp /// /// 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 @@ namespace SixLabors.ImageSharp // 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 @@ namespace SixLabors.ImageSharp /// /// 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 342143bb5..0d7fb27f4 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -9,232 +9,30 @@ using SixLabors.ImageSharp.PixelFormats; 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. - /// - /// The byte array containing encoded image data to read the header from. - /// The data is null. - /// The format or null if none found. - public static IImageFormat DetectFormat(byte[] data) - => DetectFormat(Configuration.Default, data); - - /// - /// By reading the header on the provided byte array 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 mime type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, byte[] data) - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return DetectFormat(configuration, stream); - } - } - - /// - /// 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 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 _); - - /// - /// 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 format type of the decoded image. - /// The data is null. - /// The data is not readable. - /// - /// The or null if suitable info detector not found. - /// - public static IImageInfo Identify(byte[] data, out IImageFormat format) => Identify(Configuration.Default, data, out format); - - /// - /// Reads the raw image information from the specified stream without fully decoding it. - /// - /// The configuration. - /// The byte array containing encoded image data to read the header from. - /// The format type of the decoded image. - /// The configuration is null. - /// The data is null. - /// The data is not readable. - /// - /// The or null if suitable info detector is not found. - /// - public static IImageInfo Identify(Configuration configuration, byte[] data, out IImageFormat format) - { - Guard.NotNull(data, nameof(data)); - - using (var stream = new MemoryStream(data, 0, data.Length, false, true)) - { - return Identify(configuration, 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); + public static IImageFormat DetectFormat(ReadOnlySpan data) + => DetectFormat(DecoderOptions.Default, data); /// /// By reading the header on the provided byte span this calculates the images format. /// - /// The configuration. + /// The general decoder options. /// The byte span containing encoded image data to read the header from. - /// The configuration is null. + /// The options are null. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpan data) + public static IImageFormat DetectFormat(DecoderOptions options, ReadOnlySpan data) { - Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options.Configuration)); + Configuration configuration = options.Configuration; int maxHeaderSize = configuration.MaxHeaderSize; if (maxHeaderSize <= 0) { @@ -265,7 +63,7 @@ namespace SixLabors.ImageSharp /// 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 +77,51 @@ namespace SixLabors.ImageSharp /// 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. + /// The options are null. /// 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. - /// 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()) { - 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)) - { - return Load(configuration, stream, out format); + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Load(options, stream, out format); } } @@ -462,21 +134,7 @@ namespace SixLabors.ImageSharp /// 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 +147,37 @@ namespace SixLabors.ImageSharp /// 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()) { - 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 79c036d24..6594ed218 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -21,23 +21,21 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// /// 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 @@ namespace SixLabors.ImageSharp /// /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 @@ namespace SixLabors.ImageSharp /// 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 803d8a754..86baa1c1a 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -26,19 +26,19 @@ namespace SixLabors.ImageSharp /// 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 +49,23 @@ namespace SixLabors.ImageSharp /// 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 +94,7 @@ namespace SixLabors.ImageSharp /// 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 +108,30 @@ namespace SixLabors.ImageSharp /// 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 +140,30 @@ namespace SixLabors.ImageSharp /// 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 +174,7 @@ namespace SixLabors.ImageSharp /// /// 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 +185,15 @@ namespace SixLabors.ImageSharp 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 +202,13 @@ namespace SixLabors.ImageSharp /// 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 +223,7 @@ namespace SixLabors.ImageSharp /// 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 +237,7 @@ namespace SixLabors.ImageSharp /// 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 +249,7 @@ namespace SixLabors.ImageSharp /// 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 +263,37 @@ namespace SixLabors.ImageSharp /// Image contains invalid content. /// A representing the asynchronous operation. public static Task LoadAsync(Stream stream, CancellationToken cancellationToken = default) - => LoadAsync(Configuration.Default, stream, cancellationToken); + => LoadAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Decode a new instance of the class from the given stream. - /// The pixel format is selected by the decoder. /// + /// The general decoder options. /// The stream containing image information. - /// The decoder. + /// The options are 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. - /// 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)); - } + public static Image Load(DecoderOptions options, Stream stream) + => Load(options, stream, out _); /// /// 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 general decoder options. /// The stream containing image information. - /// The decoder. /// The token to monitor for cancellation requests. - /// The configuration is null. + /// The options are 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) + public static async Task LoadAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) { - Guard.NotNull(decoder, nameof(decoder)); - return WithSeekableStreamAsync( - configuration, - stream, - (s, ct) => decoder.Decode(configuration, s, ct), - cancellationToken); - } - - /// - /// Decode a new instance of the class from the given stream. - /// - /// The configuration for the decoder. - /// The stream containing image information. - /// 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. - /// A new . - public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _); - - /// - /// Decode a new instance of the class from the given stream. - /// - /// The configuration for the decoder. - /// The stream containing image information. - /// 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. - /// A representing the asynchronous operation. - public static async Task LoadAsync(Configuration configuration, 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 +310,7 @@ namespace SixLabors.ImageSharp /// 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 +325,7 @@ namespace SixLabors.ImageSharp /// 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 +340,7 @@ namespace SixLabors.ImageSharp /// 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,119 +355,41 @@ namespace SixLabors.ImageSharp /// 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); + => LoadWithFormatAsync(DecoderOptions.Default, stream, cancellationToken); /// /// Create a new instance of the class from the given stream. /// + /// The general decoder options. /// The stream containing image information. - /// The decoder. + /// 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(Stream stream, IImageDecoder decoder) + public static Image Load(DecoderOptions options, Stream stream) where TPixel : unmanaged, IPixel - => WithSeekableStream(Configuration.Default, stream, s => decoder.Decode(Configuration.Default, s, default)); + => Load(options, stream, out IImageFormat _); /// /// 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); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The configuration options. - /// The stream containing image information. - /// 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) - where TPixel : unmanaged, IPixel - => Load(configuration, 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; @@ -558,7 +401,7 @@ namespace SixLabors.ImageSharp var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders) { sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); } @@ -569,24 +412,24 @@ namespace SixLabors.ImageSharp /// /// 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, + options, stream, - (s, ct) => Decode(s, configuration, ct), + (s, ct) => Decode(options, s, ct), cancellationToken) .ConfigureAwait(false); @@ -598,7 +441,7 @@ namespace SixLabors.ImageSharp var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders) { sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); } @@ -609,10 +452,10 @@ namespace SixLabors.ImageSharp /// /// 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,16 +463,16 @@ namespace SixLabors.ImageSharp /// 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, + options, stream, - (s, ct) => Decode(s, configuration, ct), + (s, ct) => Decode(options, s, ct), cancellationToken) .ConfigureAwait(false); @@ -641,7 +484,7 @@ namespace SixLabors.ImageSharp var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders) { sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); } @@ -652,10 +495,10 @@ namespace SixLabors.ImageSharp /// /// 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,12 +506,12 @@ namespace SixLabors.ImageSharp /// 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) + (Image img, _) = await LoadWithFormatAsync(options, stream, cancellationToken) .ConfigureAwait(false); return img; } @@ -677,30 +520,30 @@ namespace SixLabors.ImageSharp /// 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)); + (Image img, IImageFormat fmt) = WithSeekableStream(options, stream, s => Decode(options, s)); - format = data.Format; + format = fmt; - if (data.Img != null) + if (img != null) { - return data.Img; + return img; } var sb = new StringBuilder(); sb.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders) + foreach (KeyValuePair val in options.Configuration.ImageFormatsManager.ImageDecoders) { sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); } @@ -712,16 +555,16 @@ namespace SixLabors.ImageSharp /// 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, + 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 +572,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException("Cannot read from the stream."); } + Configuration configuration = options.Configuration; if (stream.CanSeek) { if (configuration.ReadOrigin == ReadOrigin.Begin) @@ -751,18 +595,18 @@ namespace SixLabors.ImageSharp /// 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, + 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 +614,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException("Cannot read from the stream."); } + Configuration configuration = options.Configuration; if (stream.CanSeek) { if (configuration.ReadOrigin == ReadOrigin.Begin) diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs index 93563c794..a59f5a34c 100644 --- a/src/ImageSharp/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image.LoadPixelData.cs @@ -16,20 +16,7 @@ namespace SixLabors.ImageSharp /// /// 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 @@ namespace SixLabors.ImageSharp => 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 @@ namespace SixLabors.ImageSharp => 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 @@ namespace SixLabors.ImageSharp /// 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/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index c26de9159..bee55600f 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -13,6 +13,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using ImageMagick; using PhotoSauce.MagicScaler; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests; @@ -157,7 +158,7 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave 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)); @@ -208,18 +209,15 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave 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, default); + using ImageSharpImage image = decoder.Decode(options, inputStream, default); 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; @@ -235,18 +233,19 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave 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); From 1c2d1f42e2c46e2d3d1f025e93db9ea91872c0f1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 01:14:04 +1000 Subject: [PATCH 09/54] Update tests and fix issues --- .../Formats/Bmp/BmpDecoderOptions.cs | 4 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 2 +- src/ImageSharp/Formats/ImageDecoder{T}.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Formats/Webp/WebpAnimationDecoder.cs | 2 +- src/ImageSharp/Image.FromBytes.cs | 45 +++ .../Codecs/Jpeg/DecodeJpeg.cs | 3 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 4 +- .../Codecs/Jpeg/DecodeJpeg_Aggregate.cs | 21 +- .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 23 +- .../Codecs/Jpeg/IdentifyJpeg.cs | 3 +- .../Codecs/Tga/DecodeTga.cs | 5 +- .../Codecs/Webp/DecodeWebp.cs | 4 +- .../Formats/Bmp/BmpDecoderTests.cs | 344 +++++++----------- .../Formats/Bmp/BmpEncoderTests.cs | 194 +++++----- .../Formats/GeneralFormatTests.cs | 136 +++---- .../Formats/Gif/GifDecoderTests.cs | 81 ++--- .../Formats/Gif/GifMetadataTests.cs | 125 +++---- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 128 +++---- .../Formats/Jpg/JpegDecoderTests.cs | 98 ++--- .../Formats/Jpg/SpectralJpegTests.cs | 6 +- .../Jpg/SpectralToPixelConversionTests.cs | 31 +- .../Formats/Jpg/Utils/JpegFixture.cs | 11 +- .../Formats/Png/PngDecoderTests.Chunks.cs | 10 +- .../Formats/Png/PngEncoderTests.cs | 226 +++++------- .../Formats/Png/PngMetadataTests.cs | 282 +++++++------- .../Formats/Png/PngSmokeTests.cs | 45 +-- .../Formats/Tga/TgaFileHeaderTests.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 8 +- .../Formats/Tiff/TiffMetadataTests.cs | 202 +++++----- .../Formats/WebP/PredictorEncoderTests.cs | 4 +- .../Formats/WebP/WebpDecoderTests.cs | 241 +++++------- .../Formats/WebP/WebpMetaDataTests.cs | 61 ++-- .../Image/ImageTests.Decode_Cancellation.cs | 91 +++-- .../Image/ImageTests.DetectFormat.cs | 60 ++- .../Image/ImageTests.Identify.cs | 105 ++++-- .../Image/ImageTests.ImageLoadTestBase.cs | 29 +- ...d_FileSystemPath_PassLocalConfiguration.cs | 67 ++-- ..._FileSystemPath_UseDefaultConfiguration.cs | 66 +--- ...s.Load_FromBytes_PassLocalConfiguration.cs | 90 ++--- ...s.Load_FromBytes_UseGlobalConfiguration.cs | 83 +---- ....Load_FromStream_PassLocalConfiguration.cs | 63 ++-- ...ts.Load_FromStream_ThrowsRightException.cs | 17 +- ...Load_FromStream_UseDefaultConfiguration.cs | 91 +---- .../Image/LargeImageIntegrationTests.cs | 10 +- .../Profiles/Exif/ExifProfileTests.cs | 84 ++--- .../Profiles/IPTC/IptcProfileTests.cs | 30 +- .../Metadata/Profiles/XMP/XmpProfileTests.cs | 10 +- .../JpegProfilingBenchmarks.cs | 19 +- .../LoadResizeSaveProfilingBenchmarks.cs | 32 +- tests/ImageSharp.Tests/TestFile.cs | 56 ++- tests/ImageSharp.Tests/TestFormat.cs | 36 +- .../ImageProviders/FileProvider.cs | 77 +++- .../ImageProviders/TestImageProvider.cs | 53 +-- .../ReferenceCodecs/MagickReferenceDecoder.cs | 77 ++-- .../SystemDrawingReferenceDecoder.cs | 67 ++-- .../TestUtilities/TestEnvironment.Formats.cs | 2 +- .../TestUtilities/TestImageExtensions.cs | 66 ++-- .../Tests/MagickReferenceCodecTests.cs | 51 +-- .../Tests/SystemDrawingReferenceCodecTests.cs | 86 ++--- .../Tests/TestImageProviderTests.cs | 109 +++--- 61 files changed, 1826 insertions(+), 2156 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index b01d17980..1f5ce08d1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp public DecoderOptions GeneralOptions { get; set; } = new(); /// - /// Gets the value indicating how to deal with skipped pixels, + /// 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; } + public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index f9098538b..ff4b97b3c 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (nextFlag == GifConstants.ImageLabel) { - if (previousFrame != null && frameCount++ > this.maxFrames) + if (previousFrame != null && ++frameCount == this.maxFrames) { break; } diff --git a/src/ImageSharp/Formats/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs index 3a7559579..6f43b053f 100644 --- a/src/ImageSharp/Formats/ImageDecoder{T}.cs +++ b/src/ImageSharp/Formats/ImageDecoder{T}.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats /// /// The type of specialized decoder options. public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder - where T : ISpecializedDecoderOptions, new() + where T : class, ISpecializedDecoderOptions, new() { /// public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index cec199736..f2e5fc7de 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -177,7 +177,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff ImageFrame frame = this.DecodeFrame(ifd, cancellationToken); frames.Add(frame); - if (frameCount++ > this.maxFrames) + if (++frameCount == this.maxFrames) { break; } diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index e2083efe7..57c01cac7 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Formats.Webp break; } - if (stream.Position == stream.Length || frameCount++ > this.maxFrames) + if (stream.Position == stream.Length || ++frameCount == this.maxFrames) { break; } diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 0d7fb27f4..9635ec76e 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -52,6 +52,51 @@ namespace SixLabors.ImageSharp return default; } + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// 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(ReadOnlySpan data) => Identify(data, out IImageFormat _); + + /// + /// 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 format type of the decoded image. + /// The data is null. + /// The data is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(ReadOnlySpan data, out IImageFormat format) + => Identify(DecoderOptions.Default, data, out format); + + /// + /// Reads the raw image information from the specified span of bytes without fully decoding it. + /// + /// 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. + /// The data is not readable. + /// + /// The or null if suitable info detector is not found. + /// + public static unsafe IImageInfo Identify(DecoderOptions options, ReadOnlySpan data, out IImageFormat format) + { + fixed (byte* ptr = &data.GetPinnableReference()) + { + using var stream = new UnmanagedMemoryStream(ptr, data.Length); + return Identify(options, stream, out format); + } + } + /// /// Load a new instance of from the given encoded byte span. /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 79daa7df4..1c8977f5e 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg 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, default); } [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 34aa11144..546051772 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -38,8 +38,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { 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 5b3fa68b8..d6d699c0c 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 2c0c10997..f704bef28 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [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 b9f1e72fc..d1e1f0e77 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; @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { using var memoryStream = new MemoryStream(this.jpegBytes); var decoder = new JpegDecoder(); - return decoder.Identify(Configuration.Default, memoryStream, default); + 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 4a96a2b53..9e60fecac 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -7,7 +7,6 @@ using System.Threading; using BenchmarkDotNet.Attributes; using ImageMagick; using Pfim; -using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -18,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [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 55d2a8345..f49310b9c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs 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 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs 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.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 239e20976..8a5892e02 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } string providerDump = BasicSerializer.Serialize(provider); - RemoteExecutor.Invoke( - RunTest, - providerDump, - "Disco") - .Dispose(); + RemoteExecutor.Invoke(RunTest, providerDump, "Disco").Dispose(); } [Theory] @@ -98,11 +94,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void BmpDecoder_CanDecodeOversizedPalette(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [WithFile(InvalidPaletteSize, PixelTypes.Rgba32)] public void BmpDecoder_ThrowsInvalidImageContentException_OnInvalidPaletteSize(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void BmpDecoder_CanDecodeBmpv4(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void BmpDecoder_IsNotBoundToSinglePixelType(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void BmpDecoder_CanDecode4BytePerEntryPalette(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void Identify_DetectsCorrectPixelType(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) - { - IImageInfo imageInfo = Image.Identify(stream); - Assert.NotNull(imageInfo); - Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel); - } + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel); } [Theory] @@ -541,13 +483,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void Identify_DetectsCorrectWidthAndHeight(string imagePath, int expectedWidth, int expectedHeight) { 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); - } + 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); } [Theory] @@ -555,17 +495,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 BmpDecoder(); - 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 BmpDecoder(); + using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -573,13 +509,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp public void BmpDecoder_CanDecode_Os2v2Header(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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 dd59fb279..5dd712c1f 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -53,22 +53,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp return; } - using (Image 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp 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 e936eef65..d19f4862b 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -45,12 +45,10 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 @@ namespace SixLabors.ImageSharp.Tests.Formats 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 8fbac4a9e..7c0447a98 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -4,7 +4,7 @@ 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; @@ -35,11 +35,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public void Decode_VerifyAllFrames(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - image.DebugSaveMultiFrame(provider); - image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); - } + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } [Fact] @@ -51,13 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, default); + Assert.Equal((200, 200), (image.Width, image.Height)); } } @@ -66,11 +60,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 +72,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public void Decode_VerifyRootFrameAndFrameCount(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 +83,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public void CanDecodeJustOneFrame(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 +93,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 +105,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 +131,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 +173,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 +185,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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 6b6f4fbc2..23de14b63 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -58,51 +59,39 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif [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 +100,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, 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]); } [Theory] @@ -132,15 +117,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -148,17 +131,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -166,13 +145,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, default); + GifMetadata meta = image.Metadata.GetGifMetadata(); + Assert.Equal(repeatCount, meta.RepeatCount); } [Theory] @@ -180,15 +157,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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, default); + 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 74cda1f3a..9046ca14d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -80,17 +80,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -98,15 +94,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -114,13 +108,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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, default); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); } [Theory] @@ -128,14 +120,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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, default); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); } [Theory] @@ -150,12 +138,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void Identify_DetectsCorrectColorType(string imagePath, JpegColorType 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, default); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(expectedColorType, meta.ColorType); } [Theory] @@ -167,28 +153,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void Decode_DetectsCorrectColorType(TestImageProvider provider, JpegColorType 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 = ((IImageInfoDetector)decoder).Identify(DecoderOptions.Default, stream, default); + test(imageInfo); + } + else + { + using Image img = decoder.Decode(DecoderOptions.Default, stream, default); + test(img); } } @@ -248,23 +230,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [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); } } @@ -314,7 +294,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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); } @@ -357,11 +337,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [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 87179a0be..1706f2e21 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -6,6 +6,7 @@ using System.IO; 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; @@ -73,10 +74,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [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 @@ -148,17 +150,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 +176,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 +207,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void Issue1732_DecodesWithRgbColorSpace(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 +218,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 +229,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 +240,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 9824315ff..2ecfe331e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -49,8 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { // Calculating data from ImageSharp byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + JpegDecoderOptions option = new(); - using var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); + using var decoder = new JpegDecoderCore(option); using var ms = new MemoryStream(sourceBytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); @@ -78,8 +79,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // 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 af870ed44..f0e2d3ce1 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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 ef74549d0..ffb54fb0a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils // 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils // 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils // 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils 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/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index f17d8584c..16627dd34 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var decoder = new PngDecoder(); ImageFormatException exception = - Assert.Throws(() => decoder.Decode(Configuration.Default, memStream, default)); + Assert.Throws(() => decoder.Decode(DecoderOptions.Default, memStream, default)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } @@ -83,18 +84,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5593128ae..a35f8037c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -111,15 +111,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png return; } - foreach (var filterMethod in PngFilterMethods) + foreach (object[] filterMethod in PngFilterMethods) { foreach (PngInterlaceMode interlaceMode in InterlaceMode) { @@ -235,7 +233,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void InfersColorTypeAndBitDepth(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, default); - 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 0x0A // LF }; - Assert.Equal(expected, data); - } + Assert.Equal(expected, data); } [Theory] @@ -356,22 +350,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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,42 +567,42 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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); - // Compare to the Magick reference decoder. - 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, default); + + 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 1c89e72ac..fe74c0b8c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -5,6 +5,7 @@ using System; 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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, default); + PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); + VerifyTextDataIsPresent(meta); } [Theory] @@ -88,16 +83,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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, default); + 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -227,26 +207,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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, default); + ImageMetadata meta = image.Metadata; + Assert.Equal(xResolution, meta.HorizontalResolution); + Assert.Equal(yResolution, meta.VerticalResolution); + Assert.Equal(resolutionUnit, meta.ResolutionUnits); } [Theory] @@ -270,13 +242,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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 60d4be422..934b57b03 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,19 +20,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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()); - } - } + // image.Save(provider.Utility.GetTestOutputFileName("bmp")); + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms, default); + ImageComparer.Tolerant().VerifySimilarity(image, img2); + + // 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 @@ -104,20 +102,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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", "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")); + image.Mutate(x => x.Resize(100, 100)); + + // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms, default); + ImageComparer.Tolerant().VerifySimilarity(image, img2); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index eb1e42ad0..12e2926bd 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga 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/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 12e0b819d..eb719bcb1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,6 +4,7 @@ // ReSharper disable InconsistentNaming using System; using System.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -635,10 +636,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void CanDecodeJustOneFrame(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] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 38fac249c..c30a7b6c6 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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 f9b949e21..bbf35e51b 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp Assert.Equal(expectedData, transformData); } - // Test image: Input\Png\Bike.png private static void RunColorSpaceTransformTestWithBikeImage() { // arrange @@ -119,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp // 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 fe5520cc6..90587e36c 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -2,8 +2,8 @@ // Licensed under the Six Labors Split License. using System.IO; +using SixLabors.ImageSharp.Formats; 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 +44,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +65,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +79,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +100,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +113,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +130,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp public void WebpDecoder_CanDecode_Lossy_WithPartitions(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 +142,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +157,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +180,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +190,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp public void WebpDecoder_CanDecode_Lossless_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] @@ -221,11 +201,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp public void WebpDecoder_CanDecode_Lossless_WithoutTransforms(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 +218,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +232,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +243,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +254,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +276,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +293,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +303,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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(); - - image.DebugSaveMultiFrame(provider); - image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); - - Assert.Equal(0, webpMetaData.AnimationLoopCount); - Assert.Equal(150U, frameMetaData.FrameDuration); - Assert.Equal(12, image.Frames.Count); - } + 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); + + Assert.Equal(0, webpMetaData.AnimationLoopCount); + Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); } [Theory] @@ -356,18 +320,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp public void Decode_AnimatedLossy_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(); - - 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); - } + 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)); + + Assert.Equal(0, webpMetaData.AnimationLoopCount); + Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); } [Theory] @@ -375,10 +337,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp public void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFrame(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 +350,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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)) - { - image.DebugSave(provider); - } + using Image image = provider.GetImage(WebpDecoder); + image.DebugSave(provider); } // https://github.com/SixLabors/ImageSharp/issues/1594 @@ -401,11 +360,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 +381,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 05d0d6c5a..128853872 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp 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 c9be2a74e..d1848281f 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 @@ namespace SixLabors.ImageSharp.Tests 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 446d36c06..dab6000f3 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -29,26 +29,23 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 5a50ad96e..e7f5b52ac 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 134387ae6..1cfc744d0 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -63,28 +63,27 @@ namespace SixLabors.ImageSharp.Tests 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 8546b73a3..df68eda0f 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 0b0cf9f71..1ce8fd4eb 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 @@ namespace SixLabors.ImageSharp.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 aa3f38f62..d4e8adf11 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 @@ namespace SixLabors.ImageSharp.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 b9435f487..2b57f9aa1 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 @@ namespace SixLabors.ImageSharp.Tests { 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 c33a932c6..8931ab46c 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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 @@ namespace SixLabors.ImageSharp.Tests [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 b65d0071e..7ad660c68 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 @@ namespace SixLabors.ImageSharp.Tests [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 74799eb30..aacc0cee9 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 @@ namespace SixLabors.ImageSharp.Tests } 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests } } - [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 c5a94ec4d..6d4198c59 100644 --- a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs +++ b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs @@ -6,7 +6,6 @@ using System.IO; 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 @@ namespace SixLabors.ImageSharp.Tests 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 c4ad11cbf..a101e6e70 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif WebpLossy } - private static readonly Dictionary TestProfileValues = new Dictionary + private static readonly Dictionary TestProfileValues = new() { { ExifTag.Software, "Software" }, { ExifTag.Copyright, "Copyright" }, @@ -125,42 +125,40 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif [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 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif 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 c9972aa25..a91d12e56 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 JpegDecoder() { IgnoreMetadata = false }; + private static JpegDecoder JpegDecoder => new(); - private static TiffDecoder TiffDecoder => new TiffDecoder() { IgnoreMetadata = false }; + private static TiffDecoder TiffDecoder => new(); public static IEnumerable AllIptcTags() { @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // 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 @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // 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 @@ -164,8 +164,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // 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); @@ -185,8 +185,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // 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); @@ -222,8 +222,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC // 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); @@ -257,8 +257,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // 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 @@ -309,7 +309,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { // arrange var profile = new IptcProfile(); - var expectedValue = "another one"; + const string expectedValue = "another one"; profile.SetValue(tag, "test", false); // act @@ -330,7 +330,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC 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"); @@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC 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 5a901a5cd..8a40ad8ad 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/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs index e4f2bdd25..b82458725 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks { } - public static readonly TheoryData DecodeJpegData = new TheoryData + public static readonly TheoryData DecodeJpegData = new() { { TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, 20 }, { TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, 20 }, @@ -38,16 +38,17 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks [MemberData(nameof(DecodeJpegData))] public void DecodeJpeg(string fileName, int executionCount) { - var decoder = new JpegDecoder() + DecoderOptions options = new() { - IgnoreMetadata = true + SkipMetadata = true }; - this.DecodeJpegBenchmarkImpl(fileName, decoder, executionCount); + + this.DecodeJpegBenchmarkImpl(fileName, options, executionCount); } - private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder, int executionCount) + private void DecodeJpegBenchmarkImpl(string fileName, DecoderOptions options, int executionCount) { - // do not run this on CI even by accident + // Do not run this on CI even by accident if (TestEnvironment.RunsOnCI) { return; @@ -65,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks executionCount, () => { - var img = Image.Load(bytes, decoder); + var img = Image.Load(options, bytes); img.Dispose(); }, #pragma warning disable SA1515 // Single-line comment should be preceded by blank line @@ -123,8 +124,8 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks } }, #pragma warning disable SA1515 // Single-line comment should be preceded by blank line - // ReSharper disable once ExplicitCallerInfoArgument - $@"Encode {testFiles.Length} images"); + // ReSharper disable once ExplicitCallerInfoArgument + $"Encode {testFiles.Length} images"); #pragma warning restore SA1515 // Single-line comment should be preceded by blank line } diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs index 82425d50d..869530f01 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 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks 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 825f0263c..ed5c4a0f0 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 @@ namespace SixLabors.ImageSharp.Tests /// 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 @@ namespace SixLabors.ImageSharp.Tests /// /// 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 @@ namespace SixLabors.ImageSharp.Tests /// 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 @@ namespace SixLabors.ImageSharp.Tests /// 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 @@ namespace SixLabors.ImageSharp.Tests /// 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 @@ namespace SixLabors.ImageSharp.Tests /// 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 806018324..eecdb4bb9 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : IImageDecoder, IImageInfoDetector + public class TestDecoder : ImageDecoder { private readonly TestFormat testFormat; @@ -206,20 +206,21 @@ namespace SixLabors.ImageSharp.Tests public int HeaderSize => this.testFormat.HeaderSize; - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => this.DecodeImpl(configuration, stream); + public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - private Image DecodeImpl(Configuration config, Stream stream) - where TPixel : unmanaged, IPixel + public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { + 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 +228,13 @@ namespace SixLabors.ImageSharp.Tests 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 override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(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/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index cb19802ce..603a7b90f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; 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; @@ -90,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests return false; } - if (!object.Equals(kv.Value, otherVal)) + if (!Equals(kv.Value, otherVal)) { return false; } @@ -126,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests 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 +148,78 @@ namespace SixLabors.ImageSharp.Tests 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)); + 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(ImageDecoder 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); + Image cachedImage = Cache.GetOrAdd(key, _ => this.DecodeImage(decoder, options)); + + return cachedImage.Clone(this.Configuration); + } + + public override Task> GetImageAsync(ImageDecoder 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.DecodeSpecialized(options, stream, default)); } public override void Deserialize(IXunitSerializationInfo info) @@ -193,10 +235,23 @@ namespace SixLabors.ImageSharp.Tests 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(ImageDecoder 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.DecodeSpecialized(options, stream, default); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index d6d867ab7..8d186bdab 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -53,19 +53,17 @@ namespace SixLabors.ImageSharp.Tests => 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests /// 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(ImageDecoder decoder, T options) + where T : class, ISpecializedDecoderOptions, new() + => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); + + public virtual Task> GetImageAsync(ImageDecoder decoder, T options) + 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 @@ namespace SixLabors.ImageSharp.Tests 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 011d89e9f..ded1adee3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class MagickReferenceDecoder : IImageDecoder + public class MagickReferenceDecoder : ImageDecoder { private readonly bool validate; @@ -28,39 +28,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public static MagickReferenceDecoder Instance { get; } = new MagickReferenceDecoder(); - 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) - where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel + public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Configuration configuration = options.GeneralOptions.Configuration; var bmpReadDefines = new BmpReadDefines { IgnoreFileSize = !this.validate @@ -100,6 +70,45 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new Image(configuration, new ImageMetadata(), framesList); } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + public override IImageInfo IdentifySpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(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); + } + } + } + + public class MagickReferenceDecoderOptions : ISpecializedDecoderOptions + { + public DecoderOptions GeneralOptions { get; set; } = new(); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 743a62797..21f0fd865 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -6,52 +6,53 @@ using System.Threading; 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 : ImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override IImageInfo IdentifySpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using (var sourceBitmap = new System.Drawing.Bitmap(stream)) - { - 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); - } - } + using var sourceBitmap = new SDBitmap(stream); + PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); + return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } - public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) + public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + using var sourceBitmap = new SDBitmap(stream); + if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) + { + return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sourceBitmap); + } + + 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 override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + } + + public class SystemDrawingReferenceDecoderOptions : ISpecializedDecoderOptions + { + public DecoderOptions GeneralOptions { get; set; } = new(); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 97fc00514..2b7316f0c 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; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 30715161b..3081edfa7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -327,7 +327,8 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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); } /// @@ -682,11 +682,11 @@ namespace SixLabors.ImageSharp.Tests 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); } internal static AllocatorBufferCapacityConfigurator LimitAllocatorBufferCapacity( diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index 81ec462f1..f97d1341f 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests ImageComparer comparer = ImageComparer.Exact; - using (var mImage = Image.Load(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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests // 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 573dbd9b0..e7e215d70 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests 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 a272303fa..fed4c7587 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 @@ namespace SixLabors.ImageSharp.Tests /// A test image. public static Image CreateTestImage() where TPixel : unmanaged, IPixel => - new Image(3, 3); + new(3, 3); [Theory] [MemberData(nameof(BasicData))] @@ -179,16 +179,14 @@ namespace SixLabors.ImageSharp.Tests 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 +197,8 @@ namespace SixLabors.ImageSharp.Tests 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 +234,20 @@ namespace SixLabors.ImageSharp.Tests 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 +255,8 @@ namespace SixLabors.ImageSharp.Tests 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 +302,15 @@ namespace SixLabors.ImageSharp.Tests 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 +320,19 @@ namespace SixLabors.ImageSharp.Tests 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 : ImageDecoder { // 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 +344,18 @@ namespace SixLabors.ImageSharp.Tests } } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; return new Image(42, 42); } + public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -375,16 +363,13 @@ namespace SixLabors.ImageSharp.Tests 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 : ImageDecoder { - 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; @@ -400,13 +385,18 @@ namespace SixLabors.ImageSharp.Tests } } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; return new Image(42, 42); } + public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal void InitCaller(string name) @@ -414,8 +404,11 @@ namespace SixLabors.ImageSharp.Tests this.callerName = name; InvocationCounts[name] = 0; } + } - public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); + private class TestDecoderOptions : ISpecializedDecoderOptions + { + public DecoderOptions GeneralOptions { get; set; } = new(); } } } From 8d50996ce74b5b9fb252ad967c41359c87645ccc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 01:58:19 +1000 Subject: [PATCH 10/54] Fix cache key resolution --- .../Formats/Webp/WebpDecoderCore.cs | 2 +- .../ImageProviders/FileProvider.cs | 50 +++++++++++++++---- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 9cae89252..ecb7b1b87 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The webp features. private void ParseOptionalChunks(WebpFeatures features) { - if (this.skipMetadata || (features.ExifProfile == false && features.XmpMetaData == false)) + if (this.skipMetadata || (!features.ExifProfile && !features.XmpMetaData)) { return; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 603a7b90f..5bf28b3c8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -28,38 +28,68 @@ namespace SixLabors.ImageSharp.Tests 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)) + { + string key = $"{type.FullName}.{p.Name}"; + data[key] = p.GetValue(options); + } + + type = type.GetTypeInfo().BaseType; + } + } + public bool Equals(Key other) { if (other is null) @@ -165,7 +195,7 @@ namespace SixLabors.ImageSharp.Tests return this.DecodeImage(decoder, options); } - var key = new Key(this.PixelType, this.FilePath, 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); @@ -202,7 +232,7 @@ namespace SixLabors.ImageSharp.Tests return this.DecodeImage(decoder, options); } - var key = new Key(this.PixelType, this.FilePath, decoder); + 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); From 6dbc5ec70a396b0d8dc20d74077706112d27bd88 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 02:32:47 +1000 Subject: [PATCH 11/54] Fix param caching test --- .../ImageProviders/FileProvider.cs | 5 ++ .../Tests/TestImageProviderTests.cs | 65 ++++++++++++++----- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 5bf28b3c8..1fc0100a6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -82,6 +82,11 @@ namespace SixLabors.ImageSharp.Tests { 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); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index fed4c7587..d75ac21dc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -109,19 +109,31 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests 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)); }); } @@ -365,7 +389,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoderWithParameters : ImageDecoder + private class TestDecoderWithParameters : ImageDecoder { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -373,10 +397,6 @@ namespace SixLabors.ImageSharp.Tests private string callerName; - public string Param1 { get; set; } - - public int Param2 { get; set; } - public static void DoTestThreadSafe(Action action) { lock (Monitor) @@ -385,16 +405,16 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo IdentifySpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; @@ -410,5 +430,14 @@ namespace SixLabors.ImageSharp.Tests { public DecoderOptions GeneralOptions { get; set; } = new(); } + + private class TestDecoderWithParametersOptions : ISpecializedDecoderOptions + { + public string Param1 { get; set; } + + public int Param2 { get; set; } + + public DecoderOptions GeneralOptions { get; set; } = new(); + } } } From a42101e743238c993fafcab873739c37d024bcbb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 12:58:04 +1000 Subject: [PATCH 12/54] Migrate new Tiff WebPDecoder --- .../Decompressors/WebpTiffCompression.cs | 10 ++++++---- .../Tiff/Compression/TiffDecompressorsFactory.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.Internal.cs | 14 +------------- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index 0d63382ff..e918837c1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -16,22 +16,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) { - using var image = Image.Load(stream, new WebpDecoder()); + using Image image = new WebpDecoder().Decode(this.options, stream, default); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index 2b121f4de..213746d7d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression 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/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs index 6bf7ae88f..537545285 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs @@ -1,22 +1,10 @@ // Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. +// Licensed under the Six Labors Split License. using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; -using SixLabors.ImageSharp.Tests.TestUtilities; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; -using Xunit.Abstractions; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg From 204fddd8355f3dcbb75dbec613102d7582010dc0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 13:11:24 +1000 Subject: [PATCH 13/54] Pass cancellation token to decompressors. --- .../Decompressors/DeflateTiffCompression.cs | 3 ++- .../Decompressors/JpegTiffCompression.cs | 16 +++++++--------- .../Decompressors/LzwTiffCompression.cs | 3 ++- .../ModifiedHuffmanTiffCompression.cs | 3 ++- .../Decompressors/NoneTiffCompression.cs | 3 ++- .../Decompressors/PackBitsTiffCompression.cs | 3 ++- .../Decompressors/T4TiffCompression.cs | 3 ++- .../Decompressors/T6TiffCompression.cs | 3 ++- .../Decompressors/WebpTiffCompression.cs | 5 +++-- .../Tiff/Compression/TiffBaseDecompressor.cs | 9 ++++++--- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 6 ++++-- .../Compression/DeflateTiffCompressionTests.cs | 12 +++++------- .../Tiff/Compression/LzwTiffCompressionTests.cs | 6 +++--- .../Tiff/Compression/NoneTiffCompressionTests.cs | 2 +- .../Compression/PackBitsTiffCompressionTests.cs | 2 +- 15 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index 37debc9f6..ae89fce0e 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - 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 babcb4516..ef44263a5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - 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) { @@ -60,12 +60,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors case TiffPhotometricInterpretation.WhiteIsZero: { using SpectralConverter spectralConverterGray = new GrayJpegSpectralConverter(configuration); - var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None); + 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,12 +74,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { using SpectralConverter spectralConverter = new TiffJpegSpectralConverter(configuration, this.photometricInterpretation); - var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); + 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 57da76f6c..a53704abf 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - 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 16d3f125b..c95048b68 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors 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 291d40c85..6bec4e2ce 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - 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 9df331b6e..230f9f5c9 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - 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 2fed2b742..e8513c474 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors 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 2373e7b9b..1a105e96a 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors 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/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index e918837c1..d2461bfa2 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; @@ -31,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors => 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 Image image = new WebpDecoder().Decode(this.options, stream, default); + using Image image = 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 d828a7f4f..aaa3e9499 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// 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 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// 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/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index f2e5fc7de..6b3abbc9e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -410,7 +410,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, - stripBuffers[planeIndex].GetSpan()); + stripBuffers[planeIndex].GetSpan(), + cancellationToken); stripIndex += stripsPerPlane; } @@ -498,7 +499,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff stripOffsets[stripIndex], stripByteCounts[stripIndex], stripHeight, - stripBufferSpan); + stripBufferSpan, + cancellationToken); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index ac775b712..12f20cd30 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -23,16 +23,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression [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 b2bd316a4..197beade2 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression 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 33cac791e..ca457d15e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression 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 7f1452c41..4ed4330ec 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression 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); } From 1d9b20914f1157bf9455b060ad405196604b84b0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 13:36:33 +1000 Subject: [PATCH 14/54] Update TestImageExtensions.cs --- tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index e8d894ce9..a4956ffb7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -688,6 +688,8 @@ namespace SixLabors.ImageSharp.Tests ImageComparer comparer = customComparer ?? ImageComparer.Exact; comparer.VerifySimilarity(encodedImage, image); + + return actualOutputFile; } internal static AllocatorBufferCapacityConfigurator LimitAllocatorBufferCapacity( From 8826e87718d50b24976e128b73c1e148aac262bd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 21:45:58 +1000 Subject: [PATCH 15/54] Update src/ImageSharp/Formats/DecoderOptions.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Formats/DecoderOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 5e03efb6e..1fa28983d 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -37,6 +37,6 @@ namespace SixLabors.ImageSharp.Formats /// /// Gets or sets the maximum number of image frames to decode, inclusive. /// - public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Min(Math.Max(value, 1), int.MaxValue); } + public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } } } From 2355de686633c416a2600e6e94a65639433dc0aa Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 21:46:08 +1000 Subject: [PATCH 16/54] Update src/ImageSharp/Image.FromBytes.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Image.FromBytes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 9635ec76e..1e33fc897 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp /// public static unsafe IImageInfo Identify(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 Identify(options, stream, out format); From 01e497f5690d92764a62de3efb8963b1329992e7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 21:46:16 +1000 Subject: [PATCH 17/54] Update src/ImageSharp/Image.FromBytes.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Image.FromBytes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 1e33fc897..d4743eabf 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp 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(options, stream); From 91a641301b7216407e2b37cdf2fb4d5d12c6157a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 21:46:21 +1000 Subject: [PATCH 18/54] Update src/ImageSharp/Image.FromBytes.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Image.FromBytes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index d4743eabf..eace98f99 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp out IImageFormat format) where TPixel : unmanaged, IPixel { - fixed (byte* ptr = &data.GetPinnableReference()) + fixed (byte* ptr = data) { using var stream = new UnmanagedMemoryStream(ptr, data.Length); return Load(options, stream, out format); From 37d020cb2356b645886e87286ef968819600de83 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 21:46:27 +1000 Subject: [PATCH 19/54] Update src/ImageSharp/Image.FromBytes.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/ImageSharp/Image.FromBytes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index eace98f99..341961a2b 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp ReadOnlySpan data, out IImageFormat format) { - fixed (byte* ptr = &data.GetPinnableReference()) + fixed (byte* ptr = data) { using var stream = new UnmanagedMemoryStream(ptr, data.Length); return Load(options, stream, out format); From 085ff1ae465e67e27a80dae0601a56ae3149c66d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 18 Jul 2022 22:07:44 +1000 Subject: [PATCH 20/54] Normalize not loaded exception --- src/ImageSharp/Image.FromStream.cs | 90 +++++++++++------------------- 1 file changed, 33 insertions(+), 57 deletions(-) diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 86baa1c1a..f7ff9966d 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; @@ -393,20 +394,12 @@ namespace SixLabors.ImageSharp format = data.Format; - if (data.Image != null) + if (data.Image is null) { - return data.Image; + ThrowNotLoaded(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()); + return data.Image; } /// @@ -426,27 +419,16 @@ namespace SixLabors.ImageSharp Stream stream, CancellationToken cancellationToken = default) { - (Image Image, IImageFormat Format) data = await WithSeekableStreamAsync( - options, - stream, - (s, ct) => Decode(options, s, ct), - cancellationToken) - .ConfigureAwait(false); - - if (data.Image != null) - { - return data; - } + (Image Image, IImageFormat Format) data = + await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken) + .ConfigureAwait(false); - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); - - foreach (KeyValuePair val in options.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; } /// @@ -469,27 +451,15 @@ namespace SixLabors.ImageSharp where TPixel : unmanaged, IPixel { (Image Image, IImageFormat Format) data = - await WithSeekableStreamAsync( - options, - stream, - (s, ct) => Decode(options, s, ct), - cancellationToken) - .ConfigureAwait(false); - - if (data.Image != null) - { - return data; - } - - var sb = new StringBuilder(); - sb.AppendLine("Image cannot be loaded. Available decoders:"); + await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken) + .ConfigureAwait(false); - foreach (KeyValuePair val in options.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; } /// @@ -512,7 +482,7 @@ namespace SixLabors.ImageSharp where TPixel : unmanaged, IPixel { (Image img, _) = await LoadWithFormatAsync(options, stream, cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); return img; } @@ -535,20 +505,12 @@ namespace SixLabors.ImageSharp format = fmt; - if (img != null) + if (img is null) { - return img; + ThrowNotLoaded(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()); + return img; } /// @@ -633,5 +595,19 @@ namespace SixLabors.ImageSharp 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()); + } } } From 0472ffd3f177ca1350a756898035a93e8cedd482 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 30 Jul 2022 22:15:47 +1000 Subject: [PATCH 21/54] Make default DecoderOptions internal --- src/ImageSharp/Formats/DecoderOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 1fa28983d..a608fcd52 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats /// /// Gets the shared default general decoder options instance. /// - public static DecoderOptions Default { get; } = LazyOptions.Value; + internal static DecoderOptions Default { get; } = LazyOptions.Value; /// /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. From d29cf8abf48d7a6fd57ead59381473cf23263744 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 7 Aug 2022 21:12:06 +1000 Subject: [PATCH 22/54] Implement specialized options for limited types, add extensions. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 34 +++-- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 17 ++- src/ImageSharp/Formats/Gif/GifDecoder.cs | 30 +++-- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 12 +- .../Formats/Gif/GifDecoderOptions.cs | 14 --- src/ImageSharp/Formats/IImageDecoder.cs | 6 + ...ernals{T}.cs => IImageDecoderInternals.cs} | 10 +- .../Formats/IImageDecoderSpecialized{T}.cs | 39 ++++++ src/ImageSharp/Formats/IImageInfoDetector.cs | 3 + src/ImageSharp/Formats/ImageDecoder.cs | 65 ++++++++++ .../ImageDecoderSpecializedExtensions.cs | 82 ++++++++++++ .../Formats/ImageDecoderUtilities.cs | 17 ++- src/ImageSharp/Formats/ImageDecoder{T}.cs | 119 ------------------ src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 47 ++++--- .../Formats/Jpeg/JpegDecoderCore.cs | 14 ++- .../Formats/Jpeg/JpegDecoderOptions.cs | 5 + .../Formats/Jpeg/JpegDecoderResizeMode.cs | 27 ++++ src/ImageSharp/Formats/Pbm/PbmDecoder.cs | 32 ++--- src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs | 8 +- .../Formats/Pbm/PbmDecoderOptions.cs | 14 --- src/ImageSharp/Formats/Png/PngDecoder.cs | 63 +++++----- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 14 +-- .../Formats/Png/PngDecoderOptions.cs | 14 --- src/ImageSharp/Formats/Tga/TgaDecoder.cs | 30 +++-- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 8 +- .../Formats/Tga/TgaDecoderOptions.cs | 14 --- .../TiffJpegSpectralConverter{TPixel}.cs | 4 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 30 +++-- .../Formats/Tiff/TiffDecoderCore.cs | 16 +-- .../Formats/Tiff/TiffDecoderOptions.cs | 14 --- src/ImageSharp/Formats/Webp/WebpDecoder.cs | 31 +++-- .../Formats/Webp/WebpDecoderCore.cs | 12 +- .../Formats/Webp/WebpDecoderOptions.cs | 14 --- src/ImageSharp/Image.FromStream.cs | 4 +- .../Codecs/Jpeg/DecodeJpeg.cs | 1 - tests/ImageSharp.Tests/TestFormat.cs | 17 ++- .../ImageProviders/FileProvider.cs | 6 +- .../ImageProviders/TestImageProvider.cs | 4 +- .../ReferenceCodecs/MagickReferenceDecoder.cs | 19 ++- .../SystemDrawingReferenceDecoder.cs | 15 +-- .../Tests/TestImageProviderTests.cs | 34 +++-- 42 files changed, 524 insertions(+), 437 deletions(-) delete mode 100644 src/ImageSharp/Formats/Gif/GifDecoderOptions.cs rename src/ImageSharp/Formats/{IImageDecoderInternals{T}.cs => IImageDecoderInternals.cs} (88%) create mode 100644 src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs create mode 100644 src/ImageSharp/Formats/ImageDecoder.cs create mode 100644 src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs delete mode 100644 src/ImageSharp/Formats/ImageDecoder{T}.cs create mode 100644 src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs delete mode 100644 src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Png/PngDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 5e8b899a4..1732a6bdb 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Advanced } /// - /// This method pre-seeds the all in the AoT compiler. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. [Preserve] diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 347460682..5b0e803f3 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -10,31 +10,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Image decoder for generating an image out of a Windows bitmap stream. /// - public class BmpDecoder : ImageDecoder + public class BmpDecoder : ImageDecoder, IImageDecoderSpecialized { /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - BmpDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); - - return image; + return new BmpDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new BmpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = new BmpDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + Resize(options.GeneralOptions, image); + + return image; } + + /// + public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index d9e2c17b8..1adc34849 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// A useful decoding source example can be found at /// - internal sealed class BmpDecoderCore : IImageDecoderInternals + internal sealed class BmpDecoderCore : IImageDecoderInternals { /// /// The default mask for the red part of the color for 16 bit rgb bitmaps. @@ -99,19 +99,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private readonly MemoryAllocator memoryAllocator; + /// + /// How to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. + /// + private readonly RleSkippedPixelHandling rleSkippedPixelHandling; + /// /// Initializes a new instance of the class. /// /// The options. public BmpDecoderCore(BmpDecoderOptions options) { - this.Options = options; + this.Options = options.GeneralOptions; + this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public BmpDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); @@ -322,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp byte colorIdx = bufferRow[x]; if (undefinedPixelsSpan[rowStartIdx + x]) { - switch (this.Options.RleSkippedPixelHandling) + switch (this.rleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); @@ -394,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int idx = rowStartIdx + (x * 3); if (undefinedPixelsSpan[yMulWidth + x]) { - switch (this.Options.RleSkippedPixelHandling) + switch (this.rleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 5115486a4..3729e4208 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : ImageDecoder + public sealed class GifDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - GifDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new GifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new GifDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + GifDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index ff4b97b3c..b307ffd60 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Performs the gif decoding operation. /// - internal sealed class GifDecoderCore : IImageDecoderInternals + internal sealed class GifDecoderCore : IImageDecoderInternals { /// /// The temp buffer used to reduce allocations. @@ -90,17 +90,17 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Initializes a new instance of the class. /// /// The decoder options. - public GifDecoderCore(GifDecoderOptions options) + public GifDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public GifDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); diff --git a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs deleted file mode 100644 index 3c5f7bddc..000000000 --- a/src/ImageSharp/Formats/Gif/GifDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Gif -{ - /// - /// Configuration options for decoding Gif images. - /// - public sealed class GifDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index cdec1e928..b0363ccf9 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -15,6 +15,9 @@ namespace SixLabors.ImageSharp.Formats /// /// Decodes the image from the specified stream to an of a specific pixel type. /// + /// + /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use. + /// /// The pixel format. /// The general decoder options. /// The containing image data. @@ -27,6 +30,9 @@ namespace SixLabors.ImageSharp.Formats /// /// Decodes the image from the specified stream to an . /// + /// + /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use. + /// /// The general decoder options. /// The containing image data. /// The token to monitor for cancellation requests. diff --git a/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs similarity index 88% rename from src/ImageSharp/Formats/IImageDecoderInternals{T}.cs rename to src/ImageSharp/Formats/IImageDecoderInternals.cs index 27b8a434b..63596266f 100644 --- a/src/ImageSharp/Formats/IImageDecoderInternals{T}.cs +++ b/src/ImageSharp/Formats/IImageDecoderInternals.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; @@ -11,14 +11,12 @@ namespace SixLabors.ImageSharp.Formats /// /// Abstraction for shared internals for XXXDecoderCore implementations to be used with . /// - /// The type of specialized decoder options. - internal interface IImageDecoderInternals - where T : ISpecializedDecoderOptions + internal interface IImageDecoderInternals { /// - /// Gets the specialized decoder options. + /// Gets the general decoder options. /// - T Options { get; } + DecoderOptions Options { get; } /// /// Gets the dimensions of the image being decoded. diff --git a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs new file mode 100644 index 000000000..46ad284da --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all specialized image decoders. + /// + /// The type of specialized options. + public interface IImageDecoderSpecialized : IImageDecoder + where T : ISpecializedDecoderOptions + { + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs index 443e8b980..1bc8a4409 100644 --- a/src/ImageSharp/Formats/IImageInfoDetector.cs +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -14,6 +14,9 @@ namespace SixLabors.ImageSharp.Formats /// /// Reads the raw image information from the specified stream. /// + /// + /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use. + /// /// The general decoder options. /// The containing image data. /// The token to monitor for cancellation requests. diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs new file mode 100644 index 000000000..4863f5b67 --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all image decoders. + /// + public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder + { + /// + public abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + + /// + /// Performs a resize operation against the decoded image. If the target size is not set, or the image size + /// already matches the target size, the image is untouched. + /// + /// The decoder options. + /// The decoded image. + protected static void Resize(DecoderOptions options, Image image) + { + if (ShouldResize(options, image)) + { + ResizeOptions resizeOptions = new() + { + Size = options.TargetSize.Value, + Sampler = KnownResamplers.Box, + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(resizeOptions)); + } + } + + /// + /// Determines whether the decoded image should be resized. + /// + /// The decoder options. + /// The decoded image. + /// if the image should be resized, otherwise; . + private static bool ShouldResize(DecoderOptions options, Image image) + { + if (options.TargetSize is null) + { + return false; + } + + Size targetSize = options.TargetSize.Value; + Size currentSize = image.Size(); + return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; + } + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs b/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs new file mode 100644 index 000000000..d23f088aa --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Extensions methods for . + /// + public static class ImageDecoderSpecializedExtensions + { + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The type of specialized options. + /// The pixel format. + /// The decoder. + /// The specialized decoder options. + /// The containing image data. + /// The . + /// Thrown if the encoded image contains errors. + public static Image DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) + where T : ISpecializedDecoderOptions + where TPixel : unmanaged, IPixel + => decoder.DecodeSpecialized(options, stream, default); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The type of specialized options. + /// The decoder. + /// The specialized decoder options. + /// The containing image data. + /// The . + /// Thrown if the encoded image contains errors. + public static Image DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) + where T : ISpecializedDecoderOptions + => decoder.DecodeSpecialized(options, stream, default); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The type of specialized options. + /// The pixel format. + /// The decoder. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// A representing the asynchronous operation. + /// Thrown if the encoded image contains errors. + public static Task> DecodeSpecializedAsync(this IImageDecoderSpecialized decoder, T options, Stream stream, CancellationToken cancellationToken = default) + where T : ISpecializedDecoderOptions + where TPixel : unmanaged, IPixel + => Image.WithSeekableStreamAsync( + options.GeneralOptions, + stream, + (s, ct) => decoder.DecodeSpecialized(options, s, ct), + cancellationToken); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The type of specialized options. + /// The decoder. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// A representing the asynchronous operation. + /// Thrown if the encoded image contains errors. + public static Task DecodeSpecializedAsync(this IImageDecoderSpecialized decoder, T options, Stream stream, CancellationToken cancellationToken = default) + where T : ISpecializedDecoderOptions + => Image.WithSeekableStreamAsync( + options.GeneralOptions, + stream, + (s, ct) => decoder.DecodeSpecialized(options, s, ct), + cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index 1dc0841f2..b6cefd2c8 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -12,12 +12,11 @@ namespace SixLabors.ImageSharp.Formats { internal static class ImageDecoderUtilities { - public static IImageInfo Identify( - this IImageDecoderInternals decoder, + public static IImageInfo Identify( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions { using var bufferedReadStream = new BufferedReadStream(configuration, stream); @@ -31,22 +30,20 @@ namespace SixLabors.ImageSharp.Formats } } - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel - => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); + => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); - public static Image Decode( - this IImageDecoderInternals decoder, + public static Image Decode( + this IImageDecoderInternals decoder, Configuration configuration, Stream stream, Func largeImageExceptionFactory, CancellationToken cancellationToken) - where T : ISpecializedDecoderOptions where TPixel : unmanaged, IPixel { using var bufferedReadStream = new BufferedReadStream(configuration, stream); diff --git a/src/ImageSharp/Formats/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs deleted file mode 100644 index 6f43b053f..000000000 --- a/src/ImageSharp/Formats/ImageDecoder{T}.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// The base class for all image decoders. - /// - /// The type of specialized decoder options. - public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder - where T : class, ISpecializedDecoderOptions, new() - { - /// - public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - T specializedOptions = new() { GeneralOptions = options }; - return this.IdentifySpecialized(specializedOptions, stream, cancellationToken); - } - - /// - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - { - T specializedOptions = new() { GeneralOptions = options }; - Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); - - Resize(options, image); - - return image; - } - - /// - public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - T specializedOptions = new() { GeneralOptions = options }; - Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); - - Resize(options, image); - - return image; - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The object. - /// Thrown if the encoded image contains errors. - public abstract IImageInfo IdentifySpecialized(T options, Stream stream, CancellationToken cancellationToken); - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The pixel format. - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// Thrown if the encoded image contains errors. - public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// Thrown if the encoded image contains errors. - public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); - - /// - /// Performs a resize operation against the decoded image. If the target size is not set, or the image size - /// already matches the target size, the image is untouched. - /// - /// The decoder options. - /// The decoded image. - protected static void Resize(DecoderOptions options, Image image) - { - if (ShouldResize(options, image)) - { - ResizeOptions resizeOptions = new() - { - Size = options.TargetSize.Value, - Sampler = KnownResamplers.Box, - Mode = ResizeMode.Max - }; - - image.Mutate(x => x.Resize(resizeOptions)); - } - } - - /// - /// Determines whether the decoded image should be resized. - /// - /// The decoder options. - /// The decoded image. - /// if the image should be resized, otherwise; . - private static bool ShouldResize(DecoderOptions options, Image image) - { - if (options.TargetSize is null) - { - return false; - } - - Size targetSize = options.TargetSize.Value; - Size currentSize = image.Size(); - return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 1675fd6d7..a655acbce 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -10,38 +10,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Decoder for generating an image out of a jpeg encoded stream. /// - public sealed class JpegDecoder : ImageDecoder + public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized { /// - /// - /// Unlike , when - /// is passed, the codec may not be able to scale efficiently to - /// the exact scale factor requested, so returns a size that approximates that scale. - /// Upscaling is not supported, so the original size will be returned. - /// - public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - using JpegDecoderCore decoder = new(options); - return decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + Guard.NotNull(stream, nameof(stream)); + + using JpegDecoderCore decoder = new(new() { GeneralOptions = options }); + return decoder.Identify(options.Configuration, stream, cancellationToken); } /// - /// - /// Unlike , when - /// is passed, the codec may not be able to scale efficiently to - /// the exact scale factor requested, so returns a size that approximates that scale. - /// Upscaling is not supported, so the original size will be returned. - /// - public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override IImageInfo IdentifySpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); using JpegDecoderCore decoder = new(options); - return decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + + if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) + { + Resize(options.GeneralOptions, image); + } + + return image; } + + /// + public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 30af0b4e2..18201d49b 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals + internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals { /// /// The only supported precision @@ -125,19 +125,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private readonly bool skipMetadata; + /// + /// The jpeg specific resize options. + /// + private readonly JpegDecoderResizeMode resizeMode; + /// /// Initializes a new instance of the class. /// /// The decoder options. public JpegDecoderCore(JpegDecoderOptions options) { - this.Options = options; + this.Options = options.GeneralOptions; + this.resizeMode = options.ResizeMode; this.configuration = options.GeneralOptions.Configuration; this.skipMetadata = options.GeneralOptions.SkipMetadata; } /// - public JpegDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => this.Frame.PixelSize; @@ -207,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - using var spectralConverter = new SpectralConverter(this.configuration, this.Options.GeneralOptions.TargetSize); + using var spectralConverter = new SpectralConverter(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); this.ParseStream(stream, spectralConverter, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs index 80e680cd0..95d1c81d2 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public sealed class JpegDecoderOptions : ISpecializedDecoderOptions { + /// + /// Gets or sets the resize mode. + /// + public JpegDecoderResizeMode ResizeMode { get; set; } + /// public DecoderOptions GeneralOptions { get; set; } = new(); } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs new file mode 100644 index 000000000..d99d522c5 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Jpeg +{ + /// + /// Provides enumeration for resize modes taken during decoding. + /// Applicable only when has a value. + /// + public enum JpegDecoderResizeMode + { + /// + /// Both and . + /// + Combined, + + /// + /// IDCT-only to nearest block scale. + /// + IdctOnly, + + /// + /// Opt-out the IDCT part and only Resize. Can be useful in case of quality concerns. + /// + ScaleOnly + } +} diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs index 34dca4ca4..1127f8e09 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs @@ -26,29 +26,33 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// The specification of these images is found at . /// - public sealed class PbmDecoder : ImageDecoder + public sealed class PbmDecoder : ImageDecoder { - /// - public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) + /// + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - PbmDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new PbmDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new PbmDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + PbmDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index cac1a9c64..05f06deaa 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// Performs the PBM decoding operation. /// - internal sealed class PbmDecoderCore : IImageDecoderInternals + internal sealed class PbmDecoderCore : IImageDecoderInternals { private int maxPixelValue; @@ -52,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// Initializes a new instance of the class. /// /// The decoder options. - public PbmDecoderCore(PbmDecoderOptions options) + public PbmDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; } /// - public PbmDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => this.pixelSize; diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs deleted file mode 100644 index 1253d72b3..000000000 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Pbm -{ - /// - /// Configuration options for decoding Pbm images. - /// - public sealed class PbmDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 5771e5b09..9f23982d0 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -10,24 +10,39 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Decoder for generating an image out of a png encoded stream. /// - public sealed class PngDecoder : ImageDecoder + public sealed class PngDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new PngDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + PngDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options.GeneralOptions, image); + Resize(options, image); return image; } /// - public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + PngDecoderCore decoder = new(options, true); - IImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + IImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken); stream.Position = 0; PngMetadata meta = info.Metadata.GetPngMetadata(); @@ -39,50 +54,42 @@ namespace SixLabors.ImageSharp.Formats.Png if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.Palette: - return this.DecodeSpecialized(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.DecodeSpecialized(options, stream, cancellationToken) - : this.DecodeSpecialized(options, stream, cancellationToken); + ? this.Decode(options, stream, cancellationToken) + : this.Decode(options, stream, cancellationToken); default: - return this.DecodeSpecialized(options, stream, cancellationToken); + return this.Decode(options, stream, cancellationToken); } } - - /// - public override IImageInfo IdentifySpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken) - { - Guard.NotNull(stream, nameof(stream)); - - return new PngDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); - } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 8d6a1719e..0ec937a20 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Performs the png decoding operation. /// - internal sealed class PngDecoderCore : IImageDecoderInternals + internal sealed class PngDecoderCore : IImageDecoderInternals { /// /// Reusable buffer. @@ -123,25 +123,25 @@ namespace SixLabors.ImageSharp.Formats.Png /// Initializes a new instance of the class. /// /// The decoder options. - public PngDecoderCore(PngDecoderOptions options) + public PngDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; this.memoryAllocator = this.configuration.MemoryAllocator; } - internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) + internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly) { this.Options = options; this.colorMetadataOnly = colorMetadataOnly; this.skipMetadata = true; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public PngDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.header.Width, this.header.Height); diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs deleted file mode 100644 index 722d80325..000000000 --- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Png -{ - /// - /// Configuration options for decoding Png images. - /// - public sealed class PngDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index ca4d9a5f5..2ad845adb 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Image decoder for Truevision TGA images. /// - public sealed class TgaDecoder : ImageDecoder + public sealed class TgaDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - TgaDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new TgaDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new TgaDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + TgaDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 056aa9e84..830f2cc3d 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Performs the tga decoding operation. /// - internal sealed class TgaDecoderCore : IImageDecoderInternals + internal sealed class TgaDecoderCore : IImageDecoderInternals { /// /// A scratch buffer to reduce allocations. @@ -61,15 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Tga /// Initializes a new instance of the class. /// /// The options. - public TgaDecoderCore(TgaDecoderOptions options) + public TgaDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; + this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public TgaDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs deleted file mode 100644 index 89ec90430..000000000 --- a/src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Tga -{ - /// - /// Configuration options for decoding Tga images. - /// - public sealed class TgaDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs index ced6b9027..5d09bd790 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs @@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision); } - /// + /// /// This converter must be used only for RGB and YCbCr color spaces for performance reasons. /// For grayscale images must be used. - /// + /// private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation) => interpretation switch { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 68d27262f..012adc586 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : ImageDecoder + public class TiffDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - TiffDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + return new TiffDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new TiffDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + TiffDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 6b3abbc9e..8e3f8c16b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Performs the tiff decoding operation. /// - internal class TiffDecoderCore : IImageDecoderInternals + internal class TiffDecoderCore : IImageDecoderInternals { /// /// General configuration options. @@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Initializes a new instance of the class. /// /// The decoder options. - public TiffDecoderCore(TiffDecoderOptions options) + public TiffDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffPredictor Predictor { get; set; } /// - public TiffDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions { get; private set; } @@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Options.GeneralOptions, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, @@ -454,7 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( - this.Options.GeneralOptions, + this.Options, this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs deleted file mode 100644 index b22cf001e..000000000 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - /// - /// Configuration options for decoding Tiff images. - /// - public sealed class TiffDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index dc653705d..b2dbbcba9 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -10,29 +10,34 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Image decoder for generating an image out of a webp stream. /// - public sealed class WebpDecoder : ImageDecoder + public sealed class WebpDecoder : ImageDecoder { /// - public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - WebpDecoderCore decoder = new(options); - Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - - Resize(options.GeneralOptions, image); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); - return image; + using WebpDecoderCore decoder = new(options); + return decoder.Identify(options.Configuration, stream, cancellationToken); } /// - public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - - /// - public override IImageInfo IdentifySpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - return new WebpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken); + using WebpDecoderCore decoder = new(options); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + + Resize(options, image); + + return image; } + + /// + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index ecb7b1b87..75f5f88f8 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Performs the webp decoding operation. /// - internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable + internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable { /// /// Reusable buffer. @@ -76,17 +76,17 @@ namespace SixLabors.ImageSharp.Formats.Webp /// Initializes a new instance of the class. /// /// The decoder options. - public WebpDecoderCore(WebpDecoderOptions options) + public WebpDecoderCore(DecoderOptions options) { this.Options = options; - this.configuration = options.GeneralOptions.Configuration; - this.skipMetadata = options.GeneralOptions.SkipMetadata; - this.maxFrames = options.GeneralOptions.MaxFrames; + this.configuration = options.Configuration; + this.skipMetadata = options.SkipMetadata; + this.maxFrames = options.MaxFrames; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public WebpDecoderOptions Options { get; } + public DecoderOptions Options { get; } /// public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs deleted file mode 100644 index 8e8218472..000000000 --- a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp -{ - /// - /// Configuration options for decoding Webp images. - /// - public sealed class WebpDecoderOptions : ISpecializedDecoderOptions - { - /// - public DecoderOptions GeneralOptions { get; set; } = new(); - } -} diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index f7ff9966d..5c73590aa 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -521,7 +521,7 @@ namespace SixLabors.ImageSharp /// The input stream. /// The action to perform. /// The . - private static T WithSeekableStream( + internal static T WithSeekableStream( DecoderOptions options, Stream stream, Func action) @@ -562,7 +562,7 @@ namespace SixLabors.ImageSharp /// The action to perform. /// The cancellation token. /// The . - private static async Task WithSeekableStreamAsync( + internal static async Task WithSeekableStreamAsync( DecoderOptions options, Stream stream, Func action, diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 1c8977f5e..5df226770 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -65,7 +65,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } } - /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1348 (20H2/October2020Update) Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index eecdb4bb9..96fcb6afd 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : ImageDecoder + public class TestDecoder : ImageDecoder, IImageDecoderSpecialized { private readonly TestFormat testFormat; @@ -208,10 +208,17 @@ namespace SixLabors.ImageSharp.Tests public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { Configuration configuration = options.GeneralOptions.Configuration; var ms = new MemoryStream(); @@ -228,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests return this.testFormat.Sample(); } - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 1fc0100a6..6cc96dede 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests return Task.FromResult(decoder.Decode(options, stream, default)); } - public override Image GetImage(ImageDecoder decoder, T options) + public override Image GetImage(IImageDecoderSpecialized decoder, T options) { Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); @@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests return cachedImage.Clone(this.Configuration); } - public override Task> GetImageAsync(ImageDecoder decoder, T options) + public override Task> GetImageAsync(IImageDecoderSpecialized decoder, T options) { Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests return decoder.Decode(options, stream, default); } - private Image DecodeImage(ImageDecoder decoder, T options) + private Image DecodeImage(IImageDecoderSpecialized decoder, T options) where T : class, ISpecializedDecoderOptions, new() { options.GeneralOptions.Configuration = this.Configuration; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 8d186bdab..bb0e19649 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -101,11 +101,11 @@ namespace SixLabors.ImageSharp.Tests public virtual Task> GetImageAsync(IImageDecoder decoder, DecoderOptions options) => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); - public virtual Image GetImage(ImageDecoder decoder, T options) + public virtual Image GetImage(IImageDecoderSpecialized decoder, T options) where T : class, ISpecializedDecoderOptions, new() => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); - public virtual Task> GetImageAsync(ImageDecoder decoder, T options) + public virtual Task> GetImageAsync(IImageDecoderSpecialized decoder, T options) where T : class, ISpecializedDecoderOptions, new() => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 02abbbb43..92b7aeac9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class MagickReferenceDecoder : ImageDecoder + public class MagickReferenceDecoder : ImageDecoder { private readonly bool validate; @@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public static MagickReferenceDecoder Instance { get; } = new(); - public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { - Configuration configuration = options.GeneralOptions.Configuration; + Configuration configuration = options.Configuration; var bmpReadDefines = new BmpReadDefines { IgnoreFileSize = !this.validate @@ -70,11 +70,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new Image(configuration, new ImageMetadata(), framesList); } - public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); - public override IImageInfo IdentifySpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel @@ -106,9 +106,4 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } } } - - public class MagickReferenceDecoderOptions : ISpecializedDecoderOptions - { - public DecoderOptions GeneralOptions { get; set; } = new(); - } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 21f0fd865..ddf09ebdd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -11,18 +11,18 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class SystemDrawingReferenceDecoder : ImageDecoder + public class SystemDrawingReferenceDecoder : ImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public override IImageInfo IdentifySpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using var sourceBitmap = new SDBitmap(stream); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } - public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using var sourceBitmap = new SDBitmap(stream); if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) @@ -47,12 +47,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); } - public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); - } - - public class SystemDrawingReferenceDecoderOptions : ISpecializedDecoderOptions - { - public DecoderOptions GeneralOptions { get; set; } = new(); + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index d75ac21dc..9255d5d9f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoder : ImageDecoder + private class TestDecoder : ImageDecoder, IImageDecoderSpecialized { // Couldn't make xUnit happy without this hackery: private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -368,16 +368,23 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; @@ -389,7 +396,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoderWithParameters : ImageDecoder + private class TestDecoderWithParameters : ImageDecoder, IImageDecoderSpecialized { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -405,16 +412,23 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo IdentifySpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + + public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) => this.DecodeSpecialized(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; From ac4fb62eeb1d4ab69bd3a016865dfeed0feb8612 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 8 Aug 2022 13:05:36 +1000 Subject: [PATCH 23/54] Only expose sanitized decoder methods. --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 21 +- src/ImageSharp/Formats/Gif/GifDecoder.cs | 12 +- src/ImageSharp/Formats/IImageDecoder.cs | 2 +- .../Formats/IImageDecoderSpecialized{T}.cs | 12 +- src/ImageSharp/Formats/ImageDecoder.cs | 65 ------- .../Formats/ImageDecoderExtensions.cs | 182 ++++++++++++++++++ .../ImageDecoderSpecializedExtensions.cs | 82 -------- .../Formats/ImageDecoderUtilities.cs | 49 ++++- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 21 +- src/ImageSharp/Formats/Pbm/PbmDecoder.cs | 12 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 40 ++-- src/ImageSharp/Formats/Tga/TgaDecoder.cs | 12 +- .../Decompressors/WebpTiffCompression.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoder.cs | 12 +- src/ImageSharp/Formats/Webp/WebpDecoder.cs | 12 +- .../Codecs/Jpeg/DecodeJpeg.cs | 2 +- .../Codecs/Jpeg/IdentifyJpeg.cs | 2 +- .../LoadResizeSaveStressRunner.cs | 2 +- .../Formats/Bmp/BmpDecoderTests.cs | 2 +- .../Formats/Gif/GifDecoderTests.cs | 2 +- .../Formats/Gif/GifMetadataTests.cs | 10 +- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 10 +- .../Formats/Png/PngDecoderTests.Chunks.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 4 +- .../Formats/Png/PngMetadataTests.cs | 8 +- .../Formats/Png/PngSmokeTests.cs | 68 +------ tests/ImageSharp.Tests/TestFormat.cs | 21 +- .../ImageProviders/FileProvider.cs | 4 +- .../ReferenceCodecs/MagickReferenceDecoder.cs | 9 +- .../SystemDrawingReferenceDecoder.cs | 9 +- .../Tests/TestImageProviderTests.cs | 42 ++-- 31 files changed, 379 insertions(+), 354 deletions(-) delete mode 100644 src/ImageSharp/Formats/ImageDecoder.cs create mode 100644 src/ImageSharp/Formats/ImageDecoderExtensions.cs delete mode 100644 src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 5b0e803f3..0b93cf01f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Image decoder for generating an image out of a Windows bitmap stream. /// - public class BmpDecoder : ImageDecoder, IImageDecoderSpecialized + public class BmpDecoder : IImageDecoderSpecialized { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -22,29 +22,28 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); /// - public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + Image IImageDecoderSpecialized.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); Image image = new BmpDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - Resize(options.GeneralOptions, image); + ImageDecoderUtilities.Resize(options.GeneralOptions, image); return image; } /// - public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + Image IImageDecoderSpecialized.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 3729e4208..a737ce74e 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : ImageDecoder + public sealed class GifDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Gif } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Gif GifDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index b0363ccf9..949e2133a 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -10,7 +10,7 @@ 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. diff --git a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs index 46ad284da..93a3abb7a 100644 --- a/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs +++ b/src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.IO; @@ -17,23 +17,29 @@ namespace SixLabors.ImageSharp.Formats /// /// Decodes the image from the specified stream to an of a specific pixel type. /// + /// + /// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use. + /// /// The pixel format. /// The specialized decoder options. /// The containing image data. /// The token to monitor for cancellation requests. /// The . /// Thrown if the encoded image contains errors. - public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) + 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 DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); + public Image Decode(T options, Stream stream, CancellationToken cancellationToken); } } diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs deleted file mode 100644 index 4863f5b67..000000000 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.IO; -using System.Threading; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// The base class for all image decoders. - /// - public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder - { - /// - public abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); - - /// - public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; - - /// - public abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); - - /// - /// Performs a resize operation against the decoded image. If the target size is not set, or the image size - /// already matches the target size, the image is untouched. - /// - /// The decoder options. - /// The decoded image. - protected static void Resize(DecoderOptions options, Image image) - { - if (ShouldResize(options, image)) - { - ResizeOptions resizeOptions = new() - { - Size = options.TargetSize.Value, - Sampler = KnownResamplers.Box, - Mode = ResizeMode.Max - }; - - image.Mutate(x => x.Resize(resizeOptions)); - } - } - - /// - /// Determines whether the decoded image should be resized. - /// - /// The decoder options. - /// The decoded image. - /// if the image should be resized, otherwise; . - private static bool ShouldResize(DecoderOptions options, Image image) - { - if (options.TargetSize is null) - { - return false; - } - - Size targetSize = options.TargetSize.Value; - Size currentSize = image.Size(); - return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; - } - } -} diff --git a/src/ImageSharp/Formats/ImageDecoderExtensions.cs b/src/ImageSharp/Formats/ImageDecoderExtensions.cs new file mode 100644 index 000000000..0e3bcdf9a --- /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/ImageDecoderSpecializedExtensions.cs b/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs deleted file mode 100644 index d23f088aa..000000000 --- a/src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// Extensions methods for . - /// - public static class ImageDecoderSpecializedExtensions - { - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The type of specialized options. - /// The pixel format. - /// The decoder. - /// The specialized decoder options. - /// The containing image data. - /// The . - /// Thrown if the encoded image contains errors. - public static Image DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) - where T : ISpecializedDecoderOptions - where TPixel : unmanaged, IPixel - => decoder.DecodeSpecialized(options, stream, default); - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The type of specialized options. - /// The decoder. - /// The specialized decoder options. - /// The containing image data. - /// The . - /// Thrown if the encoded image contains errors. - public static Image DecodeSpecialized(this IImageDecoderSpecialized decoder, T options, Stream stream) - where T : ISpecializedDecoderOptions - => decoder.DecodeSpecialized(options, stream, default); - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The type of specialized options. - /// The pixel format. - /// The decoder. - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// A representing the asynchronous operation. - /// Thrown if the encoded image contains errors. - public static Task> DecodeSpecializedAsync(this IImageDecoderSpecialized decoder, T options, Stream stream, CancellationToken cancellationToken = default) - where T : ISpecializedDecoderOptions - where TPixel : unmanaged, IPixel - => Image.WithSeekableStreamAsync( - options.GeneralOptions, - stream, - (s, ct) => decoder.DecodeSpecialized(options, s, ct), - cancellationToken); - - /// - /// Decodes the image from the specified stream to an of a specific pixel type. - /// - /// The type of specialized options. - /// The decoder. - /// The specialized decoder options. - /// The containing image data. - /// The token to monitor for cancellation requests. - /// A representing the asynchronous operation. - /// Thrown if the encoded image contains errors. - public static Task DecodeSpecializedAsync(this IImageDecoderSpecialized decoder, T options, Stream stream, CancellationToken cancellationToken = default) - where T : ISpecializedDecoderOptions - => Image.WithSeekableStreamAsync( - options.GeneralOptions, - stream, - (s, ct) => decoder.DecodeSpecialized(options, s, ct), - cancellationToken); - } -} diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index b6cefd2c8..2c0482034 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -7,12 +7,55 @@ using System.Threading; 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 = KnownResamplers.Box, + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(resizeOptions)); + } + } + + /// + /// Determines whether the decoded image should be resized. + /// + /// The decoder options. + /// The decoded image. + /// if the image should be resized, otherwise; . + private static bool ShouldResize(DecoderOptions options, Image image) + { + if (options.TargetSize is null) + { + return false; + } + + Size targetSize = options.TargetSize.Value; + Size currentSize = image.Size(); + return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; + } + + internal static IImageInfo Identify( this IImageDecoderInternals decoder, Configuration configuration, Stream stream, @@ -30,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats } } - public static Image Decode( + internal static Image Decode( this IImageDecoderInternals decoder, Configuration configuration, Stream stream, @@ -38,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats 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/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index a655acbce..09b034c79 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Decoder for generating an image out of a jpeg encoded stream. /// - public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized + public sealed class JpegDecoder : IImageDecoderSpecialized { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, nameof(stream)); @@ -22,16 +22,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); /// - public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + Image IImageDecoderSpecialized.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -41,14 +40,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) { - Resize(options.GeneralOptions, image); + ImageDecoderUtilities.Resize(options.GeneralOptions, image); } return image; } /// - public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + Image IImageDecoderSpecialized.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs index 1127f8e09..394da0a60 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs @@ -26,10 +26,10 @@ namespace SixLabors.ImageSharp.Formats.Pbm /// /// The specification of these images is found at . /// - public sealed class PbmDecoder : ImageDecoder + public sealed class PbmDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -46,13 +46,13 @@ namespace SixLabors.ImageSharp.Formats.Pbm PbmDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 9f23982d0..2aa563435 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Decoder for generating an image out of a png encoded stream. /// - public sealed class PngDecoder : ImageDecoder + public sealed class PngDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Png } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Png PngDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -48,47 +48,49 @@ namespace SixLabors.ImageSharp.Formats.Png 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(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.Rgb: if (bits == PngBitDepth.Bit16) { return !meta.HasTransparency - ? this.Decode(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); } return !meta.HasTransparency - ? this.Decode(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.Palette: - return this.Decode(options, stream, cancellationToken); + return imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); case PngColorType.RgbWithAlpha: return (bits == PngBitDepth.Bit16) - ? this.Decode(options, stream, cancellationToken) - : this.Decode(options, stream, cancellationToken); + ? imageDecoder.Decode(options, stream, cancellationToken) + : imageDecoder.Decode(options, stream, cancellationToken); default: - return this.Decode(options, stream, cancellationToken); + return imageDecoder.Decode(options, stream, cancellationToken); } } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs index 2ad845adb..ac99271cb 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Image decoder for Truevision TGA images. /// - public sealed class TgaDecoder : ImageDecoder + public sealed class TgaDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Tga } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Tga TgaDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index d2461bfa2..db93406a0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { - using Image image = new WebpDecoder().Decode(this.options, stream, cancellationToken); + using Image image = ((IImageDecoder)new WebpDecoder()).Decode(this.options, stream, cancellationToken); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs index 012adc586..dfbee19a0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// Image decoder for generating an image out of a TIFF stream. /// - public class TiffDecoder : ImageDecoder + public class TiffDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index b2dbbcba9..9b05de5ea 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Image decoder for generating an image out of a webp stream. /// - public sealed class WebpDecoder : ImageDecoder + public sealed class WebpDecoder : IImageDecoder { /// - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Webp } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); @@ -31,13 +31,13 @@ namespace SixLabors.ImageSharp.Formats.Webp using WebpDecoderCore decoder = new(options); Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - Resize(options, image); + ImageDecoderUtilities.Resize(options, image); return image; } /// - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.Decode(options, stream, cancellationToken); + Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoder)this).Decode(options, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 5df226770..1fee65fa0 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg private void GenericBechmark() { this.preloadedImageStream.Position = 0; - using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream, default); + using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream); } [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index d1e1f0e77..a9cbb418a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg public IImageInfo Identify() { using var memoryStream = new MemoryStream(this.jpegBytes); - var decoder = new JpegDecoder(); + IImageDecoder decoder = new JpegDecoder(); return decoder.Identify(DecoderOptions.Default, memoryStream, default); } } diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index bee55600f..81884ae3d 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave }; var decoder = new JpegDecoder(); - using ImageSharpImage image = decoder.Decode(options, inputStream, default); + using ImageSharpImage image = decoder.Decode(options, inputStream); this.LogImageProcessed(image.Width, image.Height); // Reduce the size of the file diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 8a5892e02..e303cb516 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -497,7 +497,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new BmpDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + using Image image = decoder.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 7c0447a98..cf6abe23c 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif fixed (byte* data = testFile.Bytes.AsSpan(0, length)) { using var stream = new UnmanagedMemoryStream(data, length); - using Image image = GifDecoder.Decode(DecoderOptions.Default, stream, default); + using Image image = GifDecoder.Decode(DecoderOptions.Default, stream); Assert.Equal((200, 200), (image.Width, image.Height)); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 23de14b63..56b991a02 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif input.Save(memoryStream, new GifEncoder()); memoryStream.Position = 0; - using Image image = decoder.Decode(DecoderOptions.Default, memoryStream, default); + 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]); @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new GifDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new GifDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + using Image image = decoder.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new GifDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); GifMetadata meta = image.Metadata.GetGifMetadata(); Assert.Equal(repeatCount, meta.RepeatCount); } @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new GifDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + 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 9046ca14d..d9b07f9b5 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new JpegDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + using Image image = decoder.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new JpegDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new JpegDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream, default); + using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); - IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream); JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(expectedColorType, meta.ColorType); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 16627dd34..9de9f32ec 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var decoder = new PngDecoder(); ImageFormatException exception = - Assert.Throws(() => decoder.Decode(DecoderOptions.Default, memStream, default)); + Assert.Throws(() => decoder.Decode(DecoderOptions.Default, memStream)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index a35f8037c..5e41530cd 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var decoder = new PngDecoder(); - Image image = decoder.Decode(DecoderOptions.Default, stream, default); + Image image = decoder.Decode(DecoderOptions.Default, stream); PngMetadata metadata = image.Metadata.GetPngMetadata(); Assert.Equal(pngColorType, metadata.ColorType); @@ -597,7 +597,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png // 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, default); + using Image imageSharpImage = new PngDecoder().Decode(DecoderOptions.Default, fileStream); fileStream.Position = 0; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index fe74c0b8c..74217c982 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png input.Save(memoryStream, new PngEncoder()); memoryStream.Position = 0; - using Image image = decoder.Decode(DecoderOptions.Default, memoryStream, default); + using Image image = decoder.Decode(DecoderOptions.Default, memoryStream); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); VerifyTextDataIsPresent(meta); } @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png }); memoryStream.Position = 0; - using Image image = decoder.Decode(DecoderOptions.Default, memoryStream, default); + 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)); @@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new PngDecoder(); - using Image image = decoder.Decode(DecoderOptions.Default, stream, default); + using Image image = decoder.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); @@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(imagePath); using var stream = new MemoryStream(testFile.Bytes, false); var decoder = new PngDecoder(); - IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); + IImageInfo image = decoder.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(yResolution, meta.VerticalResolution); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 934b57b03..58843227d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -26,76 +26,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.Save(ms, new PngEncoder()); ms.Position = 0; - using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms, default); + using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms); ImageComparer.Tolerant().VerifySimilarity(image, img2); // 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); - } - } - }*/ - - /* 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]); - } - } - } - } - } - } - } - }*/ - [Theory] [WithTestPatternImages(300, 300, PixelTypes.Rgba32)] public void Resize(TestImageProvider provider) @@ -111,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); image.Save(ms, new PngEncoder()); ms.Position = 0; - using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms, default); + using Image img2 = new PngDecoder().Decode(DecoderOptions.Default, ms); ImageComparer.Tolerant().VerifySimilarity(image, img2); } } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 96fcb6afd..9ec7d8f0b 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; } - public class TestDecoder : ImageDecoder, IImageDecoderSpecialized + public class TestDecoder : IImageDecoderSpecialized { private readonly TestFormat testFormat; @@ -208,16 +208,17 @@ namespace SixLabors.ImageSharp.Tests public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Configuration configuration = options.GeneralOptions.Configuration; @@ -235,8 +236,8 @@ namespace SixLabors.ImageSharp.Tests return this.testFormat.Sample(); } - public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } public class TestDecoderOptions : ISpecializedDecoderOptions diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 6cc96dede..733e3a31f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests // 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.DecodeSpecialized(options, stream, default)); + return Task.FromResult(decoder.Decode(options, stream, default)); } public override void Deserialize(IXunitSerializationInfo info) @@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.Tests var testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); - return decoder.DecodeSpecialized(options, stream, default); + return decoder.Decode(options, stream, default); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 92b7aeac9..1f1d7febe 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class MagickReferenceDecoder : ImageDecoder + public class MagickReferenceDecoder : IImageDecoder { private readonly bool validate; @@ -28,7 +28,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public static MagickReferenceDecoder Instance { get; } = new(); - public override Image Decode(DecoderOptions options, 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 @@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new Image(configuration, new ImageMetadata(), framesList); } - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken 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) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index ddf09ebdd..e14866f81 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -11,18 +11,19 @@ using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { - public class SystemDrawingReferenceDecoder : ImageDecoder + public class SystemDrawingReferenceDecoder : IImageDecoder { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public override IImageInfo Identify(DecoderOptions options, 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 override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel { using var sourceBitmap = new SDBitmap(stream); if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) @@ -47,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(convertedBitmap); } - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 9255d5d9f..46e8b34bc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoder : ImageDecoder, IImageDecoderSpecialized + private class TestDecoder : IImageDecoderSpecialized { // Couldn't make xUnit happy without this hackery: private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -368,24 +368,25 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderOptions)(new() { GeneralOptions = options }), stream, cancellationToken); - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(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 override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderOptions)(new() { GeneralOptions = options }), stream, cancellationToken); - public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; @@ -396,7 +397,7 @@ namespace SixLabors.ImageSharp.Tests } } - private class TestDecoderWithParameters : ImageDecoder, IImageDecoderSpecialized + private class TestDecoderWithParameters : IImageDecoderSpecialized { private static readonly ConcurrentDictionary InvocationCounts = new(); @@ -412,24 +413,25 @@ namespace SixLabors.ImageSharp.Tests } } - public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderWithParametersOptions)(new() { GeneralOptions = options }), stream, cancellationToken); - public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(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 override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode((TestDecoderWithParametersOptions)(new() { GeneralOptions = options }), stream, cancellationToken); - public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; return new Image(42, 42); } - public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) - => this.DecodeSpecialized(options, stream, cancellationToken); + public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; From 0e13e6c96c8ef1adc9d7f2290785612447f53eec Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 8 Aug 2022 13:45:53 +1000 Subject: [PATCH 24/54] Add sampler option, add comment. --- src/ImageSharp/Formats/DecoderOptions.cs | 7 +++++++ src/ImageSharp/Formats/ImageDecoderUtilities.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index a608fcd52..8f16c6ecc 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Formats { @@ -29,6 +31,11 @@ namespace SixLabors.ImageSharp.Formats /// 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. /// diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs index 2c0482034..37a4ebe5f 100644 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats ResizeOptions resizeOptions = new() { Size = options.TargetSize.Value, - Sampler = KnownResamplers.Box, + Sampler = options.Sampler, Mode = ResizeMode.Max }; diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs index d99d522c5..5037c8580 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Processing; + namespace SixLabors.ImageSharp.Formats.Jpeg { /// @@ -15,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg Combined, /// - /// IDCT-only to nearest block scale. + /// IDCT-only to nearest block scale. Similar in output to . /// IdctOnly, From f78ec5108f2005e0572962bc4ab470229bdd0df9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Aug 2022 22:30:06 +1000 Subject: [PATCH 25/54] Add jpeg tests --- .../Formats/Gif/GifMetadataTests.cs | 29 +++++ .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 28 ++++- .../Formats/Jpg/JpegDecoderTests.cs | 113 ++++++++++++++++++ ...code_Resize_Bicubic_Calliphora_150_150.png | 3 + ...coder_Decode_Resize_Calliphora_150_150.png | 3 + ...zed_Combined_Resize_Calliphora_150_150.png | 3 + ...ialized_IDCT_Resize_Calliphora_150_150.png | 3 + ...alized_Scale_Resize_Calliphora_150_150.png | 3 + 8 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 56b991a02..d2c667a6b 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -4,6 +4,7 @@ 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; @@ -126,6 +127,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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] [MemberData(nameof(RatioFiles))] public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) @@ -140,6 +155,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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] [MemberData(nameof(RepeatFiles))] public void Identify_VerifyRepeatCount(string imagePath, uint repeatCount) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index d9b07f9b5..46359b167 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; @@ -103,6 +104,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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] [MemberData(nameof(QualityFiles))] public void Identify_VerifyQuality(string imagePath, int quality) @@ -126,6 +141,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg 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] [InlineData(TestImages.Jpeg.Baseline.Floorplan, JpegColorType.Luminance)] [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, JpegColorType.YCbCrRatio420)] @@ -164,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var stream = new MemoryStream(testFile.Bytes, false); if (useIdentify) { - IImageInfo imageInfo = ((IImageInfoDetector)decoder).Identify(DecoderOptions.Default, stream, default); + IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream, default); test(imageInfo); } else diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 1706f2e21..684716dcf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -11,6 +11,7 @@ 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; @@ -120,6 +121,118 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg appendPixelTypeToFileName: false); } + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24)] + public void JpegDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + using Image image = provider.GetImage(JpegDecoder, options); + + 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)] 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 000000000..e982d9034 --- /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 000000000..65aac22e9 --- /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 000000000..65aac22e9 --- /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 000000000..abe31b2be --- /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 000000000..87087adc5 --- /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 From c5c15759c7980681e38d54964c88e7631f4f4be4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Aug 2022 22:39:28 +1000 Subject: [PATCH 26/54] Fix warnings --- .../General/Buffer2D_DangerousGetRowSpan.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs index 68a2bcde9..7712fc4e6 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 @@ namespace SixLabors.ImageSharp.Benchmarks.General { 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; From a4c14e70f2a78a063a3b2377719461992892ad88 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 11:56:52 +1000 Subject: [PATCH 27/54] Add additional tests --- .../Formats/Gif/GifDecoderTests.cs | 24 +++++++++++++++++- .../Formats/Pbm/PbmDecoderTests.cs | 25 +++++++++++++++++++ .../Formats/Png/PngDecoderTests.cs | 24 +++++++++++++++++- .../Formats/Tga/TgaDecoderTests.cs | 25 ++++++++++++++++++- .../Formats/Tiff/TiffDecoderTests.cs | 23 +++++++++++++++++ .../Formats/WebP/WebpDecoderTests.cs | 24 ++++++++++++++++++ .../TestUtilities/TestEnvironment.Formats.cs | 6 ++--- ...GifDecoder_Decode_Resize_giphy_150_150.png | 3 +++ ...ecoder_Decode_Resize_rgb_plain_150_150.png | 3 +++ ...ngDecoder_Decode_Resize_splash_150_150.png | 3 +++ ...der_Decode_Resize_rgb_a_rle_UL_150_150.png | 3 +++ ...size_RgbaUnassociatedAlpha3bit_150_150.png | 3 +++ ...er_Decode_Resize_bike_lossless_150_150.png | 3 +++ 13 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/PbmDecoderTests/PbmDecoder_Decode_Resize_rgb_plain_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png create mode 100644 tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index cf6abe23c..cbca125a5 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -7,7 +7,6 @@ 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; @@ -40,6 +39,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif 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() + { + 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.Exact, + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + [Fact] public unsafe void Decode_NonTerminatedFinalFrame() { diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs index 570ede8cf..0d37c6b81 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Pbm 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.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 29e0af9c6..7af6a8007 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -5,7 +5,7 @@ using System; 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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.Exact, + 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/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 57d8aeff5..8a5412ab7 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 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } } + [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.Exact, + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 6aaeafdda..443e42700 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -5,6 +5,7 @@ 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; @@ -710,5 +711,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff 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/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 90587e36c..aae27cae2 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -1,8 +1,10 @@ // 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.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -354,6 +356,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp image.DebugSave(provider); } + [Theory] + [WithFile(Lossless.BikeThreeTransforms, PixelTypes.Rgba32)] + public void WebpDecoder_Decode_Resize(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + 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.Exact, + provider, + testOutputDetails: details, + appendPixelTypeToFileName: false); + } + // https://github.com/SixLabors/ImageSharp/issues/1594 [Theory] [WithFile(Lossy.Issue1594, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 2b7316f0c..7e4797c5a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -63,8 +63,8 @@ namespace SixLabors.ImageSharp.Tests 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 @@ namespace SixLabors.ImageSharp.Tests cfg.ConfigureCodecs( BmpFormat.Instance, - IsWindows ? (IImageDecoder)SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, + IsWindows ? SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, bmpEncoder, new BmpImageFormatDetector()); 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 000000000..87c1fb728 --- /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/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 000000000..b7e848d84 --- /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 000000000..c697cd4c2 --- /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 000000000..f3b7b0e80 --- /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 000000000..412af0ac7 --- /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 000000000..35b99df98 --- /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 From acbc7bc580eedacb6c327c0b7e8b949ebd88fb9e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 12:15:37 +1000 Subject: [PATCH 28/54] Update PngDecoderTests.cs --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 7af6a8007..6733146d7 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.Exact, + ImageComparer.TolerantPercentage(0.0004F), // Magick decoder shows difference on Mac provider, testOutputDetails: details, appendPixelTypeToFileName: false); From 62427239b0ae4ca3c1ea710d188699d608d540f1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 12:16:15 +1000 Subject: [PATCH 29/54] Update PngDecoderTests.cs --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 6733146d7..4be5e4e44 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0004F), // Magick decoder shows difference on Mac + ImageComparer.TolerantPercentage(0.0003F), // Magick decoder shows difference on Mac provider, testOutputDetails: details, appendPixelTypeToFileName: false); From 3b6b30a2f5ebf57ccd9988f082de93fd25b2c54a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 12:59:44 +1000 Subject: [PATCH 30/54] Change percentages for mac --- .../Formats/Gif/GifDecoderTests.cs | 2 +- .../Formats/Tga/TgaDecoderTests.cs | 2 +- .../Formats/WebP/WebpDecoderTests.cs | 2 +- ...ImageDifferenceIsOverThresholdException.cs | 28 +++++++++++++++++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index cbca125a5..3a8ae6f11 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.Exact, + ImageComparer.TolerantPercentage(0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 8a5412ab7..5007aa371 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -764,7 +764,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.Exact, + ImageComparer.TolerantPercentage(0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index aae27cae2..417d3ce2d 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -372,7 +372,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.Exact, + ImageComparer.TolerantPercentage(0.0006F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index e77dce861..7560308db 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -24,8 +24,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison 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); @@ -33,11 +32,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison 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 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison return sb.ToString(); } + + private static string GetEnvironmentName() + { + if (TestEnvironment.IsMacOS) + { + return "Mac OS"; + } + + if (TestEnvironment.IsMacOS) + { + return "Linux"; + } + + if (TestEnvironment.IsWindows) + { + return "Windows"; + } + + return "Unknown"; + } } } From efb2511633f5afff5cfd7bb4e1cd21216e274ab4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 13:00:00 +1000 Subject: [PATCH 31/54] Fix whitespace --- .../Exceptions/ImageDifferenceIsOverThresholdException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index 7560308db..eb446c327 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison 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); From 751bf48b35763483775e236ab916091361597aa0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 20 Aug 2022 13:16:28 +1000 Subject: [PATCH 32/54] Tweak WebP test percentage for MacOS --- tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs | 2 +- .../Exceptions/ImageDifferenceIsOverThresholdException.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 417d3ce2d..ac5918952 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -372,7 +372,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0006F), + ImageComparer.TolerantPercentage(0.0007F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index eb446c327..8857471d0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { if (TestEnvironment.IsMacOS) { - return "Mac OS"; + return "MacOS"; } if (TestEnvironment.IsMacOS) From 9c7ba12ee28dca474f68bc82ca4f5bdabdeec4e8 Mon Sep 17 00:00:00 2001 From: Luis Miguel Merino Date: Tue, 23 Aug 2022 10:17:18 +0200 Subject: [PATCH 33/54] Fix IPTC tags written on jpg files that contains non-English characters can't be correctly displayed on external apps #2212 --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 72c00302d..870c4c8ce 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -20,6 +20,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc private const uint MaxStandardDataTagSize = 0x7FFF; + /// + /// 1:90 Coded Character Set + /// + private const byte IptcEnvelopeCodedCharacterSet = 0x5A; + + /// + /// This value marks that UTF-8 encoding is used in application records + /// + private static readonly byte[] CodedCharacterSetUtf8Value = { 0x1B, 0x25, 0x47 }; + /// /// Initializes a new instance of the class. /// @@ -194,6 +204,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc this.values.Add(new IptcValue(tag, encoding, value, strict)); } + /// + /// Sets the value of the specified tag. + /// + /// The tag of the iptc value. + /// The value. + /// + /// Indicates if length restrictions from the specification should be followed strictly. + /// Defaults to true. + /// + public void SetValue(IptcTag tag, string value, bool strict = true) => this.SetValue(tag, Encoding.UTF8, value, strict); + /// /// Makes sure the datetime is formatted according to the iptc specification. /// @@ -219,17 +240,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc this.SetValue(tag, Encoding.UTF8, formattedDate); } - /// - /// Sets the value of the specified tag. - /// - /// The tag of the iptc value. - /// The value. - /// - /// Indicates if length restrictions from the specification should be followed strictly. - /// Defaults to true. - /// - public void SetValue(IptcTag tag, string value, bool strict = true) => this.SetValue(tag, Encoding.UTF8, value, strict); - /// /// Updates the data of the profile. /// @@ -241,9 +251,28 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc length += value.Length + 5; } - this.Data = new byte[length]; + bool hasValuesInUtf8 = this.HasValuesInUtf8(); + + if (hasValuesInUtf8) + { + length += 5 + CodedCharacterSetUtf8Value.Length; // additional length for UTF-8 Tag + } + this.Data = new byte[length]; int i = 0; + + if (hasValuesInUtf8) + { + // Standard DataSet Tag + this.Data[i++] = IptcTagMarkerByte; + this.Data[i++] = 1; // Envelope + this.Data[i++] = IptcEnvelopeCodedCharacterSet; + this.Data[i++] = (byte)(CodedCharacterSetUtf8Value.Length >> 8); + this.Data[i++] = (byte)CodedCharacterSetUtf8Value.Length; + Buffer.BlockCopy(CodedCharacterSetUtf8Value, 0, this.Data, i, CodedCharacterSetUtf8Value.Length); + i += CodedCharacterSetUtf8Value.Length; + } + foreach (IptcValue value in this.Values) { // Standard DataSet Tag @@ -264,7 +293,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc // | | | octet 4(most significant bit) always will be 0. | // +-----------+----------------+---------------------------------------------------------------------------------+ this.Data[i++] = IptcTagMarkerByte; - this.Data[i++] = 2; + this.Data[i++] = 2; // Application this.Data[i++] = (byte)value.Tag; this.Data[i++] = (byte)(value.Length >> 8); this.Data[i++] = (byte)value.Length; @@ -309,7 +338,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc if (isValidEntry && byteCount > 0 && (offset <= this.Data.Length - byteCount)) { - var iptcData = new byte[byteCount]; + byte[] iptcData = new byte[byteCount]; Buffer.BlockCopy(this.Data, offset, iptcData, 0, (int)byteCount); this.values.Add(new IptcValue(tag, iptcData, false)); } @@ -317,5 +346,22 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc offset += (int)byteCount; } } + + /// + /// Gets if any value has UTF-8 encoding + /// + /// true if any value has UTF-8 encoding + private bool HasValuesInUtf8() + { + foreach (IptcValue value in this.values) + { + if (value.Encoding == Encoding.UTF8) + { + return true; + } + } + + return false; + } } } From a58d8d5e25a34eeb53e0080f85ab324854b17b38 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 00:57:08 +0200 Subject: [PATCH 34/54] Ignore iptc records, which are not application records when reading iptc data --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 870c4c8ce..c2a94e5b0 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -21,12 +21,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc private const uint MaxStandardDataTagSize = 0x7FFF; /// - /// 1:90 Coded Character Set + /// 1:90 Coded Character Set. /// private const byte IptcEnvelopeCodedCharacterSet = 0x5A; /// - /// This value marks that UTF-8 encoding is used in application records + /// This value marks that UTF-8 encoding is used in application records. /// private static readonly byte[] CodedCharacterSetUtf8Value = { 0x1B, 0x25, 0x47 }; @@ -255,7 +255,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc if (hasValuesInUtf8) { - length += 5 + CodedCharacterSetUtf8Value.Length; // additional length for UTF-8 Tag + length += 5 + CodedCharacterSetUtf8Value.Length; // Additional length for UTF-8 Tag. } this.Data = new byte[length]; @@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc if (hasValuesInUtf8) { - // Standard DataSet Tag + // Envelope Record. this.Data[i++] = IptcTagMarkerByte; this.Data[i++] = 1; // Envelope this.Data[i++] = IptcEnvelopeCodedCharacterSet; @@ -275,7 +275,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc foreach (IptcValue value in this.Values) { - // Standard DataSet Tag + // Application Record. // +-----------+----------------+---------------------------------------------------------------------------------+ // | Octet Pos | Name | Description | // +==========-+================+=================================================================================+ @@ -327,6 +327,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc bool isValidRecordNumber = recordNumber is >= 1 and <= 9; var tag = (IptcTag)this.Data[offset++]; bool isValidEntry = isValidTagMarker && isValidRecordNumber; + bool isApplicationRecord = recordNumber == 0x02; uint byteCount = BinaryPrimitives.ReadUInt16BigEndian(this.Data.AsSpan(offset, 2)); offset += 2; @@ -336,7 +337,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc break; } - if (isValidEntry && byteCount > 0 && (offset <= this.Data.Length - byteCount)) + if (isValidEntry && isApplicationRecord && byteCount > 0 && (offset <= this.Data.Length - byteCount)) { byte[] iptcData = new byte[byteCount]; Buffer.BlockCopy(this.Data, offset, iptcData, 0, (int)byteCount); @@ -348,9 +349,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc } /// - /// Gets if any value has UTF-8 encoding + /// Gets if any value has UTF-8 encoding. /// - /// true if any value has UTF-8 encoding + /// true if any value has UTF-8 encoding. private bool HasValuesInUtf8() { foreach (IptcValue value in this.values) From e314305d9646b05047a9e7f16965d9f04865e4b4 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 05:30:43 +0200 Subject: [PATCH 35/54] Add enum for RecordNumber --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 7 ++++--- .../Profiles/IPTC/IptcRecordNumber.cs | 21 +++++++++++++++++++ .../Formats/Jpg/JpegEncoderTests.Metadata.cs | 8 ++++--- 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index c2a94e5b0..07b6b7360 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; +using SixLabors.ImageSharp.Metadata.Profiles.IPTC; namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc { @@ -265,7 +266,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc { // Envelope Record. this.Data[i++] = IptcTagMarkerByte; - this.Data[i++] = 1; // Envelope + this.Data[i++] = (byte)IptcRecordNumber.Envelope; this.Data[i++] = IptcEnvelopeCodedCharacterSet; this.Data[i++] = (byte)(CodedCharacterSetUtf8Value.Length >> 8); this.Data[i++] = (byte)CodedCharacterSetUtf8Value.Length; @@ -293,7 +294,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc // | | | octet 4(most significant bit) always will be 0. | // +-----------+----------------+---------------------------------------------------------------------------------+ this.Data[i++] = IptcTagMarkerByte; - this.Data[i++] = 2; // Application + this.Data[i++] = (byte)IptcRecordNumber.Application; this.Data[i++] = (byte)value.Tag; this.Data[i++] = (byte)(value.Length >> 8); this.Data[i++] = (byte)value.Length; @@ -327,7 +328,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc bool isValidRecordNumber = recordNumber is >= 1 and <= 9; var tag = (IptcTag)this.Data[offset++]; bool isValidEntry = isValidTagMarker && isValidRecordNumber; - bool isApplicationRecord = recordNumber == 0x02; + bool isApplicationRecord = recordNumber == (byte)IptcRecordNumber.Application; uint byteCount = BinaryPrimitives.ReadUInt16BigEndian(this.Data.AsSpan(offset, 2)); offset += 2; diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs new file mode 100644 index 000000000..52e4c47a4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Metadata.Profiles.IPTC +{ + /// + /// Enum for the different record types of a IPTC value. + /// + internal enum IptcRecordNumber : byte + { + /// + /// A Envelope Record. + /// + Envelope = 0x01, + + /// + /// A Application Record. + /// + Application = 0x02 + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 60f45664d..23b2f749d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -38,8 +38,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { // arrange using var input = new Image(1, 1); - input.Metadata.IptcProfile = new IptcProfile(); - input.Metadata.IptcProfile.SetValue(IptcTag.Byline, "unit_test"); + var expectedProfile = new IptcProfile(); + expectedProfile.SetValue(IptcTag.Country, "ESPAÑA"); + expectedProfile.SetValue(IptcTag.City, "unit-test-city"); + input.Metadata.IptcProfile = expectedProfile; // act using var memStream = new MemoryStream(); @@ -50,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var output = Image.Load(memStream); IptcProfile actual = output.Metadata.IptcProfile; Assert.NotNull(actual); - IEnumerable values = input.Metadata.IptcProfile.Values; + IEnumerable values = expectedProfile.Values; Assert.Equal(values, actual.Values); } From 65c3c44ab58086a0b5737277a7747b342bb3747c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 06:17:12 +0200 Subject: [PATCH 36/54] Avoid code duplication --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 07b6b7360..0a6831e9b 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -256,27 +256,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc if (hasValuesInUtf8) { - length += 5 + CodedCharacterSetUtf8Value.Length; // Additional length for UTF-8 Tag. + // Additional length for UTF-8 Tag. + length += 5 + CodedCharacterSetUtf8Value.Length; } this.Data = new byte[length]; - int i = 0; - + int offset = 0; if (hasValuesInUtf8) { - // Envelope Record. - this.Data[i++] = IptcTagMarkerByte; - this.Data[i++] = (byte)IptcRecordNumber.Envelope; - this.Data[i++] = IptcEnvelopeCodedCharacterSet; - this.Data[i++] = (byte)(CodedCharacterSetUtf8Value.Length >> 8); - this.Data[i++] = (byte)CodedCharacterSetUtf8Value.Length; - Buffer.BlockCopy(CodedCharacterSetUtf8Value, 0, this.Data, i, CodedCharacterSetUtf8Value.Length); - i += CodedCharacterSetUtf8Value.Length; + // Write Envelope Record. + offset = this.WriteRecord(offset, CodedCharacterSetUtf8Value, IptcRecordNumber.Envelope, IptcEnvelopeCodedCharacterSet); } foreach (IptcValue value in this.Values) { - // Application Record. + // Write Application Record. // +-----------+----------------+---------------------------------------------------------------------------------+ // | Octet Pos | Name | Description | // +==========-+================+=================================================================================+ @@ -293,17 +287,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc // | | Octet Count | the following data field(32767 or fewer octets). Note that the value of bit 7 of| // | | | octet 4(most significant bit) always will be 0. | // +-----------+----------------+---------------------------------------------------------------------------------+ - this.Data[i++] = IptcTagMarkerByte; - this.Data[i++] = (byte)IptcRecordNumber.Application; - this.Data[i++] = (byte)value.Tag; - this.Data[i++] = (byte)(value.Length >> 8); - this.Data[i++] = (byte)value.Length; - if (value.Length > 0) - { - Buffer.BlockCopy(value.ToByteArray(), 0, this.Data, i, value.Length); - i += value.Length; - } + offset = this.WriteRecord(offset, value.ToByteArray(), IptcRecordNumber.Application, (byte)value.Tag); + } + } + + private int WriteRecord(int offset, byte[] recordData, IptcRecordNumber recordNumber, byte recordBinaryRepresentation) + { + this.Data[offset++] = IptcTagMarkerByte; + this.Data[offset++] = (byte)recordNumber; + this.Data[offset++] = recordBinaryRepresentation; + this.Data[offset++] = (byte)(recordData.Length >> 8); + this.Data[offset++] = (byte)recordData.Length; + if (recordData.Length > 0) + { + Buffer.BlockCopy(recordData, 0, this.Data, offset, recordData.Length); + offset += recordData.Length; } + + return offset; } private void Initialize() From 288fb0ad514b87254b87fa72802fa67c9d4440c7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 06:30:23 +0200 Subject: [PATCH 37/54] Add test making sure envelope data is written, when UTF-8 data is present --- .../Profiles/IPTC/IptcProfileTests.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index c9972aa25..70b08b9ec 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 JpegDecoder() { IgnoreMetadata = false }; + private static JpegDecoder JpegDecoder => new() { IgnoreMetadata = false }; - private static TiffDecoder TiffDecoder => new TiffDecoder() { IgnoreMetadata = false }; + private static TiffDecoder TiffDecoder => new() { IgnoreMetadata = false }; public static IEnumerable AllIptcTags() { @@ -27,6 +27,22 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC } } + [Fact] + public void IptcProfile_WithUtf8Data_WritesEnvelopeRecord_Works() + { + // arrange + var profile = new IptcProfile(); + profile.SetValue(IptcTag.City, "ESPAÑA"); + profile.UpdateData(); + byte[] expectedEnvelopeData = { 28, 1, 90, 0, 3, 27, 37, 71 }; + + // act + byte[] profileBytes = profile.Data; + + // assert + Assert.True(profileBytes.AsSpan(0, 8).SequenceEqual(expectedEnvelopeData)); + } + [Theory] [MemberData(nameof(AllIptcTags))] public void IptcProfile_SetValue_WithStrictEnabled_Works(IptcTag tag) From 41bef5bdf779e418655faf921e07fff96d6774d8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 15:19:22 +0200 Subject: [PATCH 38/54] Review suggestions --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 0a6831e9b..ec3eed265 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -26,11 +26,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc /// private const byte IptcEnvelopeCodedCharacterSet = 0x5A; - /// - /// This value marks that UTF-8 encoding is used in application records. - /// - private static readonly byte[] CodedCharacterSetUtf8Value = { 0x1B, 0x25, 0x47 }; - /// /// Initializes a new instance of the class. /// @@ -75,6 +70,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc } } + /// + /// Gets a byte array marking that UTF-8 encoding is used in application records. + /// + private static ReadOnlySpan CodedCharacterSetUtf8Value => new byte[] { 0x1B, 0x25, 0x47 }; // Uses C#'s optimization to refer to the data segment in the assembly directly, no allocation occurs. + /// /// Gets the byte data of the IPTC profile. /// @@ -291,16 +291,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc } } - private int WriteRecord(int offset, byte[] recordData, IptcRecordNumber recordNumber, byte recordBinaryRepresentation) + private int WriteRecord(int offset, ReadOnlySpan recordData, IptcRecordNumber recordNumber, byte recordBinaryRepresentation) { - this.Data[offset++] = IptcTagMarkerByte; - this.Data[offset++] = (byte)recordNumber; - this.Data[offset++] = recordBinaryRepresentation; - this.Data[offset++] = (byte)(recordData.Length >> 8); - this.Data[offset++] = (byte)recordData.Length; + Span data = this.Data.AsSpan(offset, 5); + data[0] = IptcTagMarkerByte; + data[1] = (byte)recordNumber; + data[2] = recordBinaryRepresentation; + data[3] = (byte)(recordData.Length >> 8); + data[4] = (byte)recordData.Length; + offset += 5; if (recordData.Length > 0) { - Buffer.BlockCopy(recordData, 0, this.Data, offset, recordData.Length); + recordData.CopyTo(this.Data.AsSpan(offset)); offset += recordData.Length; } From 0ee5e4382e00593ca7fb01f80f7ed643e1f5198d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 22:28:04 +0200 Subject: [PATCH 39/54] Set HasTransparency to true, if tRns chunk is found. Fixes #2209 --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 784d9aa11..8e6789132 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Buffers.Binary; -using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; @@ -188,6 +187,7 @@ namespace SixLabors.ImageSharp.Formats.Png chunk.Data.GetSpan().CopyTo(alpha); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha, pngMetadata); + pngMetadata.HasTransparency = true; break; case PngChunkType.Text: this.ReadTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); @@ -295,6 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Png chunk.Data.GetSpan().CopyTo(alpha); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha, pngMetadata); + pngMetadata.HasTransparency = true; if (this.colorMetadataOnly) { @@ -380,8 +381,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Reads the least significant bits from the byte pair with the others set to 0. /// - /// The source buffer - /// THe offset + /// The source buffer. + /// THe offset. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ReadByteLittleEndian(ReadOnlySpan buffer, int offset) @@ -392,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// specified number of bits. /// /// The bytes to convert from. Cannot be empty. - /// The number of bytes per scanline + /// The number of bytes per scanline. /// The number of bits per value. /// The new array. /// The resulting array. From 0e9317153324cd8aecd8d6887d3eb136bc451413 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 24 Aug 2022 22:40:56 +0200 Subject: [PATCH 40/54] Add tests for #2209 --- .../Formats/Png/PngDecoderTests.cs | 24 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 +++ tests/Images/Input/Png/issues/Issue_2209.png | 3 +++ 3 files changed, 30 insertions(+) create mode 100644 tests/Images/Input/Png/issues/Issue_2209.png diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 29e0af9c6..22454d257 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -470,6 +470,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Null(ex); } + // https://github.com/SixLabors/ImageSharp/issues/2209 + [Theory] + [WithFile(TestImages.Png.Issue2209IndexedWithTransparency, PixelTypes.Rgba32)] + public void Issue2209_Decode_HasTransparencyIsTrue(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder); + image.DebugSave(provider); + PngMetadata metadata = image.Metadata.GetPngMetadata(); + Assert.True(metadata.HasTransparency); + } + + // https://github.com/SixLabors/ImageSharp/issues/2209 + [Theory] + [InlineData(TestImages.Png.Issue2209IndexedWithTransparency)] + public void Issue2209_Identify_HasTransparencyIsTrue(string imagePath) + { + var testFile = TestFile.Create(imagePath); + using var stream = new MemoryStream(testFile.Bytes, false); + IImageInfo imageInfo = Image.Identify(stream); + PngMetadata metadata = imageInfo.Metadata.GetPngMetadata(); + Assert.True(metadata.HasTransparency); + } + // https://github.com/SixLabors/ImageSharp/issues/410 [Theory] [WithFile(TestImages.Png.Bad.Issue410_MalformedApplePng, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 306a28dae..ddaf672b4 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -125,6 +125,9 @@ namespace SixLabors.ImageSharp.Tests // Discussion 1875: https://github.com/SixLabors/ImageSharp/discussions/1875 public const string Issue1875 = "Png/raw-profile-type-exif.png"; + // Issue 2209: https://github.com/SixLabors/ImageSharp/issues/2209 + public const string Issue2209IndexedWithTransparency = "Png/issues/Issue_2209.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; diff --git a/tests/Images/Input/Png/issues/Issue_2209.png b/tests/Images/Input/Png/issues/Issue_2209.png new file mode 100644 index 000000000..6cc26bce5 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2209.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58eae3863a2107bf3359a87492bc95524b54dd091683e333ac73d76f229a1f71 +size 621275 From e69658ea7340440a97c8fdde84a8079928b5c11e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Aug 2022 10:45:20 +1000 Subject: [PATCH 41/54] Only assign when the relevant detail is there. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 8e6789132..29b28c918 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -187,7 +187,6 @@ namespace SixLabors.ImageSharp.Formats.Png chunk.Data.GetSpan().CopyTo(alpha); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha, pngMetadata); - pngMetadata.HasTransparency = true; break; case PngChunkType.Text: this.ReadTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); @@ -295,7 +294,6 @@ namespace SixLabors.ImageSharp.Formats.Png chunk.Data.GetSpan().CopyTo(alpha); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha, pngMetadata); - pngMetadata.HasTransparency = true; if (this.colorMetadataOnly) { @@ -974,6 +972,10 @@ namespace SixLabors.ImageSharp.Formats.Png pngMetadata.HasTransparency = true; } } + else if (this.pngColorType == PngColorType.Palette && alpha.Length > 0) + { + pngMetadata.HasTransparency = true; + } } /// From 7356edd4ea71468d49e4ee933659c95904f805fa Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 15:38:13 +0200 Subject: [PATCH 42/54] Add option to write 2 bit bitmap's --- src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs | 5 + src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 125 +++++++++++++----- .../Formats/Bmp/BmpEncoderTests.cs | 52 +++++++- tests/ImageSharp.Tests/TestImages.cs | 2 + .../ImageComparison/ImageComparer.cs | 11 +- tests/Images/Input/Bmp/pal2.bmp | 3 + tests/Images/Input/Bmp/pal2color.bmp | 3 + 8 files changed, 158 insertions(+), 45 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal2.bmp create mode 100644 tests/Images/Input/Bmp/pal2color.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 1b73d8b18..f66883c20 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -13,6 +13,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Pixel1 = 1, + /// + /// 2 bits per pixel. + /// + Pixel2 = 2, + /// /// 4 bits per pixel. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 3a96c4022..517a3b8cf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1387,7 +1387,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int colorMapSizeBytes = -1; if (this.infoHeader.ClrUsed == 0) { - if (this.infoHeader.BitsPerPixel is 1 or 4 or 8) + if (this.infoHeader.BitsPerPixel is 1 or 2 or 4 or 8) { switch (this.fileMarkerType) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index f71275b7c..257159bd2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -57,6 +57,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private const int ColorPaletteSize4Bit = 64; + /// + /// The color palette for an 2 bit image will have 4 entry's with 4 bytes for each entry. + /// + private const int ColorPaletteSize2Bit = 16; + /// /// The color palette for an 1 bit image will have 2 entry's with 4 bytes for each entry. /// @@ -125,19 +130,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); - int colorPaletteSize = 0; - if (this.bitsPerPixel == BmpBitsPerPixel.Pixel8) - { - colorPaletteSize = ColorPaletteSize8Bit; - } - else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel4) - { - colorPaletteSize = ColorPaletteSize4Bit; - } - else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel1) + int colorPaletteSize = this.bitsPerPixel switch { - colorPaletteSize = ColorPaletteSize1Bit; - } + BmpBitsPerPixel.Pixel8 => ColorPaletteSize8Bit, + BmpBitsPerPixel.Pixel4 => ColorPaletteSize4Bit, + BmpBitsPerPixel.Pixel2 => ColorPaletteSize2Bit, + BmpBitsPerPixel.Pixel1 => ColorPaletteSize1Bit, + _ => 0 + }; byte[] iccProfileData = null; int iccProfileSize = 0; @@ -322,27 +322,31 @@ namespace SixLabors.ImageSharp.Formats.Bmp switch (this.bitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32Bit(stream, pixels); + this.Write32BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24Bit(stream, pixels); + this.Write24BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel16: - this.Write16Bit(stream, pixels); + this.Write16BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel8: - this.Write8Bit(stream, image); + this.Write8BitPixelData(stream, image); break; case BmpBitsPerPixel.Pixel4: - this.Write4BitColor(stream, image); + this.Write4BitPixelData(stream, image); + break; + + case BmpBitsPerPixel.Pixel2: + this.Write2BitPixelData(stream, image); break; case BmpBitsPerPixel.Pixel1: - this.Write1BitColor(stream, image); + this.Write1BitPixelData(stream, image); break; } } @@ -351,12 +355,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding); /// - /// Writes the 32bit color palette to the stream. + /// Writes 32-bit data with a color palette to the stream. /// /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write32Bit(Stream stream, Buffer2D pixels) + private void Write32BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -375,12 +379,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes the 24bit color palette to the stream. + /// Writes 24-bit pixel data with a color palette to the stream. /// /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write24Bit(Stream stream, Buffer2D pixels) + private void Write24BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -401,12 +405,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes the 16bit color palette to the stream. + /// Writes 16-bit pixel data with a color palette to the stream. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write16Bit(Stream stream, Buffer2D pixels) + private void Write16BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -429,12 +433,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes an 8 bit image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// Writes 8 bit pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write8Bit(Stream stream, ImageFrame image) + private void Write8BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { bool isL8 = typeof(TPixel) == typeof(L8); @@ -443,7 +447,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp if (isL8) { - this.Write8BitGray(stream, image, colorPalette); + this.Write8BitPixelData(stream, image, colorPalette); } else { @@ -480,13 +484,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes an 8 bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// Writes 8 bit gray pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. /// A byte span of size 1024 for the color palette. - private void Write8BitGray(Stream stream, ImageFrame image, Span colorPalette) + private void Write8BitPixelData(Stream stream, ImageFrame image, Span colorPalette) where TPixel : unmanaged, IPixel { // Create a color palette with 256 different gray values. @@ -518,12 +522,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes an 4 bit color image with a color palette. The color palette has 16 entry's with 4 bytes for each entry. + /// Writes 4 bit pixel data with a color palette. The color palette has 16 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write4BitColor(Stream stream, ImageFrame image) + private void Write4BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() @@ -562,12 +566,65 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Writes a 1 bit image with a color palette. The color palette has 2 entry's with 4 bytes for each entry. + /// Writes 2 bit pixel data with a color palette. The color palette has 4 entry's with 4 bytes for each entry. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + private void Write2BitPixelData(Stream stream, ImageFrame image) + where TPixel : unmanaged, IPixel + { + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() + { + MaxColors = 4 + }); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); + using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.Allocate(ColorPaletteSize2Bit, AllocationOptions.Clean); + + Span colorPalette = colorPaletteBuffer.GetSpan(); + ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + + ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); + int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; + for (int y = image.Height - 1; y >= 0; y--) + { + pixelRowSpan = quantized.DangerousGetRowSpan(y); + + int endIdx = pixelRowSpan.Length % 4 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 4; + int i = 0; + for (i = 0; i < endIdx; i += 4) + { + stream.WriteByte((byte)((pixelRowSpan[i] << 6) | (pixelRowSpan[i + 1] << 4) | (pixelRowSpan[i + 2] << 2) | pixelRowSpan[i + 3])); + } + + if (pixelRowSpan.Length % 4 != 0) + { + int shift = 6; + byte pixelData = 0; + for (; i < pixelRowSpan.Length; i++) + { + pixelData = (byte)(pixelData | (pixelRowSpan[i] << shift)); + shift -= 2; + } + + stream.WriteByte(pixelData); + } + + for (i = 0; i < rowPadding; i++) + { + stream.WriteByte(0); + } + } + } + + /// + /// Writes 1 bit pixel data with a color palette. The color palette has 2 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write1BitColor(Stream stream, ImageFrame image) + private void Write1BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() @@ -622,7 +679,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp Span colorPaletteAsUInt = MemoryMarshal.Cast(colorPalette); for (int i = 0; i < colorPaletteAsUInt.Length; i++) { - colorPaletteAsUInt[i] = colorPaletteAsUInt[i] & 0x00FFFFFF; // Padding byte, always 0. + colorPaletteAsUInt[i] &= 0x00FFFFFF; // Padding byte, always 0. } stream.Write(colorPalette); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index dd59fb279..fc9554f6a 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Trait("Format", "Bmp")] public class BmpEncoderTests { + private static BmpDecoder BmpDecoder => new(); + public static readonly TheoryData BitsPerPixel = new() { @@ -39,6 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp new() { { Bit1, BmpBitsPerPixel.Pixel1 }, + { Bit2, BmpBitsPerPixel.Pixel2 }, { Bit4, BmpBitsPerPixel.Pixel4 }, { Bit8, BmpBitsPerPixel.Pixel8 }, { Rgb16, BmpBitsPerPixel.Pixel16 }, @@ -204,6 +207,50 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true, customComparer: comparer); } + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + public void Encode_2Bit_WithV3Header_Works( + TestImageProvider provider, + BmpBitsPerPixel bitsPerPixel) + where TPixel : unmanaged, IPixel + { + // arrange + var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; + using var memoryStream = new MemoryStream(); + using Image input = provider.GetImage(BmpDecoder); + + // act + encoder.Encode(input, memoryStream); + memoryStream.Position = 0; + + // assert + using var actual = Image.Load(memoryStream); + ImageSimilarityReport similarityReport = ImageComparer.Exact.CompareImagesOrFrames(input, actual); + Assert.True(similarityReport.IsEmpty, "encoded image does not match reference image"); + } + + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + public void Encode_2Bit_WithV4Header_Works( + TestImageProvider provider, + BmpBitsPerPixel bitsPerPixel) + where TPixel : unmanaged, IPixel + { + // arrange + var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; + using var memoryStream = new MemoryStream(); + using Image input = provider.GetImage(BmpDecoder); + + // act + encoder.Encode(input, memoryStream); + memoryStream.Position = 0; + + // assert + using var actual = Image.Load(memoryStream); + ImageSimilarityReport similarityReport = ImageComparer.Exact.CompareImagesOrFrames(input, actual); + Assert.True(similarityReport.IsEmpty, "encoded image does not match reference image"); + } + [Theory] [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel1)] public void Encode_1Bit_WithV3Header_Works( @@ -343,7 +390,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true, // if set to true, will write a V4 header, otherwise a V3 header. IQuantizer quantizer = null, - ImageComparer customComparer = null) + ImageComparer customComparer = null, + IImageDecoder referenceDecoder = null) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage()) @@ -362,7 +410,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp }; // Does DebugSave & load reference CompareToReferenceInput(): - image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, customComparer); + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder, customComparer, referenceDecoder: referenceDecoder); } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 306a28dae..27df82b5f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -344,6 +344,8 @@ namespace SixLabors.ImageSharp.Tests public const string RLE4Delta = "Bmp/pal4rletrns.bmp"; public const string Rle4Delta320240 = "Bmp/rle4-delta-320x240.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; + public const string Bit2 = "Bmp/pal2.bmp"; + public const string Bit2Color = "Bmp/pal2Color.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; public const string Bit8 = "Bmp/test8.bmp"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs index cea2784b6..d2750c31c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -19,10 +19,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison /// A ImageComparer instance. public static ImageComparer Tolerant( float imageThreshold = TolerantImageComparer.DefaultImageThreshold, - int perPixelManhattanThreshold = 0) - { - return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); - } + int perPixelManhattanThreshold = 0) => + new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); /// /// Returns Tolerant(imageThresholdInPercents/100) @@ -45,10 +43,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison Image expected, Image actual) where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel - { - return comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame); - } + where TPixelB : unmanaged, IPixel => comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame); public static IEnumerable> CompareImages( this ImageComparer comparer, diff --git a/tests/Images/Input/Bmp/pal2.bmp b/tests/Images/Input/Bmp/pal2.bmp new file mode 100644 index 000000000..ac351d5fb --- /dev/null +++ b/tests/Images/Input/Bmp/pal2.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bac6eec4100831e635fcd34a9e0e34a8a9082abdec132ac327aa1bfc7137d40f +size 2118 diff --git a/tests/Images/Input/Bmp/pal2color.bmp b/tests/Images/Input/Bmp/pal2color.bmp new file mode 100644 index 000000000..dd7c31bf6 --- /dev/null +++ b/tests/Images/Input/Bmp/pal2color.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ac541592afb207524091aa19d59614851c293193600eacb1170b4854d351dae +size 2118 From 26700fe6b0eb250faaed263b9ad4ef339408e5ff Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 17:07:12 +0200 Subject: [PATCH 43/54] Add decoder tests for 2 bit bitmap's --- .../Formats/Bmp/BmpDecoderTests.cs | 15 +++++++++++++++ .../Formats/Bmp/BmpEncoderTests.cs | 10 ++++------ .../BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png | 3 +++ ...BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png | 3 +++ 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 239e20976..f85219757 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -131,6 +131,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp } } + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32)] + [WithFile(Bit2Color, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_2Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(BmpDecoder)) + { + image.DebugSave(provider); + + // Reference decoder cant decode 2-bit, compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); + } + } + [Theory] [WithFile(Bit4, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index fc9554f6a..665bdd0da 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -22,6 +22,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { private static BmpDecoder BmpDecoder => new(); + private static BmpEncoder BmpEncoder => new(); + public static readonly TheoryData BitsPerPixel = new() { @@ -53,14 +55,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [MemberData(nameof(RatioFiles))] public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var options = new BmpEncoder(); - var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, BmpEncoder); memStream.Position = 0; using (var output = Image.Load(memStream)) @@ -78,14 +78,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [MemberData(nameof(BmpBitsPerPixelFiles))] public void Encode_PreserveBitsPerPixel(string imagePath, BmpBitsPerPixel bmpBitsPerPixel) { - var options = new BmpEncoder(); - var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, BmpEncoder); memStream.Position = 0; using (var output = Image.Load(memStream)) diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png new file mode 100644 index 000000000..4a1ac4088 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c3e8b87af737c40d7be02e55a2aec93bb0e7bd123cd1f3e3b74482a0c7d18bd +size 2376 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png new file mode 100644 index 000000000..6f7bd6869 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3da68e15f4edf6ce5da76360f3704d52baff5292ee12efe5415540b5788dda5 +size 2578 From 3db96c989e70aff06f046d17f5703fcaa35fa31e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 17:21:40 +0200 Subject: [PATCH 44/54] Compare to reference output, when reference decoder is not an option --- .../Formats/Bmp/BmpDecoderTests.cs | 27 ++++++++++--------- ...nDecodeAlphaBitfields_Rgba32_rgba32abf.png | 3 +++ ...der_CanDecode_Os2BitmapArray_Rgba32_9S.png | 3 +++ ...anDecode_Os2BitmapArray_Rgba32_DIAMOND.png | 3 +++ ...anDecode_Os2BitmapArray_Rgba32_GMARBLE.png | 3 +++ ..._CanDecode_Os2BitmapArray_Rgba32_PINES.png | 3 +++ ...CanDecode_Os2BitmapArray_Rgba32_SKATER.png | 3 +++ ..._CanDecode_Os2BitmapArray_Rgba32_SPADE.png | 3 +++ ...anDecode_Os2BitmapArray_Rgba32_SUNFLOW.png | 3 +++ ..._CanDecode_Os2BitmapArray_Rgba32_WARPD.png | 3 +++ ..._CanDecode_Os2BitmapArray_Rgba32_ba-bm.png | 3 +++ ...CanDecode_Os2v2Header_Rgba32_pal8os2v2.png | 3 +++ ..._Os2v2XShortHeader_Rgba32_pal8os2v2-16.png | 3 +++ ...nLengthEncoded_24Bit_Rgba32_rgb24rle24.png | 3 +++ ...LengthEncoded_24Bit_Rgba32_rle24rlecut.png | 3 +++ 15 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png create mode 100644 tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index f85219757..23a849790 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -304,8 +304,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing nor MagickReferenceDecoder decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } @@ -318,8 +319,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing nor MagickReferenceDecoder decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } @@ -592,8 +594,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } @@ -606,10 +609,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { 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()); + // System.Drawing can not decode this image. MagickReferenceDecoder can decode it, + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } @@ -630,8 +632,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } } diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png new file mode 100644 index 000000000..40613ca7e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c7c5d24cf8ba473a22d1c12dcd196f626d2ef056a35bb3ff54b5c84516544bf +size 14547 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png new file mode 100644 index 000000000..0319b9718 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8597af653507fb625a8f387ce01ab900603086892f046b7b92e6fcf60a636295 +size 884 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png new file mode 100644 index 000000000..630b44ecd --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a6989978a0fe36399a774000ee04336d090a4e6a2b63bcbfcd45312ccac4dab +size 648 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png new file mode 100644 index 000000000..ff484218d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:951d9d48a5b5df5b70a8c217e2a3d94f4b2c8e8cc63d70cb807627b8e98b8b1d +size 20567 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png new file mode 100644 index 000000000..78ffbe76c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35dc46a1f19f3f0a91948bee9b173f6ce264ade69754c01b688e2a878f1374a9 +size 21406 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png new file mode 100644 index 000000000..7b6ec01dc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3191c0ac33c1749f770f96814c0585715aa1c0b085f02256317cedeabc531c12 +size 636 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png new file mode 100644 index 000000000..89bf24a22 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50c5f1adb8b9f0f9a111fdd4b04df023d4239d409f93e2ab5823352c02761118 +size 802 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png new file mode 100644 index 000000000..5e7a2c071 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f0f9b6a5f1a36596fbe8ac1416e69af82e24c5892a8012a6b68206b6e467bec +size 14190 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png new file mode 100644 index 000000000..6a62cc9c7 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:452e8aeca41c0899f4e7a4f0458f7cf2dd8002e42a752708d7dd308e040641a0 +size 103892 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png new file mode 100644 index 000000000..2c9fab29f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png new file mode 100644 index 000000000..2c9fab29f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png new file mode 100644 index 000000000..2c9fab29f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png new file mode 100644 index 000000000..2c9fab29f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png new file mode 100644 index 000000000..8311bc95b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13d9b630227069f3fd744ef486d64d3f997ee0a9844824e9986c55d754bf413c +size 4379 From 9aa991884935e69c5acef696d6167ac86dc905d3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 17:30:01 +0200 Subject: [PATCH 45/54] Throw exception, when not enough data could be read --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 51 ++++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 517a3b8cf..74b41bb04 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -832,7 +832,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int offset = 0; Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -884,7 +888,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = 0; y < height; y++) { - this.stream.Read(bufferSpan); + if (this.stream.Read(bufferSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -939,7 +947,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes( @@ -967,7 +979,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( @@ -1003,7 +1019,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp // actually a BGRA image, and change tactics accordingly. for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } PixelOperations.Instance.FromBgra32Bytes( this.Configuration, @@ -1036,7 +1055,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp { for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); @@ -1054,7 +1076,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp // Slow path. We need to set each alpha component value to fully opaque. for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + PixelOperations.Instance.FromBgra32Bytes( this.Configuration, rowSpan, @@ -1115,7 +1141,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = 0; y < height; y++) { - this.stream.Read(bufferSpan); + if (this.stream.Read(bufferSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -1431,7 +1461,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp palette = new byte[colorMapSizeBytes]; - this.stream.Read(palette, 0, colorMapSizeBytes); + if (this.stream.Read(palette, 0, colorMapSizeBytes) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!"); + } } this.infoHeader.VerifyDimensions(); From ee62b83ad937614652c613ee0d50b8f3f1edac51 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 18:03:17 +0200 Subject: [PATCH 46/54] Fix test file name --- tests/ImageSharp.Tests/TestImages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c51ec4acb..0f9479a76 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -348,7 +348,7 @@ namespace SixLabors.ImageSharp.Tests public const string Rle4Delta320240 = "Bmp/rle4-delta-320x240.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; public const string Bit2 = "Bmp/pal2.bmp"; - public const string Bit2Color = "Bmp/pal2Color.bmp"; + public const string Bit2Color = "Bmp/pal2color.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; public const string Bit8 = "Bmp/test8.bmp"; From 9d699c34c85e198e01788d3456ba7b6fbd317007 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 19:21:35 +0200 Subject: [PATCH 47/54] Rename test file --- ...l2Color.png => BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Images/External/ReferenceOutput/BmpDecoderTests/{BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png => BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png} (100%) diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png similarity index 100% rename from tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2Color.png rename to tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png From b3632b1de77146af6cb049b5fdb012d0bb0de40b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 19:51:46 +0200 Subject: [PATCH 48/54] Fix issue #2217 --- .../AdaptiveThresholdProcessor{TPixel}.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index c0df16a66..ecbec84e3 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -133,19 +133,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { Rgba32 rgb = default; Span pixelRow = this.source.DangerousGetRowSpan(y); + int maxX = this.bounds.Width - 1; + int maxY = this.bounds.Height - 1; for (int x = this.startX; x < this.endX; x++) { TPixel pixel = pixelRow[x]; pixel.ToRgba32(ref rgb); - var x1 = Math.Max(x - this.startX - this.clusterSize + 1, 0); - var x2 = Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); - var y1 = Math.Max(y - this.startY - this.clusterSize + 1, 0); - var y2 = Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); + int x1 = Math.Min(Math.Max(x - this.startX - this.clusterSize + 1, 0), maxX); + int x2 = Math.Min(x - this.startX + this.clusterSize + 1, maxX); + int y1 = Math.Min(Math.Max(y - this.startY - this.clusterSize + 1, 0), maxY); + int y2 = Math.Min(y - this.startY + this.clusterSize + 1, maxY); - var count = (uint)((x2 - x1) * (y2 - y1)); - var sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); + uint count = (uint)((x2 - x1) * (y2 - y1)); + long sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.thresholdLimit) { From 3c2f8bfe33c81fa1718c896d7585eabf37cb578e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 31 Aug 2022 20:04:34 +0200 Subject: [PATCH 49/54] Add test for #2217 --- .../Binarization/AdaptiveThresholdTests.cs | 16 ++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 +++ .../Issue_2217_AdaptiveThresholdProcessor.png | 3 +++ 3 files changed, 22 insertions(+) create mode 100644 tests/Images/Input/Png/issues/Issue_2217_AdaptiveThresholdProcessor.png diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index 88ad03949..5c725dbf0 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Binarization; @@ -125,5 +126,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } + + // https://github.com/SixLabors/ImageSharp/issues/2217 + [Theory] + [WithFile(TestImages.Png.Issue2217, PixelTypes.Rgba32)] + public void Issue_2217_AdaptiveThreshold_DoesNotThrowIndexOutOfRangeException( + TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Exception exception = Record.Exception(() => + { + using Image image = provider.GetImage(); + }); + + Assert.Null(exception); + } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 306a28dae..082ba9e03 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -125,6 +125,9 @@ namespace SixLabors.ImageSharp.Tests // Discussion 1875: https://github.com/SixLabors/ImageSharp/discussions/1875 public const string Issue1875 = "Png/raw-profile-type-exif.png"; + // Issue 2217: https://github.com/SixLabors/ImageSharp/issues/2217 + public const string Issue2217 = "Png/issues/Issue_2217_AdaptiveThresholdProcessor.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; diff --git a/tests/Images/Input/Png/issues/Issue_2217_AdaptiveThresholdProcessor.png b/tests/Images/Input/Png/issues/Issue_2217_AdaptiveThresholdProcessor.png new file mode 100644 index 000000000..c8a364782 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2217_AdaptiveThresholdProcessor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0516beb3860c464e9d7bb2e9da678f0ef9bdb5643eeb1675323d5693546c6646 +size 251 From 976f2a8eacfc7c811c3bc329deaca0d85362c864 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Sep 2022 13:27:12 +1000 Subject: [PATCH 50/54] Bypass constantly failing NET7 Win tests --- .github/workflows/build-and-test.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 1a57ec1ed..440131362 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,19 +20,19 @@ jobs: sdk-preview: true runtime: -x64 codecov: false + - os: macos-latest + framework: net7.0 + sdk: 7.0.x + sdk-preview: true + runtime: -x64 + codecov: false # Temp disabled due to runtime preview issues. - #- os: macos-latest + #- os: windows-latest # framework: net7.0 # sdk: 7.0.x # sdk-preview: true # runtime: -x64 # codecov: false - - os: windows-latest - framework: net7.0 - sdk: 7.0.x - sdk-preview: true - runtime: -x64 - codecov: false - os: ubuntu-latest framework: net6.0 sdk: 6.0.x From af9bfe516eeded3e6a52a7e74cf531af86d00f56 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Sep 2022 13:44:08 +1000 Subject: [PATCH 51/54] Mac fix is not released yet --- .github/workflows/build-and-test.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 440131362..cd59dca80 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,13 +20,14 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - - os: macos-latest - framework: net7.0 - sdk: 7.0.x - sdk-preview: true - runtime: -x64 - codecov: false # Temp disabled due to runtime preview issues. + # https://github.com/SixLabors/ImageSharp/issues/2117 + #- os: macos-latest + # framework: net7.0 + # sdk: 7.0.x + # sdk-preview: true + # runtime: -x64 + # codecov: false #- os: windows-latest # framework: net7.0 # sdk: 7.0.x From dac8f712f060aa308079c39cedcf8eea56ab1cbd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Sep 2022 11:20:08 +1000 Subject: [PATCH 52/54] Fix sampling and optimize. --- .../ProcessingExtensions.IntegralImage.cs | 52 +++++++-- .../AdaptiveThresholdProcessor{TPixel}.cs | 104 +++++------------- .../Binarization/AdaptiveThresholdTests.cs | 2 + .../Processing/IntegralImageTests.cs | 53 ++++++++- ...d_WithRectangle_Works_Rgba32_Bradley02.png | 4 +- ...aptiveThreshold_Works_Rgba32_Bradley01.png | 4 +- ...aptiveThreshold_Works_Rgba32_Bradley02.png | 4 +- .../AdaptiveThreshold_Works_Rgba32_ducky.png | 4 +- 8 files changed, 134 insertions(+), 93 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs index ed30c36e7..deed04454 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs @@ -22,26 +22,60 @@ namespace SixLabors.ImageSharp.Processing /// The containing all the sums. public static Buffer2D CalculateIntegralImage(this Image source) where TPixel : unmanaged, IPixel + => CalculateIntegralImage(source.Frames.RootFrame); + + /// + /// Apply an image integral. + /// + /// The image on which to apply the integral. + /// The bounds within the image frame to calculate. + /// The type of the pixel. + /// The containing all the sums. + public static Buffer2D CalculateIntegralImage(this Image source, Rectangle bounds) + where TPixel : unmanaged, IPixel + => CalculateIntegralImage(source.Frames.RootFrame, bounds); + + /// + /// Apply an image integral. + /// + /// The image frame on which to apply the integral. + /// The type of the pixel. + /// The containing all the sums. + public static Buffer2D CalculateIntegralImage(this ImageFrame source) + where TPixel : unmanaged, IPixel + => source.CalculateIntegralImage(source.Bounds()); + + /// + /// Apply an image integral. + /// + /// The image frame on which to apply the integral. + /// The bounds within the image frame to calculate. + /// The type of the pixel. + /// The containing all the sums. + public static Buffer2D CalculateIntegralImage(this ImageFrame source, Rectangle bounds) + where TPixel : unmanaged, IPixel { Configuration configuration = source.GetConfiguration(); - int endY = source.Height; - int endX = source.Width; + var interest = Rectangle.Intersect(bounds, source.Bounds()); + int startY = interest.Y; + int startX = interest.X; + int endY = interest.Height; - Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height); + Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(interest.Width, interest.Height); ulong sumX0 = 0; - Buffer2D sourceBuffer = source.Frames.RootFrame.PixelBuffer; + Buffer2D sourceBuffer = source.PixelBuffer; - using (IMemoryOwner tempRow = configuration.MemoryAllocator.Allocate(source.Width)) + using (IMemoryOwner tempRow = configuration.MemoryAllocator.Allocate(interest.Width)) { Span tempSpan = tempRow.GetSpan(); - Span sourceRow = sourceBuffer.DangerousGetRowSpan(0); + Span sourceRow = sourceBuffer.DangerousGetRowSpan(startY).Slice(startX, tempSpan.Length); Span destRow = intImage.DangerousGetRowSpan(0); PixelOperations.Instance.ToL8(configuration, sourceRow, tempSpan); // First row - for (int x = 0; x < endX; x++) + for (int x = 0; x < tempSpan.Length; x++) { sumX0 += tempSpan[x].PackedValue; destRow[x] = sumX0; @@ -52,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing // All other rows for (int y = 1; y < endY; y++) { - sourceRow = sourceBuffer.DangerousGetRowSpan(y); + sourceRow = sourceBuffer.DangerousGetRowSpan(y + startY).Slice(startX, tempSpan.Length); destRow = intImage.DangerousGetRowSpan(y); PixelOperations.Instance.ToL8(configuration, sourceRow, tempSpan); @@ -62,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing destRow[0] = sumX0 + previousDestRow[0]; // Process all other colmns - for (int x = 1; x < endX; x++) + for (int x = 1; x < tempSpan.Length; x++) { sumX0 += tempSpan[x].PackedValue; destRow[x] = sumX0 + previousDestRow[x]; diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index ecbec84e3..e7c5ad471 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -3,7 +3,6 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -27,70 +26,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// The source area to process for the current processor instance. public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThresholdProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) - { - this.definition = definition; - } + => this.definition = definition; /// protected override void OnFrameApply(ImageFrame source) { - var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); Configuration configuration = this.Configuration; TPixel upper = this.definition.Upper.ToPixel(); TPixel lower = this.definition.Lower.ToPixel(); float thresholdLimit = this.definition.ThresholdLimit; - int startY = intersect.Y; - int endY = intersect.Bottom; - int startX = intersect.X; - int endX = intersect.Right; - - int width = intersect.Width; - int height = intersect.Height; - - // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' - byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); - - Buffer2D sourceBuffer = source.PixelBuffer; - - // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. - using (Buffer2D intImage = this.Configuration.MemoryAllocator.Allocate2D(width, height)) - { - Rgba32 rgb = default; - for (int x = startX; x < endX; x++) - { - ulong sum = 0; - for (int y = startY; y < endY; y++) - { - Span row = sourceBuffer.DangerousGetRowSpan(y); - ref TPixel rowRef = ref MemoryMarshal.GetReference(row); - ref TPixel color = ref Unsafe.Add(ref rowRef, x); - color.ToRgba32(ref rgb); + // ClusterSize defines the size of cluster to used to check for average. + // Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' + byte clusterSize = (byte)Math.Clamp(interest.Width / 16F, 0, 255); - sum += (ulong)(rgb.R + rgb.G + rgb.B); - - if (x - startX != 0) - { - intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; - } - else - { - intImage[x - startX, y - startY] = sum; - } - } - } - - var operation = new RowOperation(intersect, source.PixelBuffer, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); - ParallelRowIterator.IterateRows( - configuration, - intersect, - in operation); - } + using Buffer2D intImage = source.CalculateIntegralImage(interest); + RowOperation operation = new(configuration, interest, source.PixelBuffer, intImage, upper, lower, thresholdLimit, clusterSize); + ParallelRowIterator.IterateRows( + configuration, + interest, + in operation); } - private readonly struct RowOperation : IRowOperation + private readonly struct RowOperation : IRowOperation { + private readonly Configuration configuration; private readonly Rectangle bounds; private readonly Buffer2D source; private readonly Buffer2D intImage; @@ -98,64 +60,58 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization private readonly TPixel lower; private readonly float thresholdLimit; private readonly int startX; - private readonly int endX; private readonly int startY; private readonly byte clusterSize; [MethodImpl(InliningOptions.ShortMethod)] public RowOperation( + Configuration configuration, Rectangle bounds, Buffer2D source, Buffer2D intImage, TPixel upper, TPixel lower, float thresholdLimit, - byte clusterSize, - int startX, - int endX, - int startY) + byte clusterSize) { + this.configuration = configuration; this.bounds = bounds; + this.startX = bounds.X; + this.startY = bounds.Y; this.source = source; this.intImage = intImage; this.upper = upper; this.lower = lower; this.thresholdLimit = thresholdLimit; - this.startX = startX; - this.endX = endX; - this.startY = startY; this.clusterSize = clusterSize; } /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(int y, Span span) { - Rgba32 rgb = default; - Span pixelRow = this.source.DangerousGetRowSpan(y); + Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length); + PixelOperations.Instance.ToL8(this.configuration, rowSpan, span); + int maxX = this.bounds.Width - 1; int maxY = this.bounds.Height - 1; - - for (int x = this.startX; x < this.endX; x++) + for (int x = 0; x < rowSpan.Length; x++) { - TPixel pixel = pixelRow[x]; - pixel.ToRgba32(ref rgb); - - int x1 = Math.Min(Math.Max(x - this.startX - this.clusterSize + 1, 0), maxX); - int x2 = Math.Min(x - this.startX + this.clusterSize + 1, maxX); - int y1 = Math.Min(Math.Max(y - this.startY - this.clusterSize + 1, 0), maxY); + int x1 = Math.Clamp(x - this.clusterSize + 1, 0, maxX); + int x2 = Math.Min(x + this.clusterSize + 1, maxX); + int y1 = Math.Clamp(y - this.startY - this.clusterSize + 1, 0, maxY); int y2 = Math.Min(y - this.startY + this.clusterSize + 1, maxY); uint count = (uint)((x2 - x1) * (y2 - y1)); - long sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); + ulong sum = Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], ulong.MaxValue); - if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.thresholdLimit) + if (span[x].PackedValue * count <= sum * this.thresholdLimit) { - this.source[x, y] = this.lower; + rowSpan[x] = this.lower; } else { - this.source[x, y] = this.upper; + rowSpan[x] = this.upper; } } } diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index 5c725dbf0..c7378bac9 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -137,6 +137,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization Exception exception = Record.Exception(() => { using Image image = provider.GetImage(); + image.Mutate(img => img.AdaptiveThreshold(.5F)); + image.DebugSave(provider); }); Assert.Null(exception); diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs index 330b95a6c..89d8e5330 100644 --- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs +++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs @@ -32,6 +32,30 @@ namespace SixLabors.ImageSharp.Tests.Processing }); } + [Theory] + [WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Ducky, PixelTypes.Rgba32)] + public void CalculateIntegralImage_WithBounds_Rgba32Works(TestImageProvider provider) + { + using Image image = provider.GetImage(); + + Rectangle interest = new(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + // Act: + Buffer2D integralBuffer = image.CalculateIntegralImage(interest); + + // Assert: + VerifySumValues(provider, integralBuffer, interest, (Rgba32 pixel) => + { + L8 outputPixel = default; + + outputPixel.FromRgba32(pixel); + + return outputPixel.PackedValue; + }); + } + [Theory] [WithFile(TestImages.Png.Bradley01, PixelTypes.L8)] [WithFile(TestImages.Png.Bradley02, PixelTypes.L8)] @@ -43,16 +67,41 @@ namespace SixLabors.ImageSharp.Tests.Processing Buffer2D integralBuffer = image.CalculateIntegralImage(); // Assert: - VerifySumValues(provider, integralBuffer, (L8 pixel) => { return pixel.PackedValue; }); + VerifySumValues(provider, integralBuffer, (L8 pixel) => pixel.PackedValue); } + [Theory] + [WithFile(TestImages.Png.Bradley01, PixelTypes.L8)] + [WithFile(TestImages.Png.Bradley02, PixelTypes.L8)] + public void CalculateIntegralImage_WithBounds_L8Works(TestImageProvider provider) + { + using Image image = provider.GetImage(); + + Rectangle interest = new(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + // Act: + Buffer2D integralBuffer = image.CalculateIntegralImage(interest); + + // Assert: + VerifySumValues(provider, integralBuffer, interest, (L8 pixel) => pixel.PackedValue); + } + + private static void VerifySumValues( + TestImageProvider provider, + Buffer2D integralBuffer, + System.Func getPixel) + where TPixel : unmanaged, IPixel + => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds(), getPixel); + private static void VerifySumValues( TestImageProvider provider, Buffer2D integralBuffer, + Rectangle bounds, System.Func getPixel) where TPixel : unmanaged, IPixel { - Image image = provider.GetImage(); + // Image image = provider.GetImage(); + Buffer2DRegion image = provider.GetImage().GetRootFramePixelBuffer().GetRegion(bounds); // Check top-left corner Assert.Equal(getPixel(image[0, 0]), integralBuffer[0, 0]); diff --git a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_WithRectangle_Works_Rgba32_Bradley02.png b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_WithRectangle_Works_Rgba32_Bradley02.png index ea5a333e8..94d50e1ad 100644 --- a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_WithRectangle_Works_Rgba32_Bradley02.png +++ b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_WithRectangle_Works_Rgba32_Bradley02.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36f60abb0ade0320779e242716c61b6dbabc8243a125f0a3145be35e233e117c -size 24542 +oid sha256:5745f61e9b8cd49066b347605deee6dcde17690b9dc0f675466df6b2db706bd6 +size 22348 diff --git a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley01.png b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley01.png index 62660ef4b..5e53399d7 100644 --- a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley01.png +++ b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley01.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c4a92f0ecd0f2ec06b12091b14f2d421605ef178092bf4f7f7cb4e661270945 -size 52876 +oid sha256:7a767913020c3924f0a7ae95b20c064993a2fcdc3007610df6abe6f34c194ef8 +size 1644 diff --git a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley02.png b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley02.png index 7c40f64c0..97a594cf6 100644 --- a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley02.png +++ b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Bradley02.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6d99bcaefa9e344e602465d08714f628b165e7783f73ddb3316e31c3f679825 -size 5760 +oid sha256:e02f5e94b9251be80250926678a2d8bc05318f40c3eff98204e74312ffbca138 +size 2239 diff --git a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_ducky.png b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_ducky.png index 467206ea6..5c19a8421 100644 --- a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_ducky.png +++ b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_ducky.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6826d39280ffd36f075e52cd055975748fedec25a4b58c148b623a6dc6a517f4 -size 2040 +oid sha256:fdd84a24f616d7f06f78ebca01540b59cf1cf8564f442548fe4c8ede6dc1d412 +size 757 From 34371c7a7599450d68865b713e1f7856dbf0dfdd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Sep 2022 11:38:18 +1000 Subject: [PATCH 53/54] Cleanup --- .../Processing/Binarization/AdaptiveThresholdTests.cs | 2 +- tests/ImageSharp.Tests/Processing/IntegralImageTests.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index c7378bac9..6f4214c14 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization Exception exception = Record.Exception(() => { using Image image = provider.GetImage(); - image.Mutate(img => img.AdaptiveThreshold(.5F)); + image.Mutate(img => img.AdaptiveThreshold()); image.DebugSave(provider); }); diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs index 89d8e5330..869f577f7 100644 --- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs +++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs @@ -100,7 +100,6 @@ namespace SixLabors.ImageSharp.Tests.Processing System.Func getPixel) where TPixel : unmanaged, IPixel { - // Image image = provider.GetImage(); Buffer2DRegion image = provider.GetImage().GetRootFramePixelBuffer().GetRegion(bounds); // Check top-left corner From 098a4ae7c000268719bc70975aa0b28b19611ff1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 2 Sep 2022 12:16:07 +0200 Subject: [PATCH 54/54] Compare Issue 2217 to reference output --- .../Binarization/AdaptiveThresholdTests.cs | 19 +------------------ ..._Issue_2217_AdaptiveThresholdProcessor.png | 3 +++ 2 files changed, 4 insertions(+), 18 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Issue_2217_AdaptiveThresholdProcessor.png diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs index 6f4214c14..e9304299d 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Binarization; @@ -103,6 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] [WithFile(TestImages.Png.Ducky, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Issue2217, PixelTypes.Rgba32)] public void AdaptiveThreshold_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -126,22 +126,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization image.CompareToReferenceOutput(ImageComparer.Exact, provider); } } - - // https://github.com/SixLabors/ImageSharp/issues/2217 - [Theory] - [WithFile(TestImages.Png.Issue2217, PixelTypes.Rgba32)] - public void Issue_2217_AdaptiveThreshold_DoesNotThrowIndexOutOfRangeException( - TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - Exception exception = Record.Exception(() => - { - using Image image = provider.GetImage(); - image.Mutate(img => img.AdaptiveThreshold()); - image.DebugSave(provider); - }); - - Assert.Null(exception); - } } } diff --git a/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Issue_2217_AdaptiveThresholdProcessor.png b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Issue_2217_AdaptiveThresholdProcessor.png new file mode 100644 index 000000000..046dec8e5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AdaptiveThresholdTests/AdaptiveThreshold_Works_Rgba32_Issue_2217_AdaptiveThresholdProcessor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9e47d328e9d6d37a5bf090d2f8d748b80d78be936df06f4b3afec0fd9712f39 +size 412