Browse Source

Replace IImageDecoder

pull/2301/head
James Jackson-South 3 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>
/// 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve]
@ -280,14 +280,14 @@ internal static class AotCompilerTools
}
/// <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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <typeparam name="TDecoder">The decoder.</typeparam>
[Preserve]
private static void AotCompileImageDecoder<TPixel, TDecoder>()
where TPixel : unmanaged, IPixel<TPixel>
where TDecoder : class, IImageDecoder
where TDecoder : ImageDecoder
=> default(TDecoder).Decode<TPixel>(default, default, default);
/// <summary>

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

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary>
public class BmpDecoder : IImageDecoderSpecialized<BmpDecoderOptions>
public sealed class BmpDecoder : SpecializedImageDecoder<BmpDecoderOptions>
{
/// <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(stream, nameof(stream));
@ -20,27 +20,23 @@ public class BmpDecoder : IImageDecoderSpecialized<BmpDecoderOptions>
}
/// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions 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)
protected internal override Image<TPixel> Decode<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
Image<TPixel> image = new BmpDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options.GeneralOptions, image);
Resize(options.GeneralOptions, image);
return image;
}
/// <inheritdoc/>
Image IImageDecoderSpecialized<BmpDecoderOptions>.Decode(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<BmpDecoderOptions>)this).Decode<Rgba32>(options, stream, cancellationToken);
protected internal override Image Decode(BmpDecoderOptions options, Stream stream, CancellationToken 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>
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public sealed class GifDecoder : IImageDecoder
public sealed class GifDecoder : ImageDecoder
{
/// <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(stream, nameof(stream));
@ -20,7 +20,7 @@ public sealed class GifDecoder : IImageDecoder
}
/// <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(stream, nameof(stream));
@ -28,12 +28,12 @@ public sealed class GifDecoder : IImageDecoder
GifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image);
Resize(options, image);
return image;
}
/// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken);
protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken 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;
/// <summary>
/// Extensions methods for <see cref="IImageDecoder"/> and <see cref="IImageDecoderSpecialized{T}"/>.
/// Extensions methods for <see cref="ImageDecoder"/> and <see cref="SpecializedImageDecoder{T}"/>.
/// </summary>
public static class ImageDecoderExtensions
{
@ -18,7 +18,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <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(
options,
stream,
@ -33,7 +33,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Task{IImageInfo}"/> object.</returns>
/// <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(
options,
stream,
@ -49,7 +49,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <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>
=> Image.WithSeekableStream(
options,
@ -64,7 +64,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <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(
options,
stream,
@ -80,7 +80,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <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>
=> Image.WithSeekableStreamAsync(
options,
@ -97,7 +97,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <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(
options,
stream,
@ -114,7 +114,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <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 TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStream(
@ -131,7 +131,7 @@ public static class ImageDecoderExtensions
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <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
=> Image.WithSeekableStream(
options.GeneralOptions,
@ -149,7 +149,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <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 TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStreamAsync(
@ -168,7 +168,7 @@ public static class ImageDecoderExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <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
=> Image.WithSeekableStreamAsync(
options.GeneralOptions,

42
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -4,54 +4,14 @@
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Utility methods for <see cref="IImageDecoder"/>.
/// Utility methods for <see cref="IImageDecoderInternals"/>.
/// </summary>
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(
this IImageDecoderInternals decoder,
Configuration configuration,

20
src/ImageSharp/Formats/ImageFormatManager.cs

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

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

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
/// <summary>
/// Decoder for generating an image out of a jpeg encoded stream.
/// </summary>
public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
public sealed class JpegDecoder : SpecializedImageDecoder<JpegDecoderOptions>
{
/// <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(stream, nameof(stream));
@ -21,15 +21,7 @@ public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
}
/// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions 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)
protected internal override Image<TPixel> Decode<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
@ -39,13 +31,17 @@ public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly)
{
ImageDecoderUtilities.Resize(options.GeneralOptions, image);
Resize(options.GeneralOptions, image);
}
return image;
}
/// <inheritdoc/>
Image IImageDecoderSpecialized<JpegDecoderOptions>.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode<Rgb24>(options, stream, cancellationToken);
protected internal override Image Decode(JpegDecoderOptions options, Stream stream, CancellationToken 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>
/// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>.
/// </summary>
public sealed class PbmDecoder : IImageDecoder
public sealed class PbmDecoder : ImageDecoder
{
/// <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(stream, nameof(stream));
@ -36,7 +36,7 @@ public sealed class PbmDecoder : IImageDecoder
}
/// <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(stream, nameof(stream));
@ -44,12 +44,12 @@ public sealed class PbmDecoder : IImageDecoder
PbmDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image);
Resize(options, image);
return image;
}
/// <inheritdoc />
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgb24>(options, stream, cancellationToken);
protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgb24>(options, stream, cancellationToken);
}

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

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

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

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

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

@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Image decoder for generating an image out of a webp stream.
/// </summary>
public sealed class WebpDecoder : IImageDecoder
public sealed class WebpDecoder : ImageDecoder
{
/// <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(stream, nameof(stream));
@ -21,7 +21,7 @@ public sealed class WebpDecoder : IImageDecoder
}
/// <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(stream, nameof(stream));
@ -29,12 +29,12 @@ public sealed class WebpDecoder : IImageDecoder
using WebpDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options, image);
Resize(options, image);
return image;
}
/// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoder)this).Decode<Rgba32>(options, stream, cancellationToken);
protected internal override Image Decode(DecoderOptions options, Stream stream, CancellationToken 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="format">The IImageFormat.</param>
/// <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);
@ -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)
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)
{
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)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
@ -154,14 +154,8 @@ public abstract partial class Image
/// </returns>
private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is not IImageInfoDetector detector)
{
return (null, null);
}
IImageInfo info = detector?.Identify(options, stream, cancellationToken);
ImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageInfo info = decoder?.Identify(options, stream, cancellationToken);
return (info, format);
}
}

2
src/ImageSharp/Image.FromStream.cs

@ -610,7 +610,7 @@ public abstract partial class Image
StringBuilder sb = new();
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);
}

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

