Browse Source

Replace IImageDecoder

pull/2301/head
James Jackson-South 4 years ago
parent
commit
19bdeccd11
  1. 6
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 24
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  3. 12
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  4. 40
      src/ImageSharp/Formats/IImageDecoder.cs
  5. 42
      src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs
  6. 145
      src/ImageSharp/Formats/ImageDecoder.cs
  7. 22
      src/ImageSharp/Formats/ImageDecoderExtensions.cs
  8. 42
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  9. 20
      src/ImageSharp/Formats/ImageFormatManager.cs
  10. 24
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  11. 12
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  12. 39
      src/ImageSharp/Formats/Png/PngDecoder.cs
  13. 12
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  14. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
  15. 12
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  16. 12
      src/ImageSharp/Formats/Webp/WebpDecoder.cs
  17. 16
      src/ImageSharp/Image.Decode.cs
  18. 2
      src/ImageSharp/Image.FromStream.cs
  19. 12
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
  20. 6
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  21. 18
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  22. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  23. 2
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  24. 2
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
  25. 4
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs
  26. 14
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.Fakes.cs
  27. 7
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
  28. 7
      tests/ImageSharp.Tests/TestFile.cs
  29. 19
      tests/ImageSharp.Tests/TestFormat.cs
  30. 16
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
  31. 13
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
  32. 25
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
  33. 17
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  34. 6
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  35. 14
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  36. 2
      tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs
  37. 4
      tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs
  38. 42
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

6
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -248,7 +248,7 @@ internal static class AotCompilerTools
} }
/// <summary> /// <summary>
/// This method pre-seeds the all <see cref="IImageDecoder"/> in the AoT compiler. /// This method pre-seeds the all <see cref="ImageDecoder"/> in the AoT compiler.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve] [Preserve]
@ -280,14 +280,14 @@ internal static class AotCompilerTools
} }
/// <summary> /// <summary>
/// This method pre-seeds the <see cref="IImageDecoder"/> in the AoT compiler. /// This method pre-seeds the <see cref="ImageDecoder"/> in the AoT compiler.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <typeparam name="TDecoder">The decoder.</typeparam> /// <typeparam name="TDecoder">The decoder.</typeparam>
[Preserve] [Preserve]
private static void AotCompileImageDecoder<TPixel, TDecoder>() private static void AotCompileImageDecoder<TPixel, TDecoder>()
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
where TDecoder : class, IImageDecoder where TDecoder : ImageDecoder
=> default(TDecoder).Decode<TPixel>(default, default, default); => default(TDecoder).Decode<TPixel>(default, default, default);
/// <summary> /// <summary>

24
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary> /// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream. /// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary> /// </summary>
public class BmpDecoder : IImageDecoderSpecialized<BmpDecoderOptions> public sealed class BmpDecoder : SpecializedImageDecoder<BmpDecoderOptions>
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -20,27 +20,23 @@ public class BmpDecoder : IImageDecoderSpecialized<BmpDecoderOptions>
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<BmpDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<BmpDecoderOptions>)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image<TPixel> IImageDecoderSpecialized<BmpDecoderOptions>.Decode<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
Image<TPixel> image = new BmpDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Image<TPixel> image = new BmpDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options.GeneralOptions, image); Resize(options.GeneralOptions, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoderSpecialized<BmpDecoderOptions>.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<BmpDecoderOptions>)this).Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
protected internal override BmpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
} }

12
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public sealed class GifDecoder : IImageDecoder public sealed class GifDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -20,7 +20,7 @@ public sealed class GifDecoder : IImageDecoder
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -28,12 +28,12 @@ public sealed class GifDecoder : IImageDecoder
GifDecoderCore decoder = new(options); GifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }

40
src/ImageSharp/Formats/IImageDecoder.cs

@ -1,40 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Encapsulates properties and methods required for decoding an image from a stream.
/// </summary>
public interface IImageDecoder : IImageInfoDetector
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/>.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
}

42
src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs

@ -1,42 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// The base class for all specialized image decoders.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
public interface IImageDecoderSpecialized<T> : IImageDecoder
where T : ISpecializedDecoderOptions
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image Decode(T options, Stream stream, CancellationToken cancellationToken);
}

145
src/ImageSharp/Formats/ImageDecoder.cs

@ -0,0 +1,145 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// The base class for all image decoders.
/// </summary>
public abstract class ImageDecoder
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}" /> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected internal abstract Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image" />.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected internal abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected internal abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// 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.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
protected static void Resize(DecoderOptions options, Image image)
{
if (ShouldResize(options, image))
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Sampler = options.Sampler,
Mode = ResizeMode.Max
};
image.Mutate(x => x.Resize(resizeOptions));
}
}
/// <summary>
/// Determines whether the decoded image should be resized.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
/// <returns><see langword="true"/> if the image should be resized, otherwise; <see langword="false"/>.</returns>
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;
}
}
/// <summary>
/// The base class for all specialized image decoders.
/// Specialized decoders allow for additional options to be passed to the decoder.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
public abstract class SpecializedImageDecoder<T> : ImageDecoder
where T : ISpecializedDecoderOptions
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}" /> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected internal abstract Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image" /> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected internal abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken);
/// <inheritdoc/>
protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<TPixel>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
/// <inheritdoc/>
protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
/// <summary>
/// A factory method for creating the default specialized options.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <returns>The new <typeparamref name="T" />.</returns>
protected internal abstract T CreateDefaultSpecializedOptions(DecoderOptions options);
}

