Browse Source

Only expose sanitized decoder methods.

pull/2180/head
James Jackson-South 4 years ago
parent
commit
ac4fb62eeb
  1. 21
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  2. 12
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  3. 2
      src/ImageSharp/Formats/IImageDecoder.cs
  4. 12
      src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs
  5. 65
      src/ImageSharp/Formats/ImageDecoder.cs
  6. 182
      src/ImageSharp/Formats/ImageDecoderExtensions.cs
  7. 82
      src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs
  8. 49
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  9. 21
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  10. 12
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  11. 40
      src/ImageSharp/Formats/Png/PngDecoder.cs
  12. 12
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  13. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
  14. 12
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  15. 12
      src/ImageSharp/Formats/Webp/WebpDecoder.cs
  16. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  17. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
  18. 2
      tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
  19. 2
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  20. 2
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  21. 10
      tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
  22. 10
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  23. 2
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
  24. 4
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  25. 8
      tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
  26. 68
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  27. 21
      tests/ImageSharp.Tests/TestFormat.cs
  28. 4
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
  29. 9
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
  30. 9
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  31. 42
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

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

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

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

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

2
src/ImageSharp/Formats/IImageDecoder.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats
/// <summary> /// <summary>
/// Encapsulates properties and methods required for decoding an image from a stream. /// Encapsulates properties and methods required for decoding an image from a stream.
/// </summary> /// </summary>
public interface IImageDecoder public interface IImageDecoder : IImageInfoDetector
{ {
/// <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.

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.IO; using System.IO;
@ -17,23 +17,29 @@ 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 specialized decoder options.</param> /// <param name="options">The specialized 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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image<TPixel> DecodeSpecialized<TPixel>(T options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>; where TPixel : unmanaged, IPixel<TPixel>;
/// <summary> /// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type. /// Decodes the image from the specified stream to an <see cref="Image"/> 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>
/// <param name="options">The specialized decoder options.</param> /// <param name="options">The specialized 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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception> /// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); public Image Decode(T options, Stream stream, CancellationToken cancellationToken);
} }
} }

65
src/ImageSharp/Formats/ImageDecoder.cs

@ -1,65 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats
{
/// <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;
}
}
}

182
src/ImageSharp/Formats/ImageDecoderExtensions.cs

@ -0,0 +1,182 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Extensions methods for <see cref="IImageDecoder"/> and <see cref="IImageDecoderSpecialized{T}"/>.
/// </summary>
public static class ImageDecoderExtensions
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static IImageInfo Identify(this IImageDecoder decoder, DecoderOptions options, Stream stream)
=> Image.WithSeekableStream(
options,
stream,
s => decoder.Identify(options, s, default));
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Task{IImageInfo}"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<IImageInfo> IdentifyAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> Image.WithSeekableStreamAsync(
options,
stream,
(s, ct) => decoder.Identify(options, s, ct),
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="decoder">The decoder.</param>
/// <param name="options">The general 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> Decode<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStream(
options,
stream,
s => decoder.Decode<TPixel>(options, s, default));
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The general 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 Decode(this IImageDecoder decoder, DecoderOptions options, Stream stream)
=> Image.WithSeekableStream(
options,
stream,
s => decoder.Decode(options, s, default));
/// <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="decoder">The decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image<TPixel>> DecodeAsync<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStreamAsync(
options,
stream,
(s, ct) => decoder.Decode<TPixel>(options, s, ct),
cancellationToken);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public static Task<Image> DecodeAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> Image.WithSeekableStreamAsync(
options,
stream,
(s, ct) => decoder.Decode(options, s, ct),
cancellationToken);
/// <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> Decode<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions
where TPixel : unmanaged, IPixel<TPixel>
=> Image.WithSeekableStream(
options.GeneralOptions,
stream,
s => decoder.Decode<TPixel>(options, s, 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 Decode<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream)
where T : ISpecializedDecoderOptions
=> Image.WithSeekableStream(
options.GeneralOptions,
stream,
s => decoder.Decode(options, s, 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>> DecodeAsync<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.Decode<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> DecodeAsync<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default)
where T : ISpecializedDecoderOptions
=> Image.WithSeekableStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => decoder.Decode(options, s, ct),
cancellationToken);
}
}

