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> /// <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> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve] [Preserve]

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

@ -10,31 +10,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream. /// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary> /// </summary>
public class BmpDecoder : ImageDecoder<BmpDecoderOptions> public class BmpDecoder : ImageDecoder, IImageDecoderSpecialized<BmpDecoderOptions>
{ {
/// <inheritdoc/> /// <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)); Guard.NotNull(stream, nameof(stream));
BmpDecoderCore decoder = new(options); return new BmpDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
Image<TPixel> image = decoder.Decode<BmpDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, 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/> /// <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)); 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> /// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/> /// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks> /// </remarks>
internal sealed class BmpDecoderCore : IImageDecoderInternals<BmpDecoderOptions> internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps. /// 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> /// </summary>
private readonly MemoryAllocator memoryAllocator; 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> /// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class. /// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public BmpDecoderCore(BmpDecoderOptions options) public BmpDecoderCore(BmpDecoderOptions options)
{ {
this.Options = options; this.Options = options.GeneralOptions;
this.rleSkippedPixelHandling = options.RleSkippedPixelHandling;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc /> /// <inheritdoc />
public BmpDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);
@ -322,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
byte colorIdx = bufferRow[x]; byte colorIdx = bufferRow[x];
if (undefinedPixelsSpan[rowStartIdx + x]) if (undefinedPixelsSpan[rowStartIdx + x])
{ {
switch (this.Options.RleSkippedPixelHandling) switch (this.rleSkippedPixelHandling)
{ {
case RleSkippedPixelHandling.FirstColorOfPalette: case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4])); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4]));
@ -394,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int idx = rowStartIdx + (x * 3); int idx = rowStartIdx + (x * 3);
if (undefinedPixelsSpan[yMulWidth + x]) if (undefinedPixelsSpan[yMulWidth + x])
{ {
switch (this.Options.RleSkippedPixelHandling) switch (this.rleSkippedPixelHandling)
{ {
case RleSkippedPixelHandling.FirstColorOfPalette: case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])); 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> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public sealed class GifDecoder : ImageDecoder<GifDecoderOptions> public sealed class GifDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <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); Guard.NotNull(options, nameof(options));
Image<TPixel> image = decoder.Decode<GifDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Guard.NotNull(stream, nameof(stream));
Resize(options.GeneralOptions, image);
return image; return new GifDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image DecodeSpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(GifDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); 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> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
internal sealed class GifDecoderCore : IImageDecoderInternals<GifDecoderOptions> internal sealed class GifDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// 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. /// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public GifDecoderCore(GifDecoderOptions options) public GifDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc /> /// <inheritdoc />
public GifDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); 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> /// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type. /// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary> /// </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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param> /// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
@ -27,6 +30,9 @@ namespace SixLabors.ImageSharp.Formats
/// <summary> /// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/>. /// Decodes the image from the specified stream to an <see cref="Image"/>.
/// </summary> /// </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="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</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. // Licensed under the Six Labors Split License.
using System; using System;
@ -11,14 +11,12 @@ namespace SixLabors.ImageSharp.Formats
/// <summary> /// <summary>
/// Abstraction for shared internals for XXXDecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>. /// Abstraction for shared internals for XXXDecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of specialized decoder options.</typeparam> internal interface IImageDecoderInternals
internal interface IImageDecoderInternals<T>
where T : ISpecializedDecoderOptions
{ {
/// <summary> /// <summary>
/// Gets the specialized decoder options. /// Gets the general decoder options.
/// </summary> /// </summary>
T Options { get; } DecoderOptions Options { get; }
/// <summary> /// <summary>
/// Gets the dimensions of the image being decoded. /// 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> /// <summary>
/// Reads the raw image information from the specified stream. /// Reads the raw image information from the specified stream.
/// </summary> /// </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="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</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 internal static class ImageDecoderUtilities
{ {
public static IImageInfo Identify<T>( public static IImageInfo Identify(
this IImageDecoderInternals<T> decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
{ {
using var bufferedReadStream = new BufferedReadStream(configuration, stream); using var bufferedReadStream = new BufferedReadStream(configuration, stream);
@ -31,22 +30,20 @@ namespace SixLabors.ImageSharp.Formats
} }
} }
public static Image<TPixel> Decode<T, TPixel>( public static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals<T> decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> 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>( public static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals<T> decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory, Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using var bufferedReadStream = new BufferedReadStream(configuration, stream); 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> /// <summary>
/// Decoder for generating an image out of a jpeg encoded stream. /// Decoder for generating an image out of a jpeg encoded stream.
/// </summary> /// </summary>
public sealed class JpegDecoder : ImageDecoder<JpegDecoderOptions> public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized<JpegDecoderOptions>
{ {
/// <inheritdoc/> /// <inheritdoc/>
/// <remarks> public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
/// 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)
{ {
using JpegDecoderCore decoder = new(options); Guard.NotNull(stream, nameof(stream));
return decoder.Decode<JpegDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
using JpegDecoderCore decoder = new(new() { GeneralOptions = options });
return decoder.Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
/// <remarks> public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
/// Unlike <see cref="IImageDecoder.Decode(DecoderOptions, Stream, CancellationToken)"/>, when => this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <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. /// <inheritdoc/>
/// Upscaling is not supported, so the original size will be returned. public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
/// </remarks> => this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken);
/// <inheritdoc/> /// <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)); Guard.NotNull(stream, nameof(stream));
using JpegDecoderCore decoder = new(options); 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"/> /// 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. /// with additional fixes for both performance and common encoding errors.
/// </summary> /// </summary>
internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals<JpegDecoderOptions> internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The only supported precision /// The only supported precision
@ -125,19 +125,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
private readonly bool skipMetadata; private readonly bool skipMetadata;
/// <summary>
/// The jpeg specific resize options.
/// </summary>
private readonly JpegDecoderResizeMode resizeMode;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore"/> class. /// Initializes a new instance of the <see cref="JpegDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public JpegDecoderCore(JpegDecoderOptions options) public JpegDecoderCore(JpegDecoderOptions options)
{ {
this.Options = options; this.Options = options.GeneralOptions;
this.resizeMode = options.ResizeMode;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
} }
/// <inheritdoc /> /// <inheritdoc />
public JpegDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public Size Dimensions => this.Frame.PixelSize; public Size Dimensions => this.Frame.PixelSize;
@ -207,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> 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.ParseStream(stream, spectralConverter, cancellationToken);
this.InitExifProfile(); this.InitExifProfile();
this.InitIccProfile(); this.InitIccProfile();

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

@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
public sealed class JpegDecoderOptions : ISpecializedDecoderOptions public sealed class JpegDecoderOptions : ISpecializedDecoderOptions
{ {
/// <summary>
/// Gets or sets the resize mode.
/// </summary>
public JpegDecoderResizeMode ResizeMode { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new(); 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> /// </list>
/// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>. /// The specification of these images is found at <seealso href="http://netpbm.sourceforge.net/doc/pnm.html"/>.
/// </summary> /// </summary>
public sealed class PbmDecoder : ImageDecoder<PbmDecoderOptions> public sealed class PbmDecoder : ImageDecoder
{ {
/// <inheritdoc /> /// <inheritdoc/>
public override Image<TPixel> DecodeSpecialized<TPixel>(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
PbmDecoderCore decoder = new(options); Guard.NotNull(options, nameof(options));
Image<TPixel> image = decoder.Decode<PbmDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Guard.NotNull(stream, nameof(stream));
Resize(options.GeneralOptions, image);
return image; return new PbmDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc /> /// <inheritdoc />
public override Image DecodeSpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(PbmDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); 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> /// <summary>
/// Performs the PBM decoding operation. /// Performs the PBM decoding operation.
/// </summary> /// </summary>
internal sealed class PbmDecoderCore : IImageDecoderInternals<PbmDecoderOptions> internal sealed class PbmDecoderCore : IImageDecoderInternals
{ {
private int maxPixelValue; private int maxPixelValue;
@ -52,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Pbm
/// Initializes a new instance of the <see cref="PbmDecoderCore" /> class. /// Initializes a new instance of the <see cref="PbmDecoderCore" /> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PbmDecoderCore(PbmDecoderOptions options) public PbmDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
} }
/// <inheritdoc/> /// <inheritdoc/>
public PbmDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public Size Dimensions => this.pixelSize; 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> /// <summary>
/// Decoder for generating an image out of a png encoded stream. /// Decoder for generating an image out of a png encoded stream.
/// </summary> /// </summary>
public sealed class PngDecoder : ImageDecoder<PngDecoderOptions> public sealed class PngDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <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); 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; return image;
} }
/// <inheritdoc/> /// <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); 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; stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata(); PngMetadata meta = info.Metadata.GetPngMetadata();
@ -39,50 +54,42 @@ namespace SixLabors.ImageSharp.Formats.Png
if (bits == PngBitDepth.Bit16) if (bits == PngBitDepth.Bit16)
{ {
return !meta.HasTransparency return !meta.HasTransparency
? this.DecodeSpecialized<L16>(options, stream, cancellationToken) ? this.Decode<L16>(options, stream, cancellationToken)
: this.DecodeSpecialized<La32>(options, stream, cancellationToken); : this.Decode<La32>(options, stream, cancellationToken);
} }
return !meta.HasTransparency return !meta.HasTransparency
? this.DecodeSpecialized<L8>(options, stream, cancellationToken) ? this.Decode<L8>(options, stream, cancellationToken)
: this.DecodeSpecialized<La16>(options, stream, cancellationToken); : this.Decode<La16>(options, stream, cancellationToken);
case PngColorType.Rgb: case PngColorType.Rgb:
if (bits == PngBitDepth.Bit16) if (bits == PngBitDepth.Bit16)
{ {
return !meta.HasTransparency return !meta.HasTransparency
? this.DecodeSpecialized<Rgb48>(options, stream, cancellationToken) ? this.Decode<Rgb48>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken); : this.Decode<Rgba64>(options, stream, cancellationToken);
} }
return !meta.HasTransparency return !meta.HasTransparency
? this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken) ? this.Decode<Rgb24>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); : this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.Palette: case PngColorType.Palette:
return this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); return this.Decode<Rgba32>(options, stream, cancellationToken);
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
return (bits == PngBitDepth.Bit16) return (bits == PngBitDepth.Bit16)
? this.DecodeSpecialized<La32>(options, stream, cancellationToken) ? this.Decode<La32>(options, stream, cancellationToken)
: this.DecodeSpecialized<La16>(options, stream, cancellationToken); : this.Decode<La16>(options, stream, cancellationToken);
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
return (bits == PngBitDepth.Bit16) return (bits == PngBitDepth.Bit16)
? this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken) ? this.Decode<Rgba64>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); : this.Decode<Rgba32>(options, stream, cancellationToken);
default: 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> /// <summary>
/// Performs the png decoding operation. /// Performs the png decoding operation.
/// </summary> /// </summary>
internal sealed class PngDecoderCore : IImageDecoderInternals<PngDecoderOptions> internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// Reusable buffer. /// Reusable buffer.
@ -123,25 +123,25 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class. /// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PngDecoderCore(PngDecoderOptions options) public PngDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly)
{ {
this.Options = options; this.Options = options;
this.colorMetadataOnly = colorMetadataOnly; this.colorMetadataOnly = colorMetadataOnly;
this.skipMetadata = true; this.skipMetadata = true;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc/> /// <inheritdoc/>
public PngDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public Size Dimensions => new(this.header.Width, this.header.Height); 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> /// <summary>
/// Image decoder for Truevision TGA images. /// Image decoder for Truevision TGA images.
/// </summary> /// </summary>
public sealed class TgaDecoder : ImageDecoder<TgaDecoderOptions> public sealed class TgaDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <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); Guard.NotNull(options, nameof(options));
Image<TPixel> image = decoder.Decode<TgaDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Guard.NotNull(stream, nameof(stream));
Resize(options.GeneralOptions, image);
return image; return new TgaDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); 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> /// <summary>
/// Performs the tga decoding operation. /// Performs the tga decoding operation.
/// </summary> /// </summary>
internal sealed class TgaDecoderCore : IImageDecoderInternals<TgaDecoderOptions> internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// A scratch buffer to reduce allocations. /// 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. /// Initializes a new instance of the <see cref="TgaDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public TgaDecoderCore(TgaDecoderOptions options) public TgaDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc /> /// <inheritdoc />
public TgaDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); 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); return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision);
} }
/// <remarks> /// <summary>
/// This converter must be used only for RGB and YCbCr color spaces for performance reasons. /// 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. /// For grayscale images <see cref="GrayJpegSpectralConverter{TPixel}"/> must be used.
/// </remarks> /// </summary>
private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation) private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation)
=> interpretation switch => interpretation switch
{ {

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

@ -10,29 +10,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <summary> /// <summary>
/// Image decoder for generating an image out of a TIFF stream. /// Image decoder for generating an image out of a TIFF stream.
/// </summary> /// </summary>
public class TiffDecoder : ImageDecoder<TiffDecoderOptions> public class TiffDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <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); Guard.NotNull(options, nameof(options));
Image<TPixel> image = decoder.Decode<TiffDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Guard.NotNull(stream, nameof(stream));
Resize(options.GeneralOptions, image);
return image; return new TiffDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image DecodeSpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(TiffDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); 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> /// <summary>
/// Performs the tiff decoding operation. /// Performs the tiff decoding operation.
/// </summary> /// </summary>
internal class TiffDecoderCore : IImageDecoderInternals<TiffDecoderOptions> internal class TiffDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// General configuration options. /// General configuration options.
@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// Initializes a new instance of the <see cref="TiffDecoderCore" /> class. /// Initializes a new instance of the <see cref="TiffDecoderCore" /> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public TiffDecoderCore(TiffDecoderOptions options) public TiffDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffPredictor Predictor { get; set; } public TiffPredictor Predictor { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public TiffDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public Size Dimensions { get; private set; } public Size Dimensions { get; private set; }
@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
} }
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(
this.Options.GeneralOptions, this.Options,
this.CompressionType, this.CompressionType,
this.memoryAllocator, this.memoryAllocator,
this.PhotometricInterpretation, this.PhotometricInterpretation,
@ -454,7 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Buffer2D<TPixel> pixels = frame.PixelBuffer; Buffer2D<TPixel> pixels = frame.PixelBuffer;
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(
this.Options.GeneralOptions, this.Options,
this.CompressionType, this.CompressionType,
this.memoryAllocator, this.memoryAllocator,
this.PhotometricInterpretation, 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> /// <summary>
/// Image decoder for generating an image out of a webp stream. /// Image decoder for generating an image out of a webp stream.
/// </summary> /// </summary>
public sealed class WebpDecoder : ImageDecoder<WebpDecoderOptions> public sealed class WebpDecoder : ImageDecoder
{ {
/// <inheritdoc/> /// <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); Guard.NotNull(options, nameof(options));
Image<TPixel> image = decoder.Decode<WebpDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); Guard.NotNull(stream, nameof(stream));
Resize(options.GeneralOptions, image);
return image; using WebpDecoderCore decoder = new(options);
return decoder.Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image DecodeSpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public override IImageInfo IdentifySpecialized(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); 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> /// <summary>
/// Performs the webp decoding operation. /// Performs the webp decoding operation.
/// </summary> /// </summary>
internal sealed class WebpDecoderCore : IImageDecoderInternals<WebpDecoderOptions>, IDisposable internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
{ {
/// <summary> /// <summary>
/// Reusable buffer. /// Reusable buffer.
@ -76,17 +76,17 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Initializes a new instance of the <see cref="WebpDecoderCore"/> class. /// Initializes a new instance of the <see cref="WebpDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public WebpDecoderCore(WebpDecoderOptions options) public WebpDecoderCore(DecoderOptions options)
{ {
this.Options = options; this.Options = options;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc/> /// <inheritdoc/>
public WebpDecoderOptions Options { get; } public DecoderOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); 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="stream">The input stream.</param>
/// <param name="action">The action to perform.</param> /// <param name="action">The action to perform.</param>
/// <returns>The <typeparamref name="T"/>.</returns> /// <returns>The <typeparamref name="T"/>.</returns>
private static T WithSeekableStream<T>( internal static T WithSeekableStream<T>(
DecoderOptions options, DecoderOptions options,
Stream stream, Stream stream,
Func<Stream, T> action) Func<Stream, T> action)
@ -562,7 +562,7 @@ namespace SixLabors.ImageSharp
/// <param name="action">The action to perform.</param> /// <param name="action">The action to perform.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The <see cref="Task{T}"/>.</returns> /// <returns>The <see cref="Task{T}"/>.</returns>
private static async Task<T> WithSeekableStreamAsync<T>( internal static async Task<T> WithSeekableStreamAsync<T>(
DecoderOptions options, DecoderOptions options,
Stream stream, Stream stream,
Func<Stream, CancellationToken, T> action, 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) 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 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 TestHeader(TestFormat testFormat) => this.testFormat = testFormat;
} }
public class TestDecoder : ImageDecoder<TestDecoderOptions> public class TestDecoder : ImageDecoder, IImageDecoderSpecialized<TestDecoderOptions>
{ {
private readonly TestFormat testFormat; private readonly TestFormat testFormat;
@ -208,10 +208,17 @@ namespace SixLabors.ImageSharp.Tests
public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header); public bool IsSupportedFileFormat(Span<byte> header) => this.testFormat.IsSupportedFileFormat(header);
public override IImageInfo IdentifySpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(options, stream, 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; Configuration configuration = options.GeneralOptions.Configuration;
var ms = new MemoryStream(); var ms = new MemoryStream();
@ -228,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests
return this.testFormat.Sample<TPixel>(); 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); => 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)); 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(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -243,7 +243,7 @@ namespace SixLabors.ImageSharp.Tests
return cachedImage.Clone(this.Configuration); 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(decoder, nameof(decoder));
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests
return decoder.Decode<TPixel>(options, stream, default); 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() where T : class, ISpecializedDecoderOptions, new()
{ {
options.GeneralOptions.Configuration = this.Configuration; 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) public virtual Task<Image<TPixel>> GetImageAsync(IImageDecoder decoder, DecoderOptions options)
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");
public virtual Image<TPixel> GetImage<T>(ImageDecoder<T> decoder, T options) public virtual Image<TPixel> GetImage<T>(IImageDecoderSpecialized<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new() where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!");
public virtual Task<Image<TPixel>> GetImageAsync<T>(ImageDecoder<T> decoder, T options) public virtual Task<Image<TPixel>> GetImageAsync<T>(IImageDecoderSpecialized<T> decoder, T options)
where T : class, ISpecializedDecoderOptions, new() where T : class, ISpecializedDecoderOptions, new()
=> throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!"); => throw new NotSupportedException($"Decoder specific GetImageAsync() is not supported with {this.GetType().Name}!");

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

@ -15,7 +15,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{ {
public class MagickReferenceDecoder : ImageDecoder<MagickReferenceDecoderOptions> public class MagickReferenceDecoder : ImageDecoder
{ {
private readonly bool validate; private readonly bool validate;
@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
public static MagickReferenceDecoder Instance { get; } = new(); 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 var bmpReadDefines = new BmpReadDefines
{ {
IgnoreFileSize = !this.validate IgnoreFileSize = !this.validate
@ -70,11 +70,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return new Image<TPixel>(configuration, new ImageMetadata(), framesList); return new Image<TPixel>(configuration, new ImageMetadata(), framesList);
} }
public override Image DecodeSpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
public override IImageInfo IdentifySpecialized(MagickReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup) private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel> 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 namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{ {
public class SystemDrawingReferenceDecoder : ImageDecoder<SystemDrawingReferenceDecoderOptions> public class SystemDrawingReferenceDecoder : ImageDecoder
{ {
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); 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); using var sourceBitmap = new SDBitmap(stream);
PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat));
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
} }
public 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); using var sourceBitmap = new SDBitmap(stream);
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
@ -47,12 +47,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap); return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap);
} }
public override Image DecodeSpecialized(SystemDrawingReferenceDecoderOptions options, Stream stream, CancellationToken cancellationToken) public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
}
public class SystemDrawingReferenceDecoderOptions : ISpecializedDecoderOptions
{
public DecoderOptions GeneralOptions { get; set; } = new();
} }
} }

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: // Couldn't make xUnit happy without this hackery:
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new(); 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) public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, 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]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); 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); => this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; 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(); 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) public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, 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]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); 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); => this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];

Loading…
Cancel
Save