22
src/ImageSharp/Formats/ImageDecoderExtensions.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.Formats;
/// <summary> /// <summary>
/// Extensions methods for <see cref="IImageDecoder"/> and <see cref="IImageDecoderSpecialized{T}"/>. /// Extensions methods for <see cref="ImageDecoder"/> and <see cref="SpecializedImageDecoder{T}"/>.
/// </summary> /// </summary>
public static class ImageDecoderExtensions public static class ImageDecoderExtensions
{ {
@ -18,7 +18,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns> /// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static IImageInfo Identify(this IImageDecoder decoder, DecoderOptions options, Stream stream) public static IImageInfo Identify(this ImageDecoder decoder, DecoderOptions options, Stream stream)
=> Image.WithSeekableStream( => Image.WithSeekableStream(
options, options,
stream, stream,
@ -33,7 +33,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Task{IImageInfo}"/> object.</returns> /// <returns>The <see cref="Task{IImageInfo}"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<IImageInfo> IdentifyAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) public static Task<IImageInfo> IdentifyAsync(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> Image.WithSeekableStreamAsync( => Image.WithSeekableStreamAsync(
options, options,
stream, stream,
@ -49,7 +49,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Image<TPixel> Decode<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream) public static Image<TPixel> Decode<TPixel>(this ImageDecoder decoder, DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStream( => Image.WithSeekableStream(
options, options,
@ -64,7 +64,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Image Decode(this IImageDecoder decoder, DecoderOptions options, Stream stream) public static Image Decode(this ImageDecoder decoder, DecoderOptions options, Stream stream)
=> Image.WithSeekableStream( => Image.WithSeekableStream(
options, options,
stream, stream,
@ -80,7 +80,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image<TPixel>> DecodeAsync<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) public static Task<Image<TPixel>> DecodeAsync<TPixel>(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStreamAsync( => Image.WithSeekableStreamAsync(
options, options,
@ -97,7 +97,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image> DecodeAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) public static Task<Image> DecodeAsync(this ImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> Image.WithSeekableStreamAsync( => Image.WithSeekableStreamAsync(
options, options,
stream, stream,
@ -114,7 +114,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Image<TPixel> Decode<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream) public static Image<TPixel> Decode<T, TPixel>(this SpecializedImageDecoder<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStream( => Image.WithSeekableStream(
@ -131,7 +131,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Image Decode<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream) public static Image Decode<T>(this SpecializedImageDecoder<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions where T : ISpecializedDecoderOptions
=> Image.WithSeekableStream( => Image.WithSeekableStream(
options.GeneralOptions, options.GeneralOptions,
@ -149,7 +149,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image<TPixel>> DecodeAsync<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default) public static Task<Image<TPixel>> DecodeAsync<T, TPixel>(this SpecializedImageDecoder<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default)
where T : ISpecializedDecoderOptions where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStreamAsync( => Image.WithSeekableStreamAsync(
@ -168,7 +168,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image> DecodeAsync<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default) public static Task<Image> DecodeAsync<T>(this SpecializedImageDecoder<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default)
where T : ISpecializedDecoderOptions where T : ISpecializedDecoderOptions
=> Image.WithSeekableStreamAsync( => Image.WithSeekableStreamAsync(
options.GeneralOptions, options.GeneralOptions,

42
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -4,54 +4,14 @@
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats; namespace SixLabors.ImageSharp.Formats;
/// <summary> /// <summary>
/// Utility methods for <see cref="IImageDecoder"/>. /// Utility methods for <see cref="IImageDecoderInternals"/>.
/// </summary> /// </summary>
internal static class ImageDecoderUtilities internal static class ImageDecoderUtilities
{ {
/// <summary>
/// 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.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
public static void Resize(DecoderOptions options, Image image)
{
if (ShouldResize(options, image))
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Sampler = options.Sampler,
Mode = ResizeMode.Max
};
image.Mutate(x => x.Resize(resizeOptions));
}
}
/// <summary>
/// Determines whether the decoded image should be resized.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
/// <returns><see langword="true"/> if the image should be resized, otherwise; <see langword="false"/>.</returns>
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( internal static IImageInfo Identify(
this IImageDecoderInternals decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,

20
src/ImageSharp/Formats/ImageFormatManager.cs

@ -22,9 +22,9 @@ public class ImageFormatManager
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new(); private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types. /// The list of supported <see cref="ImageDecoder"/> keyed to mime types.
/// </summary> /// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new(); private readonly ConcurrentDictionary<IImageFormat, ImageDecoder> mimeTypeDecoders = new();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormat"/>s. /// The list of supported <see cref="IImageFormat"/>s.
@ -59,9 +59,9 @@ public class ImageFormatManager
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors; internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s. /// Gets the currently registered <see cref="ImageDecoder"/>s.
/// </summary> /// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders; internal IEnumerable<KeyValuePair<IImageFormat, ImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s. /// Gets the currently registered <see cref="IImageEncoder"/>s.
@ -122,7 +122,7 @@ public class ImageFormatManager
Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder)); Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat); this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (_, _) => encoder);
} }
/// <summary> /// <summary>
@ -130,12 +130,12 @@ public class ImageFormatManager
/// </summary> /// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param> /// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param> /// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) public void SetDecoder(IImageFormat imageFormat, ImageDecoder decoder)
{ {
Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat); this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (_, _) => decoder);
} }
/// <summary> /// <summary>
@ -158,12 +158,12 @@ public class ImageFormatManager
/// For the specified mime type find the decoder. /// For the specified mime type find the decoder.
/// </summary> /// </summary>
/// <param name="format">The format to discover</param> /// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns> /// <returns>The <see cref="ImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format) public ImageDecoder FindDecoder(IImageFormat format)
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder) return this.mimeTypeDecoders.TryGetValue(format, out ImageDecoder decoder)
? decoder ? decoder
: null; : null;
} }

24
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
/// <summary> /// <summary>
/// Decoder for generating an image out of a jpeg encoded stream. /// Decoder for generating an image out of a jpeg encoded stream.
/// </summary> /// </summary>
public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions> public sealed class JpegDecoder : SpecializedImageDecoder<JpegDecoderOptions>
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -21,15 +21,7 @@ public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image<TPixel> IImageDecoderSpecialized<JpegDecoderOptions>.Decode<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -39,13 +31,17 @@ public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly)
{ {
ImageDecoderUtilities.Resize(options.GeneralOptions, image); Resize(options.GeneralOptions, image);
} }
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoderSpecialized<JpegDecoderOptions>.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode<Rgb24>(options, stream, cancellationToken); => this.Decode<Rgb24>(options, stream, cancellationToken);
/// <inheritdoc/>
protected internal override JpegDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
} }

12
src/ImageSharp/Formats/Pbm/PbmDecoder.cs

@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Formats.Pbm;
/// </list> /// </list>
/// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>. /// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>.
/// </summary> /// </summary>
public sealed class PbmDecoder : IImageDecoder public sealed class PbmDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -36,7 +36,7 @@ public sealed class PbmDecoder : IImageDecoder
} }
/// <inheritdoc /> /// <inheritdoc />
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -44,12 +44,12 @@ public sealed class PbmDecoder : IImageDecoder
PbmDecoderCore decoder = new(options); PbmDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc /> /// <inheritdoc />
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgb24>(options, stream, cancellationToken); => this.Decode<Rgb24>(options, stream, cancellationToken);
} }

39
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Decoder for generating an image out of a png encoded stream. /// Decoder for generating an image out of a png encoded stream.
/// </summary> /// </summary>
public sealed class PngDecoder : IImageDecoder public sealed class PngDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -20,7 +20,7 @@ public sealed class PngDecoder : IImageDecoder
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -28,13 +28,13 @@ public sealed class PngDecoder : IImageDecoder
PngDecoderCore decoder = new(options); PngDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -47,48 +47,47 @@ public sealed class PngDecoder : IImageDecoder
PngColorType color = meta.ColorType.GetValueOrDefault(); PngColorType color = meta.ColorType.GetValueOrDefault();
PngBitDepth bits = meta.BitDepth.GetValueOrDefault(); PngBitDepth bits = meta.BitDepth.GetValueOrDefault();
var imageDecoder = (IImageDecoder)this;
switch (color) switch (color)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
if (bits == PngBitDepth.Bit16) if (bits == PngBitDepth.Bit16)
{ {
return !meta.HasTransparency return !meta.HasTransparency
? imageDecoder.Decode<L16>(options, stream, cancellationToken) ? this.Decode<L16>(options, stream, cancellationToken)
: imageDecoder.Decode<La32>(options, stream, cancellationToken); : this.Decode<La32>(options, stream, cancellationToken);
} }
return !meta.HasTransparency return !meta.HasTransparency
? imageDecoder.Decode<L8>(options, stream, cancellationToken) ? this.Decode<L8>(options, stream, cancellationToken)
: imageDecoder.Decode<La16>(options, stream, cancellationToken); : this.Decode<La16>(options, stream, cancellationToken);
case PngColorType.Rgb: case PngColorType.Rgb:
if (bits == PngBitDepth.Bit16) if (bits == PngBitDepth.Bit16)
{ {
return !meta.HasTransparency return !meta.HasTransparency
? imageDecoder.Decode<Rgb48>(options, stream, cancellationToken) ? this.Decode<Rgb48>(options, stream, cancellationToken)
: imageDecoder.Decode<Rgba64>(options, stream, cancellationToken); : this.Decode<Rgba64>(options, stream, cancellationToken);
} }
return !meta.HasTransparency return !meta.HasTransparency
? imageDecoder.Decode<Rgb24>(options, stream, cancellationToken) ? this.Decode<Rgb24>(options, stream, cancellationToken)
: imageDecoder.Decode<Rgba32>(options, stream, cancellationToken); : this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.Palette: case PngColorType.Palette:
return imageDecoder.Decode<Rgba32>(options, stream, cancellationToken); return this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
return (bits == PngBitDepth.Bit16) return (bits == PngBitDepth.Bit16)
? imageDecoder.Decode<La32>(options, stream, cancellationToken) ? this.Decode<La32>(options, stream, cancellationToken)
: imageDecoder.Decode<La16>(options, stream, cancellationToken); : this.Decode<La16>(options, stream, cancellationToken);
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
return (bits == PngBitDepth.Bit16) return (bits == PngBitDepth.Bit16)
? imageDecoder.Decode<Rgba64>(options, stream, cancellationToken) ? this.Decode<Rgba64>(options, stream, cancellationToken)
: imageDecoder.Decode<Rgba32>(options, stream, cancellationToken); : this.Decode<Rgba32>(options, stream, cancellationToken);
default: default:
return imageDecoder.Decode<Rgba32>(options, stream, cancellationToken); return this.Decode<Rgba32>(options, stream, cancellationToken);
} }
} }
} }

