Browse Source

Implement specialized options for limited types, add extensions.

pull/2180/head
James Jackson-South 4 years ago
parent
commit
d29cf8abf4
  1. 2
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 34
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  3. 17
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  4. 30
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  5. 12
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  6. 14
      src/ImageSharp/Formats/Gif/GifDecoderOptions.cs
  7. 6
      src/ImageSharp/Formats/IImageDecoder.cs
  8. 10
      src/ImageSharp/Formats/IImageDecoderInternals.cs
  9. 39
      src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs
  10. 3
      src/ImageSharp/Formats/IImageInfoDetector.cs
  11. 65
      src/ImageSharp/Formats/ImageDecoder.cs
  12. 82
      src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs
  13. 17
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  14. 119
      src/ImageSharp/Formats/ImageDecoder{T}.cs
  15. 47
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  16. 14
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  17. 5
      src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs
  18. 27
      src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs
  19. 32
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  20. 8
      src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
  21. 14
      src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs
  22. 63
      src/ImageSharp/Formats/Png/PngDecoder.cs
  23. 14
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  24. 14
      src/ImageSharp/Formats/Png/PngDecoderOptions.cs
  25. 30
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  26. 8
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  27. 14
      src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs
  28. 4
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs
  29. 30
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  30. 16
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  31. 14
      src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs
  32. 31
      src/ImageSharp/Formats/Webp/WebpDecoder.cs
  33. 12
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  34. 14
      src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs
  35. 4
      src/ImageSharp/Image.FromStream.cs
  36. 1
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  37. 17
      tests/ImageSharp.Tests/TestFormat.cs
  38. 6
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
  39. 4
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
  40. 19
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
  41. 15
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  42. 34
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

2
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Advanced
}
/// <summary>
/// This method pre-seeds the all <see cref="IImageDecoderInternals{T}"/> in the AoT compiler.
/// This method pre-seeds the all <see cref="IImageDecoderInternals"/> in the AoT compiler.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve]

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

@ -10,31 +10,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary>
public class BmpDecoder : ImageDecoder<BmpDecoderOptions>
public class BmpDecoder : ImageDecoder, IImageDecoderSpecialized<BmpDecoderOptions>
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
BmpDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<BmpDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
return new BmpDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> DecodeSpecialized<TPixel>(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
Image<TPixel> image = new BmpDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
}
/// <inheritdoc/>
public Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
}
}

17
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks>
internal sealed class BmpDecoderCore : IImageDecoderInternals<BmpDecoderOptions>
internal sealed class BmpDecoderCore : IImageDecoderInternals
{
/// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps.
@ -99,19 +99,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// How to deal with skipped pixels,
/// which can occur during decoding run length encoded bitmaps.
/// </summary>
private readonly RleSkippedPixelHandling rleSkippedPixelHandling;
/// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary>
/// <param name="options">The options.</param>
public BmpDecoderCore(BmpDecoderOptions options)
{
this.Options = options;
this.Options = options.GeneralOptions;
this.rleSkippedPixelHandling = options.RleSkippedPixelHandling;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc />
public BmpDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc />
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);
@ -322,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
byte colorIdx = bufferRow[x];
if (undefinedPixelsSpan[rowStartIdx + x])
{
switch (this.Options.RleSkippedPixelHandling)
switch (this.rleSkippedPixelHandling)
{
case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4]));
@ -394,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int idx = rowStartIdx + (x * 3);
if (undefinedPixelsSpan[yMulWidth + x])
{
switch (this.Options.RleSkippedPixelHandling)
switch (this.rleSkippedPixelHandling)
{
case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));

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

@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public sealed class GifDecoder : ImageDecoder<GifDecoderOptions>
public sealed class GifDecoder : ImageDecoder
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
GifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<GifDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return image;
return new GifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new GifDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
GifDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
}
}

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
internal sealed class GifDecoderCore : IImageDecoderInternals<GifDecoderOptions>
internal sealed class GifDecoderCore : IImageDecoderInternals
{
/// <summary>
/// The temp buffer used to reduce allocations.
@ -90,17 +90,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public GifDecoderCore(GifDecoderOptions options)
public GifDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc />
public GifDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc />
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);