82
src/ImageSharp/Formats/ImageDecoderSpecializedExtensions.cs

@ -1,82 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
/// <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);
}
}

49
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -7,12 +7,55 @@ using System.Threading;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats namespace SixLabors.ImageSharp.Formats
{ {
/// <summary>
/// Utility methods for <see cref="IImageDecoder"/>.
/// </summary>
internal static class ImageDecoderUtilities internal static class ImageDecoderUtilities
{ {
public static IImageInfo Identify( /// <summary>
/// Performs a resize operation against the decoded image. If the target size is not set, or the image size
/// already matches the target size, the image is untouched.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
public static void Resize(DecoderOptions options, Image image)
{
if (ShouldResize(options, image))
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Sampler = 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;
}
internal static IImageInfo Identify(
this IImageDecoderInternals decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
@ -30,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats
} }
} }
public static Image<TPixel> Decode<TPixel>( internal static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,
@ -38,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> decoder.Decode<TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); => decoder.Decode<TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
public static Image<TPixel> Decode<TPixel>( internal static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder, this IImageDecoderInternals decoder,
Configuration configuration, Configuration configuration,
Stream stream, Stream stream,

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

@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Decoder for generating an image out of a jpeg encoded stream. /// Decoder for generating an image out of a jpeg encoded stream.
/// </summary> /// </summary>
public sealed class JpegDecoder : ImageDecoder, IImageDecoderSpecialized<JpegDecoderOptions> public sealed class JpegDecoder : IImageDecoderSpecialized<JpegDecoderOptions>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -22,16 +22,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken); => ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/> /// <inheritdoc/>
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); => ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/> /// <inheritdoc/>
public Image<TPixel> DecodeSpecialized<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) Image<TPixel> IImageDecoderSpecialized<JpegDecoderOptions>.Decode<TPixel>(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -41,14 +40,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly) if (options.ResizeMode != JpegDecoderResizeMode.IdctOnly)
{ {
Resize(options.GeneralOptions, image); ImageDecoderUtilities.Resize(options.GeneralOptions, image);
} }
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
public Image DecodeSpecialized(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken) Image IImageDecoderSpecialized<JpegDecoderOptions>.Decode(JpegDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken); => ((IImageDecoderSpecialized<JpegDecoderOptions>)this).Decode<Rgb24>(options, stream, cancellationToken);
} }
} }

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

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

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

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

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

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

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

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// <inheritdoc/> /// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken) protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken)
{ {
using Image<Rgb24> image = new WebpDecoder().Decode<Rgb24>(this.options, stream, cancellationToken); using Image<Rgb24> image = ((IImageDecoder)new WebpDecoder()).Decode<Rgb24>(this.options, stream, cancellationToken);
CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer);
} }

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

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

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

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

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

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private void GenericBechmark() private void GenericBechmark()
{ {
this.preloadedImageStream.Position = 0; this.preloadedImageStream.Position = 0;
using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream, default); using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream);
} }
[GlobalSetup(Target = nameof(JpegBaselineInterleaved444))] [GlobalSetup(Target = nameof(JpegBaselineInterleaved444))]

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

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
public IImageInfo Identify() public IImageInfo Identify()
{ {
using var memoryStream = new MemoryStream(this.jpegBytes); using var memoryStream = new MemoryStream(this.jpegBytes);
var decoder = new JpegDecoder(); IImageDecoder decoder = new JpegDecoder();
return decoder.Identify(DecoderOptions.Default, memoryStream, default); return decoder.Identify(DecoderOptions.Default, memoryStream, default);
} }
} }

2
tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs

@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave
}; };
var decoder = new JpegDecoder(); var decoder = new JpegDecoder();
using ImageSharpImage image = decoder.Decode(options, inputStream, default); using ImageSharpImage image = decoder.Decode(options, inputStream);
this.LogImageProcessed(image.Width, image.Height); this.LogImageProcessed(image.Width, image.Height);
// Reduce the size of the file // Reduce the size of the file

2
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -497,7 +497,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new BmpDecoder(); var decoder = new BmpDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);