12
src/ImageSharp/Formats/Tga/TgaDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Tga;
/// <summary> /// <summary>
/// Image decoder for Truevision TGA images. /// Image decoder for Truevision TGA images.
/// </summary> /// </summary>
public sealed class TgaDecoder : IImageDecoder public sealed class TgaDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -20,7 +20,7 @@ public sealed class TgaDecoder : IImageDecoder
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -28,12 +28,12 @@ public sealed class TgaDecoder : IImageDecoder
TgaDecoderCore decoder = new(options); TgaDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }

2
src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs

@ -32,7 +32,7 @@ internal class WebpTiffCompression : TiffBaseDecompressor
/// <inheritdoc/> /// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken) protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken)
{ {
using Image<Rgb24> image = ((IImageDecoder)new WebpDecoder()).Decode<Rgb24>(this.options, stream, cancellationToken); using Image<Rgb24> image = new WebpDecoder().Decode<Rgb24>(this.options, stream, cancellationToken);
CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer);
} }

12
src/ImageSharp/Formats/Tiff/TiffDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff;
/// <summary> /// <summary>
/// Image decoder for generating an image out of a TIFF stream. /// Image decoder for generating an image out of a TIFF stream.
/// </summary> /// </summary>
public class TiffDecoder : IImageDecoder public class TiffDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -20,7 +20,7 @@ public class TiffDecoder : IImageDecoder
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -28,12 +28,12 @@ public class TiffDecoder : IImageDecoder
TiffDecoderCore decoder = new(options); TiffDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }

12
src/ImageSharp/Formats/Webp/WebpDecoder.cs

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary> /// <summary>
/// Image decoder for generating an image out of a webp stream. /// Image decoder for generating an image out of a webp stream.
/// </summary> /// </summary>
public sealed class WebpDecoder : IImageDecoder public sealed class WebpDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -21,7 +21,7 @@ public sealed class WebpDecoder : IImageDecoder
} }
/// <inheritdoc/> /// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -29,12 +29,12 @@ public sealed class WebpDecoder : IImageDecoder
using WebpDecoderCore decoder = new(options); using WebpDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image); Resize(options, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }

16
src/ImageSharp/Image.Decode.cs

@ -99,7 +99,7 @@ public abstract partial class Image
/// <param name="stream">The image stream to read the header from.</param> /// <param name="stream">The image stream to read the header from.</param>
/// <param name="format">The IImageFormat.</param> /// <param name="format">The IImageFormat.</param>
/// <returns>The image format or null if none found.</returns> /// <returns>The image format or null if none found.</returns>
private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format) private static ImageDecoder DiscoverDecoder(DecoderOptions options, Stream stream, out IImageFormat format)
{ {
format = InternalDetectFormat(options.Configuration, stream); format = InternalDetectFormat(options.Configuration, stream);
@ -121,7 +121,7 @@ public abstract partial class Image
private static (Image<TPixel> Image, IImageFormat Format) Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) private static (Image<TPixel> Image, IImageFormat Format) Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null) if (decoder is null)
{ {
return (null, null); return (null, null);
@ -133,7 +133,7 @@ public abstract partial class Image
private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{ {
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null) if (decoder is null)
{ {
return (null, null); return (null, null);
@ -154,14 +154,8 @@ public abstract partial class Image
/// </returns> /// </returns>
private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{ {
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format); ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageInfo info = decoder?.Identify(options, stream, cancellationToken);
if (decoder is not IImageInfoDetector detector)
{
return (null, null);
}
IImageInfo info = detector?.Identify(options, stream, cancellationToken);
return (info, format); return (info, format);
} }
} }

2
src/ImageSharp/Image.FromStream.cs

@ -610,7 +610,7 @@ public abstract partial class Image
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine("Image cannot be loaded. Available decoders:"); sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in options.Configuration.ImageFormatsManager.ImageDecoders) foreach (KeyValuePair<IImageFormat, ImageDecoder> val in options.Configuration.ImageFormatsManager.ImageDecoders)
{ {
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
} }

12
tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs

@ -19,19 +19,13 @@ public class IdentifyJpeg
public string TestImage { get; set; } public string TestImage { get; set; }
[GlobalSetup] [GlobalSetup]
public void ReadImages() public void ReadImages() => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath);
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
[Benchmark] [Benchmark]
public IImageInfo Identify() public IImageInfo Identify()
{ {
using var memoryStream = new MemoryStream(this.jpegBytes); using MemoryStream memoryStream = new(this.jpegBytes);
IImageDecoder decoder = new JpegDecoder(); JpegDecoder decoder = new();
return decoder.Identify(DecoderOptions.Default, memoryStream, default); return decoder.Identify(DecoderOptions.Default, memoryStream, default);
} }
} }

6
tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs

@ -278,7 +278,7 @@ public class BmpEncoderTests
// Use the default decoder to test our encoded image. This verifies the content. // 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. // We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile); using FileStream stream = File.OpenRead(actualOutputFile);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default); using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
referenceImage.CompareToReferenceOutput( referenceImage.CompareToReferenceOutput(
@ -309,7 +309,7 @@ public class BmpEncoderTests
// Use the default decoder to test our encoded image. This verifies the content. // 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. // We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile); using FileStream stream = File.OpenRead(actualOutputFile);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default); using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
referenceImage.CompareToReferenceOutput( referenceImage.CompareToReferenceOutput(
@ -378,7 +378,7 @@ public class BmpEncoderTests
bool supportTransparency = true, // if set to true, will write a V4 header, otherwise a V3 header. bool supportTransparency = true, // if set to true, will write a V4 header, otherwise a V3 header.
IQuantizer quantizer = null, IQuantizer quantizer = null,
ImageComparer customComparer = null, ImageComparer customComparer = null,
IImageDecoder referenceDecoder = null) ImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();

18
tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs

@ -64,7 +64,7 @@ public class ImageFormatManagerTests
[Fact] [Fact]
public void RegisterNullSetDecoder() public void RegisterNullSetDecoder()
{ {
Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(null, new Mock<IImageDecoder>().Object)); Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(null, new Mock<ImageDecoder>().Object));
Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null)); Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null));
Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(null, null)); Assert.Throws<ArgumentNullException>(() => this.DefaultFormatsManager.SetDecoder(null, null));
} }
@ -87,14 +87,14 @@ public class ImageFormatManagerTests
[Fact] [Fact]
public void RegisterMimeTypeDecoderReplacesLast() public void RegisterMimeTypeDecoderReplacesLast()
{ {
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object; ImageDecoder decoder1 = new Mock<ImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); ImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found); Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object; ImageDecoder decoder2 = new Mock<ImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); ImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2); Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2); Assert.NotEqual(found, found2);
} }
@ -102,8 +102,8 @@ public class ImageFormatManagerTests
[Fact] [Fact]
public void AddFormatCallsConfig() public void AddFormatCallsConfig()
{ {
var provider = new Mock<IConfigurationModule>(); Mock<IConfigurationModule> provider = new();
var config = new Configuration(); Configuration config = new();
config.Configure(provider.Object); config.Configure(provider.Object);
provider.Verify(x => x.Configure(config)); provider.Verify(x => x.Configure(config));
@ -113,9 +113,9 @@ public class ImageFormatManagerTests
public void DetectFormatAllocatesCleanBuffer() public void DetectFormatAllocatesCleanBuffer()
{ {
byte[] jpegImage; byte[] jpegImage;
using (var buffer = new MemoryStream()) using (MemoryStream buffer = new())
{ {
using var image = new Image<Rgba32>(100, 100); using Image<Rgba32> image = new(100, 100);
image.SaveAsJpeg(buffer); image.SaveAsJpeg(buffer);
jpegImage = buffer.ToArray(); jpegImage = buffer.ToArray();
} }

4
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs

@ -178,7 +178,7 @@ public partial class JpegDecoderTests
Assert.Equal(expectedColorType, meta.ColorType); Assert.Equal(expectedColorType, meta.ColorType);
} }
private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action<IImageInfo> test) private static void TestImageInfo(string imagePath, ImageDecoder decoder, bool useIdentify, Action<IImageInfo> test)
{ {
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
@ -196,7 +196,7 @@ public partial class JpegDecoderTests
private static void TestMetadataImpl( private static void TestMetadataImpl(
bool useIdentify, bool useIdentify,
IImageDecoder decoder, ImageDecoder decoder,
string imagePath, string imagePath,
int expectedPixelSize, int expectedPixelSize,
bool exifProfilePresent, bool exifProfilePresent,

2
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -588,7 +588,7 @@ public partial class PngEncoderTests
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. // Compare to the Magick reference decoder.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
// We compare using both our decoder and the reference decoder as pixel transformation // We compare using both our decoder and the reference decoder as pixel transformation
// occurs within the encoder itself leaving the input image unaffected. // occurs within the encoder itself leaving the input image unaffected.

2
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs

@ -16,7 +16,7 @@ public abstract class TiffDecoderBaseTester
protected static MagickReferenceDecoder ReferenceDecoder => new(); protected static MagickReferenceDecoder ReferenceDecoder => new();
protected static void TestTiffDecoder<TPixel>(TestImageProvider<TPixel> provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) protected static void TestTiffDecoder<TPixel>(TestImageProvider<TPixel> provider, ImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(TiffDecoder); using Image<TPixel> image = provider.GetImage(TiffDecoder);

4
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff;
[Trait("Format", "Tiff")] [Trait("Format", "Tiff")]
public abstract class TiffEncoderBaseTester public abstract class TiffEncoderBaseTester
{ {
protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(); protected static readonly ImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
protected static void TestStripLength<TPixel>( protected static void TestStripLength<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
@ -85,7 +85,7 @@ public abstract class TiffEncoderBaseTester
TiffPredictor predictor = TiffPredictor.None, TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true, bool useExactComparer = true,
float compareTolerance = 0.001f, float compareTolerance = 0.001f,
IImageDecoder imageDecoder = null) ImageDecoder imageDecoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();

14
tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.Fakes.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SixLabors.ImageSharp.Tests;
public partial class ImageTests
{
}

7
tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs

@ -19,7 +19,7 @@ public partial class ImageTests
protected Image<Bgra4444> localStreamReturnImageAgnostic; protected Image<Bgra4444> localStreamReturnImageAgnostic;
protected Mock<IImageDecoder> localDecoder; protected Mock<ImageDecoder> localDecoder;
protected IImageFormatDetector localMimeTypeDetector; protected IImageFormatDetector localMimeTypeDetector;
@ -59,11 +59,10 @@ public partial class ImageTests
this.localImageInfoMock = new Mock<IImageInfo>(); this.localImageInfoMock = new Mock<IImageInfo>();
this.localImageFormatMock = new Mock<IImageFormat>(); this.localImageFormatMock = new Mock<IImageFormat>();
var detector = new Mock<IImageInfoDetector>(); this.localDecoder = new Mock<ImageDecoder>();
detector.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>())) this.localDecoder.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns(this.localImageInfoMock.Object); .Returns(this.localImageInfoMock.Object);
this.localDecoder = detector.As<IImageDecoder>();
this.localDecoder this.localDecoder
.Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>())) .Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((c, s, ct) => .Callback<DecoderOptions, Stream, CancellationToken>((c, s, ct) =>

7
tests/ImageSharp.Tests/TestFile.cs

@ -143,19 +143,22 @@ public sealed class TestFile
/// <summary> /// <summary>
/// Creates a new <see cref="Rgba32"/> image. /// Creates a new <see cref="Rgba32"/> image.
/// </summary> /// </summary>
/// <param name="decoder">The image decoder.</param>
/// <returns> /// <returns>
/// The <see cref="Image{Rgba32}"/>. /// The <see cref="Image{Rgba32}"/>.
/// </returns> /// </returns>
public Image<Rgba32> CreateRgba32Image(IImageDecoder decoder) public Image<Rgba32> CreateRgba32Image(ImageDecoder decoder)
=> this.CreateRgba32Image(decoder, new()); => this.CreateRgba32Image(decoder, new());
/// <summary> /// <summary>
/// Creates a new <see cref="Rgba32"/> image. /// Creates a new <see cref="Rgba32"/> image.
/// </summary> /// </summary>
/// <param name="decoder">The image decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <returns> /// <returns>
/// The <see cref="Image{Rgba32}"/>. /// The <see cref="Image{Rgba32}"/>.
/// </returns> /// </returns>
public Image<Rgba32> CreateRgba32Image(IImageDecoder decoder, DecoderOptions options) public Image<Rgba32> CreateRgba32Image(ImageDecoder decoder, DecoderOptions options)
{ {
options.Configuration = this.Image.GetConfiguration(); options.Configuration = this.Image.GetConfiguration();
using MemoryStream stream = new(this.Bytes); using MemoryStream stream = new(this.Bytes);

19
tests/ImageSharp.Tests/TestFormat.cs

@ -187,7 +187,7 @@ public class TestFormat : IConfigurationModule, IImageFormat
public TestHeader(TestFormat testFormat) => this.testFormat = testFormat; public TestHeader(TestFormat testFormat) => this.testFormat = testFormat;
} }
public class TestDecoder : IImageDecoderSpecialized<TestDecoderOptions> public class TestDecoder : SpecializedImageDecoder<TestDecoderOptions>
{ {
private readonly TestFormat testFormat; private readonly TestFormat testFormat;
@ -201,18 +201,13 @@ public class TestFormat : IConfigurationModule, IImageFormat
public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header); public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header);
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode<TestPixelForAgnosticDecode>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
where TPixel : unmanaged, IPixel<TPixel> => new() { GeneralOptions = options };
=> ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
Configuration configuration = options.GeneralOptions.Configuration; Configuration configuration = options.GeneralOptions.Configuration;
var ms = new MemoryStream(); var ms = new MemoryStream();
@ -229,7 +224,7 @@ public class TestFormat : IConfigurationModule, IImageFormat
return this.testFormat.Sample<TPixel>(); return this.testFormat.Sample<TPixel>();
} }
public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<TestPixelForAgnosticDecode>(options, stream, cancellationToken); => this.Decode<TestPixelForAgnosticDecode>(options, stream, cancellationToken);
} }

16
tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs

@ -27,7 +27,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
public Key( public Key(
PixelTypes pixelType, PixelTypes pixelType,
string filePath, string filePath,
IImageDecoder customDecoder, ImageDecoder customDecoder,
DecoderOptions options, DecoderOptions options,
ISpecializedDecoderOptions specialized) ISpecializedDecoderOptions specialized)
{ {
@ -175,11 +175,11 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
public override Image<TPixel> GetImage() public override Image<TPixel> GetImage()
{ {
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath); ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath);
return this.GetImage(decoder); return this.GetImage(decoder);
} }
public override Image<TPixel> GetImage(IImageDecoder decoder, DecoderOptions options) public override Image<TPixel> GetImage(ImageDecoder decoder, DecoderOptions options)
{ {
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -202,7 +202,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
return cachedImage.Clone(this.Configuration); return cachedImage.Clone(this.Configuration);
} }
public override Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder, DecoderOptions options) public override Task<Image<TPixel>> GetImageAsync(ImageDecoder decoder, DecoderOptions options)
{ {
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -216,7 +216,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
return Task.FromResult(decoder.Decode<TPixel>(options, stream, default)); return Task.FromResult(decoder.Decode<TPixel>(options, stream, default));
} }
public override Image<TPixel> GetImage<T>(IImageDecoderSpecialized<T> decoder, T options) public override Image<TPixel> GetImage<T>(SpecializedImageDecoder<T> decoder, T options)
{ {
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -239,7 +239,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
return cachedImage.Clone(this.Configuration); return cachedImage.Clone(this.Configuration);
} }
public override Task<Image<TPixel>> GetImageAsync<T>(IImageDecoderSpecialized<T> decoder, T options) public override Task<Image<TPixel>> GetImageAsync<T>(SpecializedImageDecoder<T> decoder, T options)
{ {
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -266,7 +266,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
info.AddValue("path", this.FilePath); info.AddValue("path", this.FilePath);
} }
private Image<TPixel> DecodeImage(IImageDecoder decoder, DecoderOptions options) private Image<TPixel> DecodeImage(ImageDecoder decoder, DecoderOptions options)
{ {
options.Configuration = this.Configuration; options.Configuration = this.Configuration;
@ -275,7 +275,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
return decoder.Decode<TPixel>(options, stream, default); return decoder.Decode<TPixel>(options, stream, default);
} }
private Image<TPixel> DecodeImage<T>(IImageDecoderSpecialized<T> decoder, T options) private Image<TPixel> DecodeImage<T>(SpecializedImageDecoder<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new() where T : class, ISpecializedDecoderOptions, new()
{ {
options.GeneralOptions.Configuration = this.Configuration; options.GeneralOptions.Configuration = this.Configuration;

13
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs

@ -87,29 +87,30 @@ public abstract partial class TestImageProvider<TPixel> : ITestImageProvider, IX
/// <returns>A test image.</returns> /// <returns>A test image.</returns>
public abstract Image<TPixel> GetImage(); public abstract Image<TPixel> GetImage();
public Image<TPixel> GetImage(IImageDecoder decoder) public Image<TPixel> GetImage(ImageDecoder decoder)
=> this.GetImage(decoder, new()); => this.GetImage(decoder, new());
public Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder) public Task<Image<TPixel>> GetImageAsync(ImageDecoder decoder)
=> this.GetImageAsync(decoder, new()); => this.GetImageAsync(decoder, new());
public virtual Image<TPixel> GetImage(IImageDecoder decoder, DecoderOptions options) public virtual Image<TPixel> GetImage(ImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!");
public virtual Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder, DecoderOptions options) public virtual Task<Image<TPixel>> GetImageAsync(ImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
public virtual Image<TPixel> GetImage<T>(IImageDecoderSpecialized<T> decoder, T options) public virtual Image<TPixel> GetImage<T>(SpecializedImageDecoder<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new() where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!");
public virtual Task<Image<TPixel>> GetImageAsync<T>(IImageDecoderSpecialized<T> decoder, T options) public virtual Task<Image<TPixel>> GetImageAsync<T>(SpecializedImageDecoder<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new() where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
/// <summary> /// <summary>
/// Returns an <see cref="Image{TPixel}"/> instance to the test case with the necessary traits. /// Returns an <see cref="Image{TPixel}"/> instance to the test case with the necessary traits.
/// </summary> /// </summary>
/// <param name="operationsToApply">The operation to apply to the image before returning.</param>
/// <returns>A test image.</returns> /// <returns>A test image.</returns>
public Image<TPixel> GetImage(Action<IImageProcessingContext> operationsToApply) public Image<TPixel> GetImage(Action<IImageProcessingContext> operationsToApply)
{ {

25
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs

@ -11,7 +11,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
public class MagickReferenceDecoder : IImageDecoder public class MagickReferenceDecoder : ImageDecoder
{ {
private readonly bool validate; private readonly bool validate;
@ -24,24 +24,23 @@ public class MagickReferenceDecoder : IImageDecoder
public static MagickReferenceDecoder Instance { get; } = new(); public static MagickReferenceDecoder Instance { get; } = new();
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{ {
Configuration configuration = options.Configuration; Configuration configuration = options.Configuration;
var bmpReadDefines = new BmpReadDefines BmpReadDefines bmpReadDefines = new()
{ {
IgnoreFileSize = !this.validate IgnoreFileSize = !this.validate
}; };
var settings = new MagickReadSettings(); MagickReadSettings settings = new();
settings.FrameCount = (int)options.MaxFrames; settings.FrameCount = (int)options.MaxFrames;
settings.SetDefines(bmpReadDefines); settings.SetDefines(bmpReadDefines);
using var magickImageCollection = new MagickImageCollection(stream, settings); using MagickImageCollection magickImageCollection = new(stream, settings);
var framesList = new List<ImageFrame<TPixel>>(); List<ImageFrame<TPixel>> framesList = new();
foreach (IMagickImage<ushort> magicFrame in magickImageCollection) foreach (IMagickImage<ushort> magicFrame in magickImageCollection)
{ {
var frame = new ImageFrame<TPixel>(configuration, magicFrame.Width, magicFrame.Height); ImageFrame<TPixel> frame = new(configuration, magicFrame.Width, magicFrame.Height);
framesList.Add(frame); framesList.Add(frame);
MemoryGroup<TPixel> framePixels = frame.PixelBuffer.FastMemoryGroup; MemoryGroup<TPixel> framePixels = frame.PixelBuffer.FastMemoryGroup;
@ -68,10 +67,10 @@ public class MagickReferenceDecoder : IImageDecoder
return new Image<TPixel>(configuration, new ImageMetadata(), framesList); return new Image<TPixel>(configuration, new ImageMetadata(), framesList);
} }
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup) private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup)
@ -83,9 +82,9 @@ public class MagickReferenceDecoder : IImageDecoder
Span<TPixel> destBuffer = m.Span; Span<TPixel> destBuffer = m.Span;
PixelOperations<TPixel>.Instance.FromRgba32( PixelOperations<TPixel>.Instance.FromRgba32(
configuration, configuration,
sourcePixels.Slice(0, destBuffer.Length), sourcePixels[..destBuffer.Length],
destBuffer); destBuffer);
sourcePixels = sourcePixels.Slice(destBuffer.Length); sourcePixels = sourcePixels[destBuffer.Length..];
} }
} }
@ -100,7 +99,7 @@ public class MagickReferenceDecoder : IImageDecoder
rgbaBytes, rgbaBytes,
destBuffer, destBuffer,
destBuffer.Length); destBuffer.Length);
rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 8); rgbaBytes = rgbaBytes[(destBuffer.Length * 8)..];
} }
} }
} }