14
src/ImageSharp/Formats/Gif/GifDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Configuration options for decoding Gif images.
/// </summary>
public sealed class GifDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

6
src/ImageSharp/Formats/IImageDecoder.cs

@ -15,6 +15,9 @@ namespace SixLabors.ImageSharp.Formats
/// <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>
@ -27,6 +30,9 @@ namespace SixLabors.ImageSharp.Formats
/// <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>

10
src/ImageSharp/Formats/IImageDecoderInternals{T}.cs → src/ImageSharp/Formats/IImageDecoderInternals.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -11,14 +11,12 @@ namespace SixLabors.ImageSharp.Formats
/// <summary>
/// Abstraction for shared internals for XXXDecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary>
/// <typeparam name="T">The type of specialized decoder options.</typeparam>
internal interface IImageDecoderInternals<T>
where T : ISpecializedDecoderOptions
internal interface IImageDecoderInternals
{
/// <summary>
/// Gets the specialized decoder options.
/// Gets the general decoder options.
/// </summary>
T Options { get; }
DecoderOptions Options { get; }
/// <summary>
/// Gets the dimensions of the image being decoded.

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

@ -0,0 +1,39 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
/// <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>
/// <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> DecodeSpecialized<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>
/// <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 DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken);
}
}

3
src/ImageSharp/Formats/IImageInfoDetector.cs

@ -14,6 +14,9 @@ namespace SixLabors.ImageSharp.Formats
/// <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>

65
src/ImageSharp/Formats/ImageDecoder.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// The base class for all image decoders.
/// </summary>
public abstract class ImageDecoder : IImageInfoDetector, IImageDecoder
{
/// <inheritdoc/>
public abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <inheritdoc/>
public abstract Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <inheritdoc/>
public abstract Image Decode(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 = KnownResamplers.Box,
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;
}
}
}

82
src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Extensions methods for <see cref="IImageDecoderSpecialized{T}"/>.
/// </summary>
public static class ImageDecoderSpecializedExtensions
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The specialized decoder options.</param>
/// <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> DecodeSpecialized<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel>
=> decoder.DecodeSpecialized<TPixel>(options, stream, default);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The specialized decoder options.</param>
/// <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 DecodeSpecialized<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions
=> decoder.DecodeSpecialized(options, stream, default);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <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>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>> DecodeSpecializedAsync<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => decoder.DecodeSpecialized<TPixel>(options, s, ct),
cancellationToken);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <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>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> DecodeSpecializedAsync<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default)
where T : ISpecializedDecoderOptions
=> Image.WithSeekableStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => decoder.DecodeSpecialized(options, s, ct),
cancellationToken);
}
}