2
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
fixed (byte* data = testFile.Bytes.AsSpan(0, length)) fixed (byte* data = testFile.Bytes.AsSpan(0, length))
{ {
using var stream = new UnmanagedMemoryStream(data, length); using var stream = new UnmanagedMemoryStream(data, length);
using Image<Rgba32> image = GifDecoder.Decode<Rgba32>(DecoderOptions.Default, stream, default); using Image<Rgba32> image = GifDecoder.Decode<Rgba32>(DecoderOptions.Default, stream);
Assert.Equal((200, 200), (image.Width, image.Height)); Assert.Equal((200, 200), (image.Width, image.Height));
} }
} }

10
tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs

@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
input.Save(memoryStream, new GifEncoder()); input.Save(memoryStream, new GifEncoder());
memoryStream.Position = 0; memoryStream.Position = 0;
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
GifMetadata metadata = image.Metadata.GetGifMetadata(); GifMetadata metadata = image.Metadata.GetGifMetadata();
Assert.Equal(2, metadata.Comments.Count); Assert.Equal(2, metadata.Comments.Count);
Assert.Equal(new string('c', 349), metadata.Comments[0]); Assert.Equal(new string('c', 349), metadata.Comments[0]);
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder(); var decoder = new GifDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);
@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder(); var decoder = new GifDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder(); var decoder = new GifDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
GifMetadata meta = image.Metadata.GetGifMetadata(); GifMetadata meta = image.Metadata.GetGifMetadata();
Assert.Equal(repeatCount, meta.RepeatCount); Assert.Equal(repeatCount, meta.RepeatCount);
} }
@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder(); var decoder = new GifDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
GifMetadata meta = image.Metadata.GetGifMetadata(); GifMetadata meta = image.Metadata.GetGifMetadata();
Assert.Equal(repeatCount, meta.RepeatCount); Assert.Equal(repeatCount, meta.RepeatCount);
} }

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

@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder(); var decoder = new JpegDecoder();
using Image image = decoder.Decode(DecoderOptions.Default, stream, default); using Image image = decoder.Decode(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);
@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder(); var decoder = new JpegDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);
@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder(); var decoder = new JpegDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata(); JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality); Assert.Equal(quality, meta.Quality);
} }
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream, default); using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata(); JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality); Assert.Equal(quality, meta.Quality);
} }
@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata(); JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(expectedColorType, meta.ColorType); Assert.Equal(expectedColorType, meta.ColorType);
} }

2
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
var decoder = new PngDecoder(); var decoder = new PngDecoder();
ImageFormatException exception = ImageFormatException exception =
Assert.Throws<InvalidImageContentException>(() => decoder.Decode<Rgb24>(DecoderOptions.Default, memStream, default)); Assert.Throws<InvalidImageContentException>(() => decoder.Decode<Rgb24>(DecoderOptions.Default, memStream));
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
} }

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

@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
var decoder = new PngDecoder(); var decoder = new PngDecoder();
Image image = decoder.Decode(DecoderOptions.Default, stream, default); Image image = decoder.Decode(DecoderOptions.Default, stream);
PngMetadata metadata = image.Metadata.GetPngMetadata(); PngMetadata metadata = image.Metadata.GetPngMetadata();
Assert.Equal(pngColorType, metadata.ColorType); Assert.Equal(pngColorType, metadata.ColorType);
@ -597,7 +597,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
// occurs within the encoder itself leaving the input image unaffected. // occurs within the encoder itself leaving the input image unaffected.
// This means we are benefiting from testing our decoder also. // This means we are benefiting from testing our decoder also.
using FileStream fileStream = File.OpenRead(actualOutputFile); using FileStream fileStream = File.OpenRead(actualOutputFile);
using Image<TPixel> imageSharpImage = new PngDecoder().Decode<TPixel>(DecoderOptions.Default, fileStream, default); using Image<TPixel> imageSharpImage = new PngDecoder().Decode<TPixel>(DecoderOptions.Default, fileStream);
fileStream.Position = 0; fileStream.Position = 0;