17
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs

@ -9,31 +9,30 @@ using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
public class SystemDrawingReferenceDecoder : IImageDecoder public class SystemDrawingReferenceDecoder : ImageDecoder
{ {
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
using var sourceBitmap = new SDBitmap(stream); using SDBitmap sourceBitmap = new(stream);
PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
} }
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
using var sourceBitmap = new SDBitmap(stream); using SDBitmap sourceBitmap = new(stream);
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{ {
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sourceBitmap); return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sourceBitmap);
} }
using var convertedBitmap = new SDBitmap( using SDBitmap convertedBitmap = new(
sourceBitmap.Width, sourceBitmap.Width,
sourceBitmap.Height, sourceBitmap.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb); System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(convertedBitmap))
{ {
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
@ -46,6 +45,6 @@ public class SystemDrawingReferenceDecoder : IImageDecoder
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap); return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap);
} }
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }

6
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -20,7 +20,7 @@ public static partial class TestEnvironment
internal static Configuration Configuration => ConfigurationLazy.Value; internal static Configuration Configuration => ConfigurationLazy.Value;
internal static IImageDecoder GetReferenceDecoder(string filePath) internal static ImageDecoder GetReferenceDecoder(string filePath)
{ {
IImageFormat format = GetImageFormat(filePath); IImageFormat format = GetImageFormat(filePath);
return Configuration.ImageFormatsManager.FindDecoder(format); return Configuration.ImageFormatsManager.FindDecoder(format);
@ -42,7 +42,7 @@ public static partial class TestEnvironment
private static void ConfigureCodecs( private static void ConfigureCodecs(
this Configuration cfg, this Configuration cfg,
IImageFormat imageFormat, IImageFormat imageFormat,
IImageDecoder decoder, ImageDecoder decoder,
IImageEncoder encoder, IImageEncoder encoder,
IImageFormatDetector detector) IImageFormatDetector detector)
{ {
@ -53,7 +53,7 @@ public static partial class TestEnvironment
private static Configuration CreateDefaultConfiguration() private static Configuration CreateDefaultConfiguration()
{ {
var cfg = new Configuration( Configuration cfg = new(
new JpegConfigurationModule(), new JpegConfigurationModule(),
new GifConfigurationModule(), new GifConfigurationModule(),
new PbmConfigurationModule(), new PbmConfigurationModule(),

14
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -213,7 +213,7 @@ public static class TestImageExtensions
bool grayscale = false, bool grayscale = false,
bool appendPixelTypeToFileName = true, bool appendPixelTypeToFileName = true,
bool appendSourceFileOrDescription = true, bool appendSourceFileOrDescription = true,
IImageDecoder decoder = null) ImageDecoder decoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> referenceImage = GetReferenceOutputImage<TPixel>( using (Image<TPixel> referenceImage = GetReferenceOutputImage<TPixel>(
@ -306,7 +306,7 @@ public static class TestImageExtensions
string extension = "png", string extension = "png",
bool appendPixelTypeToFileName = true, bool appendPixelTypeToFileName = true,
bool appendSourceFileOrDescription = true, bool appendSourceFileOrDescription = true,
IImageDecoder decoder = null) ImageDecoder decoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
string referenceOutputFile = provider.Utility.GetReferenceOutputFileName( string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(
@ -342,7 +342,7 @@ public static class TestImageExtensions
var temporaryFrameImages = new List<Image<TPixel>>(); var temporaryFrameImages = new List<Image<TPixel>>();
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]); ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]);
foreach (string path in frameFiles) foreach (string path in frameFiles)
{ {
@ -510,7 +510,7 @@ public static class TestImageExtensions
public static Image<TPixel> CompareToOriginal<TPixel>( public static Image<TPixel> CompareToOriginal<TPixel>(
this Image<TPixel> image, this Image<TPixel> image,
ITestImageProvider provider, ITestImageProvider provider,
IImageDecoder referenceDecoder = null) ImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> CompareToOriginal(image, provider, ImageComparer.Tolerant(), referenceDecoder); => CompareToOriginal(image, provider, ImageComparer.Tolerant(), referenceDecoder);
@ -518,7 +518,7 @@ public static class TestImageExtensions
this Image<TPixel> image, this Image<TPixel> image,
ITestImageProvider provider, ITestImageProvider provider,
ImageComparer comparer, ImageComparer comparer,
IImageDecoder referenceDecoder = null, ImageDecoder referenceDecoder = null,
DecoderOptions referenceDecoderOptions = null) DecoderOptions referenceDecoderOptions = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -545,7 +545,7 @@ public static class TestImageExtensions
this Image<TPixel> image, this Image<TPixel> image,
ITestImageProvider provider, ITestImageProvider provider,
ImageComparer comparer, ImageComparer comparer,
IImageDecoder referenceDecoder = null) ImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
string path = TestImageProvider<TPixel>.GetFilePathOrNull(provider); string path = TestImageProvider<TPixel>.GetFilePathOrNull(provider);
@ -667,7 +667,7 @@ public static class TestImageExtensions
ImageComparer customComparer = null, ImageComparer customComparer = null,
bool appendPixelTypeToFileName = true, bool appendPixelTypeToFileName = true,
string referenceImageExtension = null, string referenceImageExtension = null,
IImageDecoder referenceDecoder = null) ImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
string actualOutputFile = provider.Utility.SaveTestOutputFile( string actualOutputFile = provider.Utility.SaveTestOutputFile(

2
tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs

@ -74,7 +74,7 @@ public class ReferenceDecoderBenchmarks
this.BenchmarkDecoderImpl(BmpBenchmarkFiles, new SystemDrawingReferenceDecoder(), "System.Drawing Decode Bmp"); this.BenchmarkDecoderImpl(BmpBenchmarkFiles, new SystemDrawingReferenceDecoder(), "System.Drawing Decode Bmp");
} }
private void BenchmarkDecoderImpl(IEnumerable<string> testFiles, IImageDecoder decoder, string info, int times = DefaultExecutionCount) private void BenchmarkDecoderImpl(IEnumerable<string> testFiles, ImageDecoder decoder, string info, int times = DefaultExecutionCount)
{ {
var measure = new MeasureFixture(this.Output); var measure = new MeasureFixture(this.Output);
measure.Measure( measure.Measure(

4
tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs

@ -79,7 +79,7 @@ public class TestEnvironmentTests
return; return;
} }
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
Assert.IsType(expectedDecoderType, decoder); Assert.IsType(expectedDecoderType, decoder);
} }
@ -113,7 +113,7 @@ public class TestEnvironmentTests
return; return;
} }
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
Assert.IsType(expectedDecoderType, decoder); Assert.IsType(expectedDecoderType, decoder);
} }

42
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -346,7 +346,7 @@ public class TestImageProviderTests
} }
} }
private class TestDecoder : IImageDecoderSpecialized<TestDecoderOptions> private class TestDecoder : SpecializedImageDecoder<TestDecoderOptions>
{ {
// Couldn't make xUnit happy without this hackery: // Couldn't make xUnit happy without this hackery:
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new(); private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -363,26 +363,21 @@ public class TestImageProviderTests
} }
} }
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode<Rgba32>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
=> this.Decode<TPixel>((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken);
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
InvocationCounts[this.callerName]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); return new Image<TPixel>(42, 42);
} }
public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
protected internal override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
internal void InitCaller(string name) internal void InitCaller(string name)
@ -392,7 +387,7 @@ public class TestImageProviderTests
} }
} }
private class TestDecoderWithParameters : IImageDecoderSpecialized<TestDecoderWithParametersOptions> private class TestDecoderWithParameters : SpecializedImageDecoder<TestDecoderWithParametersOptions>
{ {
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new(); private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -408,26 +403,21 @@ public class TestImageProviderTests
} }
} }
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode<Rgba32>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image<TPixel> Decode<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
=> this.Decode<TPixel>((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken);
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
InvocationCounts[this.callerName]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); return new Image<TPixel>(42, 42);
} }
public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) protected internal override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
protected internal override TestDecoderWithParametersOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
internal void InitCaller(string name) internal void InitCaller(string name)

Loading…
Cancel
Save