17
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -12,12 +12,11 @@ namespace SixLabors.ImageSharp.Formats
{
internal static class ImageDecoderUtilities
{
public static IImageInfo Identify<T>(
this IImageDecoderInternals<T> decoder,
public static IImageInfo Identify(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
{
using var bufferedReadStream = new BufferedReadStream(configuration, stream);
@ -31,22 +30,20 @@ namespace SixLabors.ImageSharp.Formats
}
}
public static Image<TPixel> Decode<T, TPixel>(
this IImageDecoderInternals<T> decoder,
public static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel>
=> decoder.Decode<T, TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
=> decoder.Decode<TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
public static Image<TPixel> Decode<T, TPixel>(
this IImageDecoderInternals<T> decoder,
public static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory,
CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel>
{
using var bufferedReadStream = new BufferedReadStream(configuration, stream);

119
src/ImageSharp/Formats/ImageDecoder{T}.cs

@ -1,119 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// The base class for all image decoders.
/// </summary>
/// <typeparam name="T">The type of specialized decoder options.</typeparam>
public abstract class ImageDecoder<T> : IImageInfoDetector, IImageDecoder
where T : class, ISpecializedDecoderOptions, new()
{
/// <inheritdoc/>
public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
T specializedOptions = new() { GeneralOptions = options };
return this.IdentifySpecialized(specializedOptions, stream, cancellationToken);
}
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
T specializedOptions = new() { GeneralOptions = options };
Image<TPixel> image = this.DecodeSpecialized<TPixel>(specializedOptions, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <inheritdoc/>
public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
T specializedOptions = new() { GeneralOptions = options };
Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <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="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public abstract IImageInfo IdentifySpecialized(T options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <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 abstract Image<TPixel> DecodeSpecialized<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>
/// <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 abstract Image DecodeSpecialized(T 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 = KnownResamplers.Box,
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;
}
}
}

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

@ -10,38 +10,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Decoder for generating an image out of a jpeg encoded stream.
/// </summary>
public sealed class JpegDecoder : ImageDecoder<JpegDecoderOptions>
public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized<JpegDecoderOptions>
{
/// <inheritdoc/>
/// <remarks>
/// Unlike <see cref="IImageDecoder.Decode{TPixel}(DecoderOptions, Stream, CancellationToken)"/>, when
/// <see cref="DecoderOptions.TargetSize"/> is passed, the codec may not be able to scale efficiently to
/// the exact scale factor requested, so returns a size that approximates that scale.
/// Upscaling is not supported, so the original size will be returned.
/// </remarks>
public override Image<TPixel> DecodeSpecialized<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using JpegDecoderCore decoder = new(options);
return decoder.Decode<JpegDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Guard.NotNull(stream, nameof(stream));
using JpegDecoderCore decoder = new(new() { GeneralOptions = options });
return decoder.Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
/// <remarks>
/// Unlike <see cref="IImageDecoder.Decode(DecoderOptions, Stream, CancellationToken)"/>, when
/// <see cref="DecoderOptions.TargetSize"/> is passed, the codec may not be able to scale efficiently to
/// the exact scale factor requested, so returns a size that approximates that scale.
/// Upscaling is not supported, so the original size will be returned.
/// </remarks>
public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> DecodeSpecialized<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
using JpegDecoderCore decoder = new(options);
return decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
Image<TPixel> image = decoder.Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly)
{
Resize(options.GeneralOptions, image);
}
return image;
}
/// <inheritdoc/>
public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken);
}
}

14
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/>
/// with additional fixes for both performance and common encoding errors.
/// </summary>
internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals<JpegDecoderOptions>
internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
{
/// <summary>
/// The only supported precision
@ -125,19 +125,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
private readonly bool skipMetadata;
/// <summary>
/// The jpeg specific resize options.
/// </summary>
private readonly JpegDecoderResizeMode resizeMode;
/// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public JpegDecoderCore(JpegDecoderOptions options)
{
this.Options = options;
this.Options = options.GeneralOptions;
this.resizeMode = options.ResizeMode;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
}
/// <inheritdoc />
public JpegDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => this.Frame.PixelSize;
@ -207,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
using var spectralConverter = new SpectralConverter<TPixel>(this.configuration, this.Options.GeneralOptions.TargetSize);
using var spectralConverter = new SpectralConverter<TPixel>(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize);
this.ParseStream(stream, spectralConverter, cancellationToken);
this.InitExifProfile();
this.InitIccProfile();

5
src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs

@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
public sealed class JpegDecoderOptions : ISpecializedDecoderOptions
{
/// <summary>
/// Gets or sets the resize mode.
/// </summary>
public JpegDecoderResizeMode ResizeMode { get; set; }
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}

27
src/ImageSharp/Formats/Jpeg/JpegDecoderResizeMode.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Provides enumeration for resize modes taken during decoding.
/// Applicable only when <see cref="DecoderOptions.TargetSize"/> has a value.
/// </summary>
public enum JpegDecoderResizeMode
{
/// <summary>
/// Both <see cref="IdctOnly"/> and <see cref="ScaleOnly"/>.
/// </summary>
Combined,
/// <summary>
/// IDCT-only to nearest block scale.
/// </summary>
IdctOnly,
/// <summary>
/// Opt-out the IDCT part and only Resize. Can be useful in case of quality concerns.
/// </summary>
ScaleOnly
}
}

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

@ -26,29 +26,33 @@ 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 : ImageDecoder<PbmDecoderOptions>
public sealed class PbmDecoder : ImageDecoder
{
/// <inheritdoc />
public override Image<TPixel> DecodeSpecialized<TPixel>(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken)
/// <inheritdoc/>
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
PbmDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<PbmDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return image;
return new PbmDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc />
public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new PbmDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
PbmDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <inheritdoc />
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgb24>(options, stream, cancellationToken);
}
}