@ -19,19 +19,13 @@ public class IdentifyJpeg
public string TestImage { get; set; }
[GlobalSetup]
public void ReadImages()
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
public void ReadImages() => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath);
[Benchmark]
public IImageInfo Identify()
{
using var memoryStream = new MemoryStream(this.jpegBytes);
IImageDecoder decoder = new JpegDecoder();
using MemoryStream memoryStream = new(this.jpegBytes);
JpegDecoder decoder = new();
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.
// 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 Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
referenceImage.CompareToReferenceOutput(
@ -309,7 +309,7 @@ public class BmpEncoderTests
// Use the default decoder to test our encoded image. This verifies the content.
// We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
ImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
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.
IQuantizer quantizer = null,
ImageComparer customComparer = null,
IImageDecoder referenceDecoder = null)
ImageDecoder referenceDecoder = null)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();

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

@ -64,7 +64,7 @@ public class ImageFormatManagerTests
[Fact]
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(null, null));
}
@ -87,14 +87,14 @@ public class ImageFormatManagerTests
[Fact]
public void RegisterMimeTypeDecoderReplacesLast()
{
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object;
ImageDecoder decoder1 = new Mock<ImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
ImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object;
ImageDecoder decoder2 = new Mock<ImageDecoder>().Object;
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.NotEqual(found, found2);
}
@ -102,8 +102,8 @@ public class ImageFormatManagerTests
[Fact]
public void AddFormatCallsConfig()
{
var provider = new Mock<IConfigurationModule>();
var config = new Configuration();
Mock<IConfigurationModule> provider = new();
Configuration config = new();
config.Configure(provider.Object);
provider.Verify(x => x.Configure(config));
@ -113,9 +113,9 @@ public class ImageFormatManagerTests
public void DetectFormatAllocatesCleanBuffer()
{
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);
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);
}
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);
using var stream = new MemoryStream(testFile.Bytes, false);
@ -196,7 +196,7 @@ public partial class JpegDecoderTests
private static void TestMetadataImpl(
bool useIdentify,
IImageDecoder decoder,
ImageDecoder decoder,
string imagePath,
int expectedPixelSize,
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);
// 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
// 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 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>
{
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")]
public abstract class TiffEncoderBaseTester
{
protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
protected static readonly ImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
protected static void TestStripLength<TPixel>(
TestImageProvider<TPixel> provider,
@ -85,7 +85,7 @@ public abstract class TiffEncoderBaseTester
TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true,
float compareTolerance = 0.001f,
IImageDecoder imageDecoder = null)
ImageDecoder imageDecoder = null)
where TPixel : unmanaged, IPixel<TPixel>
{
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 Mock<IImageDecoder> localDecoder;
protected Mock<ImageDecoder> localDecoder;
protected IImageFormatDetector localMimeTypeDetector;
@ -59,11 +59,10 @@ public partial class ImageTests
this.localImageInfoMock = new Mock<IImageInfo>();
this.localImageFormatMock = new Mock<IImageFormat>();
var detector = new Mock<IImageInfoDetector>();
detector.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
this.localDecoder = new Mock<ImageDecoder>();
this.localDecoder.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns(this.localImageInfoMock.Object);
this.localDecoder = detector.As<IImageDecoder>();
this.localDecoder
.Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((c, s, ct) =>

7
tests/ImageSharp.Tests/TestFile.cs

@ -143,19 +143,22 @@ public sealed class TestFile
/// <summary>
/// Creates a new <see cref="Rgba32"/> image.
/// </summary>
/// <param name="decoder">The image decoder.</param>
/// <returns>
/// The <see cref="Image{Rgba32}"/>.
/// </returns>
public Image<Rgba32> CreateRgba32Image(IImageDecoder decoder)
public Image<Rgba32> CreateRgba32Image(ImageDecoder decoder)
=> this.CreateRgba32Image(decoder, new());
/// <summary>
/// Creates a new <see cref="Rgba32"/> image.
/// </summary>
/// <param name="decoder">The image decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <returns>
/// The <see cref="Image{Rgba32}"/>.
/// </returns>
public Image<Rgba32> CreateRgba32Image(IImageDecoder decoder, DecoderOptions options)
public Image<Rgba32> CreateRgba32Image(ImageDecoder decoder, DecoderOptions options)
{
options.Configuration = this.Image.GetConfiguration();
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 class TestDecoder : IImageDecoderSpecialized<TestDecoderOptions>
public class TestDecoder : SpecializedImageDecoder<TestDecoderOptions>
{
private readonly TestFormat testFormat;
@ -201,18 +201,13 @@ public class TestFormat : IConfigurationModule, IImageFormat
public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header);
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<TestPixelForAgnosticDecode>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
=> ((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);
protected internal override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
public Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
protected internal override Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Configuration configuration = options.GeneralOptions.Configuration;
var ms = new MemoryStream();
@ -229,7 +224,7 @@ public class TestFormat : IConfigurationModule, IImageFormat
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);
}

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

@ -27,7 +27,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
public Key(
PixelTypes pixelType,
string filePath,
IImageDecoder customDecoder,
ImageDecoder customDecoder,
DecoderOptions options,
ISpecializedDecoderOptions specialized)
{
@ -175,11 +175,11 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
public override Image<TPixel> GetImage()
{
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath);
ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath);
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(options, nameof(options));
@ -202,7 +202,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
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(options, nameof(options));
@ -216,7 +216,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
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(options, nameof(options));
@ -239,7 +239,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
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(options, nameof(options));
@ -266,7 +266,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
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;
@ -275,7 +275,7 @@ public abstract partial class TestImageProvider<TPixel> : IXunitSerializable
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()
{
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>
public abstract Image<TPixel> GetImage();
public Image<TPixel> GetImage(IImageDecoder decoder)
public Image<TPixel> GetImage(ImageDecoder decoder)
=> this.GetImage(decoder, new());
public Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder)
public Task<Image<TPixel>> GetImageAsync(ImageDecoder decoder)
=> 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}!");
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}!");
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()
=> 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()
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
/// <summary>
/// Returns an <see cref="Image{TPixel}"/> instance to the test case with the necessary traits.
/// </summary>
/// <param name="operationsToApply">The operation to apply to the image before returning.</param>
/// <returns>A test image.</returns>
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;
public class MagickReferenceDecoder : IImageDecoder
public class MagickReferenceDecoder : ImageDecoder
{
private readonly bool validate;
@ -24,24 +24,23 @@ public class MagickReferenceDecoder : IImageDecoder
public static MagickReferenceDecoder Instance { get; } = new();
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Configuration configuration = options.Configuration;
var bmpReadDefines = new BmpReadDefines
BmpReadDefines bmpReadDefines = new()
{
IgnoreFileSize = !this.validate
};
var settings = new MagickReadSettings();
MagickReadSettings settings = new();
settings.FrameCount = (int)options.MaxFrames;
settings.SetDefines(bmpReadDefines);
using var magickImageCollection = new MagickImageCollection(stream, settings);
var framesList = new List<ImageFrame<TPixel>>();
using MagickImageCollection magickImageCollection = new(stream, settings);
List<ImageFrame<TPixel>> framesList = new();
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);
MemoryGroup<TPixel> framePixels = frame.PixelBuffer.FastMemoryGroup;
@ -68,10 +67,10 @@ public class MagickReferenceDecoder : IImageDecoder
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);
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);
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;
PixelOperations<TPixel>.Instance.FromRgba32(
configuration,
sourcePixels.Slice(0, destBuffer.Length),
sourcePixels[..destBuffer.Length],
destBuffer);
sourcePixels = sourcePixels.Slice(destBuffer.Length);
sourcePixels = sourcePixels[destBuffer.Length..];
}
}
@ -100,7 +99,7 @@ public class MagickReferenceDecoder : IImageDecoder
rgbaBytes,
destBuffer,
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;
public class SystemDrawingReferenceDecoder : IImageDecoder
public class SystemDrawingReferenceDecoder : ImageDecoder
{
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));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
}
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
protected internal override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using var sourceBitmap = new SDBitmap(stream);
using SDBitmap sourceBitmap = new(stream);
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sourceBitmap);
}
using var convertedBitmap = new SDBitmap(
using SDBitmap convertedBitmap = new(
sourceBitmap.Width,
sourceBitmap.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(convertedBitmap))
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(convertedBitmap))
{
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
@ -46,6 +45,6 @@ public class SystemDrawingReferenceDecoder : IImageDecoder
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);
}

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

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

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

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