8
tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
input.Save(memoryStream, new PngEncoder()); input.Save(memoryStream, new PngEncoder());
memoryStream.Position = 0; memoryStream.Position = 0;
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
VerifyTextDataIsPresent(meta); VerifyTextDataIsPresent(meta);
} }
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
}); });
memoryStream.Position = 0; memoryStream.Position = 0;
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance); PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Contains(meta.TextData, m => m.Equals(expectedText)); Assert.Contains(meta.TextData, m => m.Equals(expectedText));
Assert.Contains(meta.TextData, m => m.Equals(expectedTextNoneLatin)); Assert.Contains(meta.TextData, m => m.Equals(expectedTextNoneLatin));
@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new PngDecoder(); var decoder = new PngDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream, default); using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);
@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false); using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new PngDecoder(); var decoder = new PngDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream, default); IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution); Assert.Equal(yResolution, meta.VerticalResolution);

68
tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs

@ -26,76 +26,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
// image.Save(provider.Utility.GetTestOutputFileName("bmp")); // image.Save(provider.Utility.GetTestOutputFileName("bmp"));
image.Save(ms, new PngEncoder()); image.Save(ms, new PngEncoder());
ms.Position = 0; ms.Position = 0;
using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms, default); using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms);
ImageComparer.Tolerant().VerifySimilarity(image, img2); ImageComparer.Tolerant().VerifySimilarity(image, img2);
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
} }
/* JJS: Disabled for now as the decoder now correctly decodes the full pixel components if the
paletted image has alpha of 0
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// does saving a file then reopening mean both files are identical???
using (Image<TPixel> image = provider.GetImage())
using (MemoryStream ms = new MemoryStream())
{
// image.Save(provider.Utility.GetTestOutputFileName("bmp"));
image.Save(ms, new PngEncoder() { PaletteSize = 256 });
ms.Position = 0;
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
{
ImageComparer.VerifySimilarity(image, img2, 0.03f);
}
}
}*/
/* JJS: Commented out for now since the test does not take into lossy nature of indexing.
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Color)]
public void CanSaveIndexedPngTwice<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// does saving a file then reopening mean both files are identical???
using (Image<TPixel> source = provider.GetImage())
using (MemoryStream ms = new MemoryStream())
{
source.Metadata.Quality = 256;
source.Save(ms, new PngEncoder(), new PngEncoderOptions {
Threshold = 200
});
ms.Position = 0;
using (Image img1 = Image.Load(ms, new PngDecoder()))
{
using (MemoryStream ms2 = new MemoryStream())
{
img1.Save(ms2, new PngEncoder(), new PngEncoderOptions
{
Threshold = 200
});
ms2.Position = 0;
using (Image img2 = Image.Load(ms2, new PngDecoder()))
{
using (PixelAccessor<Color> pixels1 = img1.Lock())
using (PixelAccessor<Color> pixels2 = img2.Lock())
{
for (int y = 0; y < img1.Height; y++)
{
for (int x = 0; x < img1.Width; x++)
{
Assert.Equal(pixels1[x, y], pixels2[x, y]);
}
}
}
}
}
}
}
}*/
[Theory] [Theory]
[WithTestPatternImages(300, 300, PixelTypes.Rgba32)] [WithTestPatternImages(300, 300, PixelTypes.Rgba32)]
public void Resize<TPixel>(TestImageProvider<TPixel> provider) public void Resize<TPixel>(TestImageProvider<TPixel> provider)
@ -111,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
// image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); // image.Save(provider.Utility.GetTestOutputFileName("png", "resize"));
image.Save(ms, new PngEncoder()); image.Save(ms, new PngEncoder());
ms.Position = 0; ms.Position = 0;
using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms, default); using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms);
ImageComparer.Tolerant().VerifySimilarity(image, img2); ImageComparer.Tolerant().VerifySimilarity(image, img2);
} }
} }