8
src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm
/// <summary>
/// Performs the PBM decoding operation.
/// </summary>
internal sealed class PbmDecoderCore : IImageDecoderInternals<PbmDecoderOptions>
internal sealed class PbmDecoderCore : IImageDecoderInternals
{
private int maxPixelValue;
@ -52,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Pbm
/// Initializes a new instance of the <see cref="PbmDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public PbmDecoderCore(PbmDecoderOptions options)
public PbmDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.configuration = options.Configuration;
}
/// <inheritdoc/>
public PbmDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => this.pixelSize;

14
src/ImageSharp/Formats/Pbm/PbmDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
/// <summary>
/// Configuration options for decoding Pbm images.
/// </summary>
public sealed class PbmDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

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

@ -10,24 +10,39 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Decoder for generating an image out of a png encoded stream.
/// </summary>
public sealed class PngDecoder : ImageDecoder<PngDecoderOptions>
public sealed class PngDecoder : ImageDecoder
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new PngDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
PngDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<PngDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
Resize(options, image);
return image;
}
/// <inheritdoc/>
public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
PngDecoderCore decoder = new(options, true);
IImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
IImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken);
stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata();
@ -39,50 +54,42 @@ namespace SixLabors.ImageSharp.Formats.Png
if (bits == PngBitDepth.Bit16)
{
return !meta.HasTransparency
? this.DecodeSpecialized<L16>(options, stream, cancellationToken)
: this.DecodeSpecialized<La32>(options, stream, cancellationToken);
? this.Decode<L16>(options, stream, cancellationToken)
: this.Decode<La32>(options, stream, cancellationToken);
}
return !meta.HasTransparency
? this.DecodeSpecialized<L8>(options, stream, cancellationToken)
: this.DecodeSpecialized<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
? this.DecodeSpecialized<Rgb48>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken);
? this.Decode<Rgb48>(options, stream, cancellationToken)
: this.Decode<Rgba64>(options, stream, cancellationToken);
}
return !meta.HasTransparency
? this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
? this.Decode<Rgb24>(options, stream, cancellationToken)
: this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.Palette:
return this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
return this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.GrayscaleWithAlpha:
return (bits == PngBitDepth.Bit16)
? this.DecodeSpecialized<La32>(options, stream, cancellationToken)
: this.DecodeSpecialized<La16>(options, stream, cancellationToken);
? this.Decode<La32>(options, stream, cancellationToken)
: this.Decode<La16>(options, stream, cancellationToken);
case PngColorType.RgbWithAlpha:
return (bits == PngBitDepth.Bit16)
? this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
? this.Decode<Rgba64>(options, stream, cancellationToken)
: this.Decode<Rgba32>(options, stream, cancellationToken);
default:
return this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
return this.Decode<Rgba32>(options, stream, cancellationToken);
}
}
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
return new PngDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
}
}
}

14
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Performs the png decoding operation.
/// </summary>
internal sealed class PngDecoderCore : IImageDecoderInternals<PngDecoderOptions>
internal sealed class PngDecoderCore : IImageDecoderInternals
{
/// <summary>
/// Reusable buffer.
@ -123,25 +123,25 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public PngDecoderCore(PngDecoderOptions options)
public PngDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly)
{
this.Options = options;
this.colorMetadataOnly = colorMetadataOnly;
this.skipMetadata = true;
this.configuration = options.GeneralOptions.Configuration;
this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc/>
public PngDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => new(this.header.Width, this.header.Height);

14
src/ImageSharp/Formats/Png/PngDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Configuration options for decoding Png images.
/// </summary>
public sealed class PngDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

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

@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Image decoder for Truevision TGA images.
/// </summary>
public sealed class TgaDecoder : ImageDecoder<TgaDecoderOptions>
public sealed class TgaDecoder : ImageDecoder
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
TgaDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TgaDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return image;
return new TgaDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new TgaDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
TgaDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
}
}