@ -79,7 +79,7 @@ public class TestEnvironmentTests
return;
}
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
Assert.IsType(expectedDecoderType, decoder);
}
@ -113,7 +113,7 @@ public class TestEnvironmentTests
return;
}
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
ImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName);
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:
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -363,26 +363,21 @@ public class TestImageProviderTests
}
}
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>((TestDecoderOptions)new() { GeneralOptions = options }, stream, cancellationToken);
protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions 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>
protected internal override Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
InvocationCounts[this.callerName]++;
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);
protected internal override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
internal void InitCaller(string name)
@ -392,7 +387,7 @@ public class TestImageProviderTests
}
}
private class TestDecoderWithParameters : IImageDecoderSpecialized<TestDecoderWithParametersOptions>
private class TestDecoderWithParameters : SpecializedImageDecoder<TestDecoderWithParametersOptions>
{
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -408,26 +403,21 @@ public class TestImageProviderTests
}
}
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>((TestDecoderWithParametersOptions)new() { GeneralOptions = options }, stream, cancellationToken);
protected internal override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
public Image<TPixel> Decode<TPixel>(DecoderOptions 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>
protected internal override Image<TPixel> Decode<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
{
InvocationCounts[this.callerName]++;
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);
protected internal override TestDecoderWithParametersOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
internal void InitCaller(string name)

Loading…
Cancel
Save