21
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, IImageDecoderSpecialized<TestDecoderOptions> public class TestDecoder : IImageDecoderSpecialized<TestDecoderOptions>
{ {
private readonly TestFormat testFormat; private readonly TestFormat testFormat;
@ -208,16 +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 Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken); => ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken); where TPixel : unmanaged, IPixel<TPixel>
=> ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken); => ((IImageDecoderSpecialized<TestDecoderOptions>)this).Decode<TestPixelForAgnosticDecode>(new() { GeneralOptions = options }, stream, cancellationToken);
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Configuration configuration = options.GeneralOptions.Configuration; Configuration configuration = options.GeneralOptions.Configuration;
@ -235,8 +236,8 @@ namespace SixLabors.ImageSharp.Tests
return this.testFormat.Sample<TPixel>(); return this.testFormat.Sample<TPixel>();
} }
public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TestPixelForAgnosticDecode>(options, stream, cancellationToken); => this.Decode<TestPixelForAgnosticDecode>(options, stream, cancellationToken);
} }
public class TestDecoderOptions : ISpecializedDecoderOptions public class TestDecoderOptions : ISpecializedDecoderOptions

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

@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests
// TODO: Check Path here. Why combined? // TODO: Check Path here. Why combined?
string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath); string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath);
using Stream stream = System.IO.File.OpenRead(path); using Stream stream = System.IO.File.OpenRead(path);
return Task.FromResult(decoder.DecodeSpecialized<TPixel>(options, stream, default)); return Task.FromResult(decoder.Decode<TPixel>(options, stream, default));
} }
public override void Deserialize(IXunitSerializationInfo info) public override void Deserialize(IXunitSerializationInfo info)
@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.Tests
var testFile = TestFile.Create(this.FilePath); var testFile = TestFile.Create(this.FilePath);
using Stream stream = new MemoryStream(testFile.Bytes); using Stream stream = new MemoryStream(testFile.Bytes);
return decoder.DecodeSpecialized<TPixel>(options, stream, default); return decoder.Decode<TPixel>(options, stream, default);
} }
} }

9
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 public class MagickReferenceDecoder : IImageDecoder
{ {
private readonly bool validate; private readonly bool validate;
@ -28,7 +28,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
public static MagickReferenceDecoder Instance { get; } = new(); public static MagickReferenceDecoder Instance { get; } = new();
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{ {
Configuration configuration = options.Configuration; Configuration configuration = options.Configuration;
var bmpReadDefines = new BmpReadDefines var bmpReadDefines = new BmpReadDefines
@ -70,10 +71,10 @@ 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 Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup) private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup)

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

@ -11,18 +11,19 @@ using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{ {
public class SystemDrawingReferenceDecoder : ImageDecoder public class SystemDrawingReferenceDecoder : IImageDecoder
{ {
public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder();
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
using var sourceBitmap = new SDBitmap(stream); 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> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
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,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap); return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap);
} }
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
} }
} }

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

@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
private class TestDecoder : ImageDecoder, IImageDecoderSpecialized<TestDecoderOptions> private class TestDecoder : 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,24 +368,25 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode<Rgba32>((TestDecoderOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken); where TPixel : unmanaged, IPixel<TPixel>
=> this.Decode<TPixel>((TestDecoderOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode((TestDecoderOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
InvocationCounts[this.callerName]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); return new Image<TPixel>(42, 42);
} }
public Image DecodeSpecialized(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];
@ -396,7 +397,7 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
private class TestDecoderWithParameters : ImageDecoder, IImageDecoderSpecialized<TestDecoderWithParametersOptions> private class TestDecoderWithParameters : IImageDecoderSpecialized<TestDecoderWithParametersOptions>
{ {
private static readonly ConcurrentDictionary<string, int> InvocationCounts = new(); private static readonly ConcurrentDictionary<string, int> InvocationCounts = new();
@ -412,24 +413,25 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
public override IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode<Rgba32>((TestDecoderWithParametersOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken); where TPixel : unmanaged, IPixel<TPixel>
=> this.Decode<TPixel>((TestDecoderWithParametersOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized(new() { GeneralOptions = options }, stream, cancellationToken); => this.Decode((TestDecoderWithParametersOptions)(new() { GeneralOptions = options }), stream, cancellationToken);
public Image<TPixel> DecodeSpecialized<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
InvocationCounts[this.callerName]++; InvocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42); return new Image<TPixel>(42, 42);
} }
public Image DecodeSpecialized(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) public Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken); => this.Decode<Rgba32>(options, stream, cancellationToken);
internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName]; internal static int GetInvocationCount(string callerName) => InvocationCounts[callerName];

Loading…
Cancel
Save