8
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Performs the tga decoding operation.
/// </summary>
internal sealed class TgaDecoderCore : IImageDecoderInternals<TgaDecoderOptions>
internal sealed class TgaDecoderCore : IImageDecoderInternals
{
/// <summary>
/// A scratch buffer to reduce allocations.
@ -61,15 +61,15 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// Initializes a new instance of the <see cref="TgaDecoderCore"/> class.
/// </summary>
/// <param name="options">The options.</param>
public TgaDecoderCore(TgaDecoderOptions options)
public TgaDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc />
public TgaDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc />
public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height);

14
src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// Configuration options for decoding Tga images.
/// </summary>
public sealed class TgaDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

4
src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs

@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision);
}
/// <remarks>
/// <summary>
/// This converter must be used only for RGB and YCbCr color spaces for performance reasons.
/// For grayscale images <see cref="GrayJpegSpectralConverter{TPixel}"/> must be used.
/// </remarks>
/// </summary>
private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation)
=> interpretation switch
{

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

@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <summary>
/// Image decoder for generating an image out of a TIFF stream.
/// </summary>
public class TiffDecoder : ImageDecoder<TiffDecoderOptions>
public class TiffDecoder : ImageDecoder
{
/// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
TiffDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TiffDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return image;
return new TiffDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new TiffDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
TiffDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Resize(options, image);
return image;
}
/// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
}
}

16
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <summary>
/// Performs the tiff decoding operation.
/// </summary>
internal class TiffDecoderCore : IImageDecoderInternals<TiffDecoderOptions>
internal class TiffDecoderCore : IImageDecoderInternals
{
/// <summary>
/// General configuration options.
@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// Initializes a new instance of the <see cref="TiffDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public TiffDecoderCore(TiffDecoderOptions options)
public TiffDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffPredictor Predictor { get; set; }
/// <inheritdoc/>
public TiffDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions { get; private set; }
@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(
this.Options.GeneralOptions,
this.Options,
this.CompressionType,
this.memoryAllocator,
this.PhotometricInterpretation,
@ -454,7 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Buffer2D<TPixel> pixels = frame.PixelBuffer;
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(
this.Options.GeneralOptions,
this.Options,
this.CompressionType,
this.memoryAllocator,
this.PhotometricInterpretation,

14
src/ImageSharp/Formats/Tiff/TiffDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Configuration options for decoding Tiff images.
/// </summary>
public sealed class TiffDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

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

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

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

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <summary>
/// Performs the webp decoding operation.
/// </summary>
internal sealed class WebpDecoderCore : IImageDecoderInternals<WebpDecoderOptions>, IDisposable
internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
{
/// <summary>
/// Reusable buffer.
@ -76,17 +76,17 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Initializes a new instance of the <see cref="WebpDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public WebpDecoderCore(WebpDecoderOptions options)
public WebpDecoderCore(DecoderOptions options)
{
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc/>
public WebpDecoderOptions Options { get; }
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height);

14
src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Webp
{
/// <summary>
/// Configuration options for decoding Webp images.
/// </summary>
public sealed class WebpDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

4
src/ImageSharp/Image.FromStream.cs

@ -521,7 +521,7 @@ namespace SixLabors.ImageSharp
/// <param name="stream">The input stream.</param>
/// <param name="action">The action to perform.</param>
/// <returns>The <typeparamref name="T"/>.</returns>
private static T WithSeekableStream<T>(
internal static T WithSeekableStream<T>(
DecoderOptions options,
Stream stream,
Func<Stream, T> action)
@ -562,7 +562,7 @@ namespace SixLabors.ImageSharp
/// <param name="action">The action to perform.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The <see cref="Task{T}"/>.</returns>
private static async Task<T> WithSeekableStreamAsync<T>(
internal static async Task<T> WithSeekableStreamAsync<T>(
DecoderOptions options,
Stream stream,
Func<Stream, CancellationToken, T> action,

1
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs

@ -65,7 +65,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
}
}
/*
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1348 (20H2/October2020Update)
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores

17
tests/ImageSharp.Tests/TestFormat.cs

@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests
public TestHeader(TestFormat testFormat) => this.testFormat = testFormat;
}
public class TestDecoder : ImageDecoder<TestDecoderOptions>
public class TestDecoder : ImageDecoder, IImageDecoderSpecialized<TestDecoderOptions>
{
private readonly TestFormat testFormat;
@ -208,10 +208,17 @@ namespace SixLabors.ImageSharp.Tests
public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header);
public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(options, stream, cancellationToken);
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Configuration configuration = options.GeneralOptions.Configuration;
var ms = new MemoryStream();
@ -228,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests
return this.testFormat.Sample<TPixel>();
}
public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(options, stream, cancellationToken);
}

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

@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests
return Task.FromResult(decoder.Decode<TPixel>(options, stream, default));
}
public override Image<TPixel> GetImage<T>(ImageDecoder<T> decoder, T options)
public override Image<TPixel> GetImage<T>(IImageDecoderSpecialized<T> decoder, T options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests
return cachedImage.Clone(this.Configuration);
}
public override Task<Image<TPixel>> GetImageAsync<T>(ImageDecoder<T> decoder, T options)
public override Task<Image<TPixel>> GetImageAsync<T>(IImageDecoderSpecialized<T> decoder, T options)
{
Guard.NotNull(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options));
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests
return decoder.Decode<TPixel>(options, stream, default);
}
private Image<TPixel> DecodeImage<T>(ImageDecoder<T> decoder, T options)
private Image<TPixel> DecodeImage<T>(IImageDecoderSpecialized<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new()
{
options.GeneralOptions.Configuration = this.Configuration;

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

@ -101,11 +101,11 @@ namespace SixLabors.ImageSharp.Tests
public virtual Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
public virtual Image<TPixel> GetImage<T>(ImageDecoder<T> decoder, T options)
public virtual Image<TPixel> GetImage<T>(IImageDecoderSpecialized<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>(ImageDecoder<T> decoder, T options)
public virtual Task<Image<TPixel>> GetImageAsync<T>(IImageDecoderSpecialized<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");

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

@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
public class MagickReferenceDecoder : ImageDecoder<MagickReferenceDecoderOptions>
public class MagickReferenceDecoder : ImageDecoder
{
private readonly bool validate;
@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
public static MagickReferenceDecoder Instance { get; } = new();
public override Image<TPixel> DecodeSpecialized<TPixel>(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Configuration configuration = options.GeneralOptions.Configuration;
Configuration configuration = options.Configuration;
var bmpReadDefines = new BmpReadDefines
{
IgnoreFileSize = !this.validate
@ -70,11 +70,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return new Image<TPixel>(configuration, new ImageMetadata(), framesList);
}
public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
public override IImageInfo IdentifySpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
public 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)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
@ -106,9 +106,4 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
}
}
}
public class MagickReferenceDecoderOptions : ISpecializedDecoderOptions
{
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

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

@ -11,18 +11,18 @@ using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
public class SystemDrawingReferenceDecoder : ImageDecoder<SystemDrawingReferenceDecoderOptions>
public class SystemDrawingReferenceDecoder : ImageDecoder
{
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();
public override IImageInfo IdentifySpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using var sourceBitmap = new SDBitmap(stream);
PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
}
public override Image<TPixel> DecodeSpecialized<TPixel>(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
using var sourceBitmap = new SDBitmap(stream);
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
@ -47,12 +47,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap);
}
public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
}
public class SystemDrawingReferenceDecoderOptions : ISpecializedDecoderOptions
{
public DecoderOptions GeneralOptions { get; set; } = new();
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
}
}

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

@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
private class TestDecoder : ImageDecoder<TestDecoderOptions>
private class TestDecoder : ImageDecoder, IImageDecoderSpecialized<TestDecoderOptions>
{
// Couldn't make xUnit happy without this hackery:
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -368,16 +368,23 @@ namespace SixLabors.ImageSharp.Tests
}
}
public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
public override Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
@ -389,7 +396,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
private class TestDecoderWithParameters : ImageDecoder<TestDecoderWithParametersOptions>
private class TestDecoderWithParameters : ImageDecoder, IImageDecoderSpecialized<TestDecoderWithParametersOptions>
{
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -405,16 +412,23 @@ namespace SixLabors.ImageSharp.Tests
}
}
public override IImageInfo IdentifySpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
public override Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];

Loading…
Cancel
Save