Browse Source

Merge branch 'main' into stefannikolei/add-nullable-annotation

pull/2282/head
Stefan Nikolei 3 years ago
committed by GitHub
parent
commit
ca34028e7e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 8
      src/ImageSharp/Configuration.cs
  3. 6
      src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
  4. 33
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  5. 6
      src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs
  6. 11
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  7. 6
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  8. 20
      src/ImageSharp/Formats/DecoderOptions.cs
  9. 6
      src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
  10. 21
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  11. 11
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  12. 2
      src/ImageSharp/Formats/Gif/GifFormat.cs
  13. 59
      src/ImageSharp/Formats/IImageDecoder.cs
  14. 25
      src/ImageSharp/Formats/IImageEncoder.cs
  15. 4
      src/ImageSharp/Formats/IImageEncoderInternals.cs
  16. 6
      src/ImageSharp/Formats/IImageFormatConfigurationModule.cs
  17. 23
      src/ImageSharp/Formats/IImageInfoDetector.cs
  18. 4
      src/ImageSharp/Formats/ISpecializedDecoderOptions.cs
  19. 38
      src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs
  20. 254
      src/ImageSharp/Formats/ImageDecoder.cs
  21. 178
      src/ImageSharp/Formats/ImageDecoderExtensions.cs
  22. 46
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  23. 95
      src/ImageSharp/Formats/ImageEncoder.cs
  24. 56
      src/ImageSharp/Formats/ImageEncoderUtilities.cs
  25. 272
      src/ImageSharp/Formats/ImageExtensions.Save.cs
  26. 34
      src/ImageSharp/Formats/ImageExtensions.Save.tt
  27. 6
      src/ImageSharp/Formats/ImageFormatManager.cs
  28. 6
      src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
  29. 33
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  30. 10
      src/ImageSharp/Formats/Jpeg/JpegDecoderOptions.cs
  31. 11
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  32. 6
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  33. 4
      src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs
  34. 21
      src/ImageSharp/Formats/Pbm/PbmDecoder.cs
  35. 13
      src/ImageSharp/Formats/Pbm/PbmEncoder.cs
  36. 2
      src/ImageSharp/Formats/Pbm/PbmFormat.cs
  37. 6
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  38. 48
      src/ImageSharp/Formats/Png/PngDecoder.cs
  39. 17
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  40. 14
      src/ImageSharp/Formats/Png/PngEncoder.cs
  41. 6
      src/ImageSharp/Formats/Png/PngFormat.cs
  42. 23
      src/ImageSharp/Formats/QuantizingImageEncoder.cs
  43. 91
      src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs
  44. 4
      src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
  45. 21
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  46. 11
      src/ImageSharp/Formats/Tga/TgaEncoder.cs
  47. 4
      src/ImageSharp/Formats/Tga/TgaFormat.cs
  48. 3
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
  49. 4
      src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs
  50. 21
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  51. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  52. 11
      src/ImageSharp/Formats/Tiff/TiffEncoder.cs
  53. 2
      src/ImageSharp/Formats/Tiff/TiffFormat.cs
  54. 4
      src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs
  55. 21
      src/ImageSharp/Formats/Webp/WebpDecoder.cs
  56. 11
      src/ImageSharp/Formats/Webp/WebpEncoder.cs
  57. 2
      src/ImageSharp/Formats/Webp/WebpFormat.cs
  58. 11
      src/ImageSharp/IO/BufferedReadStream.cs
  59. 1
      src/ImageSharp/IO/ChunkedMemoryStream.cs
  60. 64
      src/ImageSharp/Image.Decode.cs
  61. 45
      src/ImageSharp/Image.FromStream.cs
  62. 5
      src/ImageSharp/Image.cs
  63. 12
      src/ImageSharp/ImageExtensions.cs
  64. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  65. 3
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
  66. 13
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs
  67. 3
      tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
  68. 7
      tests/ImageSharp.Tests/ConfigurationTests.cs
  69. 79
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  70. 12
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  71. 2
      tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
  72. 27
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  73. 18
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  74. 26
      tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
  75. 8
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  76. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
  77. 38
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  78. 7
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs
  79. 77
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  80. 11
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
  81. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  82. 2
      tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs
  83. 4
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
  84. 66
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  85. 10
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  86. 32
      tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
  87. 4
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  88. 124
      tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
  89. 4
      tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs
  90. 4
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
  91. 26
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  92. 2
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  93. 14
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  94. 60
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  95. 16
      tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs
  96. 4
      tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs
  97. 180
      tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs
  98. 36
      tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs
  99. 64
      tests/ImageSharp.Tests/Image/NonSeekableStream.cs
  100. 12
      tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

2
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -289,7 +289,7 @@ internal static class AotCompilerTools
private static void AotCompileImageDecoder<TPixel, TDecoder>()
where TPixel : unmanaged, IPixel<TPixel>
where TDecoder : class, IImageDecoder
=> default(TDecoder).Decode<TPixel>(default, default, default);
=> default(TDecoder).Decode<TPixel>(default, default);
/// <summary>
/// This method pre-seeds the all <see cref="IImageProcessor" /> in the AoT compiler.

8
src/ImageSharp/Configuration.cs

@ -42,11 +42,11 @@ public sealed class Configuration
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="configurationModules">A collection of configuration modules to register.</param>
public Configuration(params IConfigurationModule[] configurationModules)
public Configuration(params IImageFormatConfigurationModule[] configurationModules)
{
if (configurationModules != null)
{
foreach (IConfigurationModule p in configurationModules)
foreach (IImageFormatConfigurationModule p in configurationModules)
{
p.Configure(this);
}
@ -180,7 +180,7 @@ public sealed class Configuration
/// Registers a new format provider.
/// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
public void Configure(IImageFormatConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
@ -203,7 +203,7 @@ public sealed class Configuration
};
/// <summary>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// Creates the default instance with the following <see cref="IImageFormatConfigurationModule"/>s preregistered:
/// <see cref="PngConfigurationModule"/>
/// <see cref="JpegConfigurationModule"/>
/// <see cref="GifConfigurationModule"/>

6
src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp;
@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
public sealed class BmpConfigurationModule : IConfigurationModule
public sealed class BmpConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(BmpFormat.Instance, new BmpEncoder());
configuration.ImageFormatsManager.SetDecoder(BmpFormat.Instance, new BmpDecoder());
configuration.ImageFormatsManager.SetDecoder(BmpFormat.Instance, BmpDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector());
}
}

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

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

6
src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs

@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
public sealed class BmpDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
public DecoderOptions GeneralOptions { get; init; } = new();
/// <summary>
/// Gets or sets the value indicating how to deal with skipped pixels,
/// Gets the value indicating how to deal with skipped pixels,
/// which can occur during decoding run length encoded bitmaps.
/// </summary>
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; }
public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; }
}

11
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -24,16 +24,9 @@ public sealed class BmpEncoder : QuantizingImageEncoder
public bool SupportTransparency { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
BmpEncoderCore encoder = new(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
BmpEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

6
src/ImageSharp/Formats/Bmp/BmpFormat.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp;
@ -13,7 +13,7 @@ public sealed class BmpFormat : IImageFormat<BmpMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static BmpFormat Instance { get; } = new BmpFormat();
@ -30,5 +30,5 @@ public sealed class BmpFormat : IImageFormat<BmpMetadata>
public IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
/// <inheritdoc/>
public BmpMetadata CreateDefaultFormatMetadata() => new BmpMetadata();
public BmpMetadata CreateDefaultFormatMetadata() => new();
}

20
src/ImageSharp/Formats/DecoderOptions.cs

@ -21,27 +21,27 @@ public sealed class DecoderOptions
internal static DecoderOptions Default { get; } = LazyOptions.Value;
/// <summary>
/// Gets or sets a custom Configuration instance to be used by the image processing pipeline.
/// Gets a custom configuration instance to be used by the image processing pipeline.
/// </summary>
public Configuration Configuration { get; set; } = Configuration.Default;
public Configuration Configuration { get; internal set; } = Configuration.Default;
/// <summary>
/// Gets or sets the target size to decode the image into.
/// Gets the target size to decode the image into. Scaling should use an operation equivalent to <see cref="ResizeMode.Max"/>.
/// </summary>
public Size? TargetSize { get; set; }
public Size? TargetSize { get; init; }
/// <summary>
/// Gets or sets the sampler to use when resizing during decoding.
/// Gets the sampler to use when resizing during decoding.
/// </summary>
public IResampler Sampler { get; set; } = KnownResamplers.Box;
public IResampler Sampler { get; init; } = KnownResamplers.Box;
/// <summary>
/// Gets or sets a value indicating whether to ignore encoded metadata when decoding.
/// Gets a value indicating whether to ignore encoded metadata when decoding.
/// </summary>
public bool SkipMetadata { get; set; }
public bool SkipMetadata { get; init; }
/// <summary>
/// Gets or sets the maximum number of image frames to decode, inclusive.
/// Gets the maximum number of image frames to decode, inclusive.
/// </summary>
public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
}

6
src/ImageSharp/Formats/Gif/GifConfigurationModule.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif;
@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
public sealed class GifConfigurationModule : IConfigurationModule
public sealed class GifConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(GifFormat.Instance, new GifEncoder());
configuration.ImageFormatsManager.SetDecoder(GifFormat.Instance, new GifDecoder());
configuration.ImageFormatsManager.SetDecoder(GifFormat.Instance, GifDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector());
}
}

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

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

11
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -16,16 +16,9 @@ public sealed class GifEncoder : QuantizingImageEncoder
public GifColorTableMode? ColorTableMode { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
GifEncoderCore encoder = new(image.GetConfiguration(), this);
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
GifEncoderCore encoder = new(image.GetConfiguration(), this);
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

2
src/ImageSharp/Formats/Gif/GifFormat.cs

@ -13,7 +13,7 @@ public sealed class GifFormat : IImageFormat<GifMetadata, GifFrameMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static GifFormat Instance { get; } = new();

59
src/ImageSharp/Formats/IImageDecoder.cs

@ -6,35 +6,68 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Encapsulates properties and methods required for decoding an image from a stream.
/// Defines the contract for all image decoders.
/// </summary>
public interface IImageDecoder : IImageInfoDetector
public interface IImageDecoder
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <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 IImageInfo Identify(DecoderOptions options, Stream stream);
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <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 Task<IImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default);
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream)
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 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 Image Decode(DecoderOptions options, Stream stream);
/// <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 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 Task<Image<TPixel>> DecodeAsync<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>;
/// <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"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image"/>.</returns>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
public Task<Image> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default);
}

25
src/ImageSharp/Formats/IImageEncoder.cs

@ -6,27 +6,32 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Encapsulates properties and methods required for encoding an image to a stream.
/// Defines the contract for all image encoders.
/// </summary>
public interface IImageEncoder
{
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// Gets a value indicating whether to ignore decoded metadata when encoding.
/// </summary>
public bool SkipMetadata { get; init; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}" />.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
void Encode<TPixel>(Image<TPixel> image, Stream stream)
/// <param name="image">The <see cref="Image{TPixel}" /> to encode from.</param>
/// <param name="stream">The <see cref="Stream" /> to encode the image data to.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}" />.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="image">The <see cref="Image{TPixel}" /> to encode from.</param>
/// <param name="stream">The <see cref="Stream" /> to encode the image data to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
/// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
}

4
src/ImageSharp/Formats/IImageEncoderInternals.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Abstraction for shared internals for ***DecoderCore implementations to be used with <see cref="ImageEncoderUtilities"/>.
/// Abstraction for shared internals for ***DecoderCore implementations.
/// </summary>
internal interface IImageEncoderInternals
{

6
src/ImageSharp/IConfigurationModule.cs → src/ImageSharp/Formats/IImageFormatConfigurationModule.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Represents an interface that can register image encoders, decoders and image format detectors.
/// </summary>
public interface IConfigurationModule
public interface IImageFormatConfigurationModule
{
/// <summary>
/// Called when loaded into a configuration object so the module can register items into the configuration.

23
src/ImageSharp/Formats/IImageInfoDetector.cs

@ -1,23 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Encapsulates methods used for detecting the raw image information without fully decoding it.
/// </summary>
public interface IImageInfoDetector
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
}

4
src/ImageSharp/Formats/ISpecializedDecoderOptions.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats;
public interface ISpecializedDecoderOptions
{
/// <summary>
/// Gets or sets the general decoder options.
/// Gets the general decoder options.
/// </summary>
DecoderOptions GeneralOptions { get; set; }
DecoderOptions GeneralOptions { get; init; }
}

38
src/ImageSharp/Formats/IImageDecoderSpecialized{T}.cs → src/ImageSharp/Formats/ISpecializedImageDecoder{T}.cs

@ -6,37 +6,51 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// The base class for all specialized image decoders.
/// Defines the contract for an image decoder that supports specialized options.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
public interface IImageDecoderSpecialized<T> : IImageDecoder
public interface ISpecializedImageDecoder<T> : IImageDecoder
where T : ISpecializedDecoderOptions
{
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken)
public Image<TPixel> Decode<TPixel>(T options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Image Decode(T options, Stream stream, CancellationToken cancellationToken);
public Image Decode(T options, Stream stream);
/// <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>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Task<Image<TPixel>> DecodeAsync<TPixel>(T options, Stream stream, CancellationToken cancellationToken = default)
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>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
public Task<Image> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken = default);
}

254
src/ImageSharp/Formats/ImageDecoder.cs

@ -0,0 +1,254 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Acts as a base class for image decoders.
/// Types that inherit this decoder are required to implement cancellable synchronous decoding operations only.
/// </summary>
public abstract class ImageDecoder : IImageDecoder
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableStream(
options,
stream,
s => this.Decode<TPixel>(options, s, default));
/// <inheritdoc/>
public Image Decode(DecoderOptions options, Stream stream)
=> WithSeekableStream(
options,
stream,
s => this.Decode(options, s, default));
/// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Decode<TPixel>(options, s, ct),
cancellationToken);
/// <inheritdoc/>
public Task<Image> DecodeAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.Decode(options, s, ct),
cancellationToken);
/// <inheritdoc/>
public IImageInfo Identify(DecoderOptions options, Stream stream)
=> WithSeekableStream(
options,
stream,
s => this.Identify(options, s, default));
/// <inheritdoc/>
public Task<IImageInfo> IdentifyAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
options,
stream,
(s, ct) => this.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>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image" />.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="IImageInfo"/> object.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken);
/// <summary>
/// Performs a scaling 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 ScaleToTargetSize(DecoderOptions options, Image image)
{
if (ShouldResize(options, image))
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Sampler = options.Sampler,
Mode = ResizeMode.Max
};
image.Mutate(x => x.Resize(resizeOptions));
}
}
/// <summary>
/// Determines whether the decoded image should be resized.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
/// <returns><see langword="true"/> if the image should be resized, otherwise; <see langword="false"/>.</returns>
private static bool ShouldResize(DecoderOptions options, Image image)
{
if (options.TargetSize is null)
{
return false;
}
Size targetSize = options.TargetSize.Value;
Size currentSize = image.Size();
return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height;
}
internal static T WithSeekableStream<T>(
DecoderOptions options,
Stream stream,
Func<Stream, T> action)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
T PeformActionAndResetPosition(Stream s, long position)
{
T result = action(s);
// Issue #2259. Our buffered reads may have left the stream in an incorrect non-zero position.
// Reset the position of the seekable stream if we did not read to the end to allow additional reads.
// The stream is always seekable in this scenario.
if (stream.Position != s.Position && s.Position != s.Length)
{
stream.Position = position + s.Position;
}
return result;
}
if (stream.CanSeek)
{
return PeformActionAndResetPosition(stream, stream.Position);
}
Configuration configuration = options.Configuration;
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
memoryStream.Position = 0;
return action(memoryStream);
}
internal static Task<T> WithSeekableMemoryStreamAsync<T>(
DecoderOptions options,
Stream stream,
Func<Stream, CancellationToken, T> action,
CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken ct)
{
try
{
T result = action(s, ct);
// Issue #2259. Our buffered reads may have left the stream in an incorrect non-zero position.
// Reset the position of the seekable stream if we did not read to the end to allow additional reads.
// We check here that the input stream is seekable because it is not guaranteed to be so since
// we always copy input streams of unknown type.
if (stream.CanSeek && stream.Position != s.Position && s.Position != s.Length)
{
stream.Position = position + s.Position;
}
return Task.FromResult(result);
}
catch (OperationCanceledException)
{
return Task.FromCanceled<T>(cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<T>(ex);
}
}
// NOTE: We are explicitly not executing the action against the stream here as we do in WithSeekableStream() because that
// would incur synchronous IO reads which must be avoided in this asynchronous method. Instead, we will *always* run the
// code below to copy the stream to an in-memory buffer before invoking the action.
if (stream is MemoryStream ms)
{
return PeformActionAndResetPosition(ms, ms.Position, cancellationToken);
}
if (stream is ChunkedMemoryStream cms)
{
return PeformActionAndResetPosition(cms, cms.Position, cancellationToken);
}
return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken);
}
private static async Task<T> CopyToMemoryStreamAndActionAsync<T>(
DecoderOptions options,
Stream stream,
Func<Stream, long, CancellationToken, Task<T>> action,
CancellationToken cancellationToken)
{
long position = stream.CanSeek ? stream.Position : 0;
Configuration configuration = options.Configuration;
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
return await action(memoryStream, position, cancellationToken).ConfigureAwait(false);
}
}

178
src/ImageSharp/Formats/ImageDecoderExtensions.cs

@ -1,178 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
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);
}

46
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -5,61 +5,21 @@
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Utility methods for <see cref="IImageDecoder"/>.
/// Utility methods for <see cref="IImageDecoderInternals"/>.
/// </summary>
internal static class ImageDecoderUtilities
{
/// <summary>
/// Performs a resize operation against the decoded image. If the target size is not set, or the image size
/// already matches the target size, the image is untouched.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
public static void Resize(DecoderOptions options, Image image)
{
if (ShouldResize(options, image))
{
ResizeOptions resizeOptions = new()
{
Size = options.TargetSize.Value,
Sampler = options.Sampler,
Mode = ResizeMode.Max
};
image.Mutate(x => x.Resize(resizeOptions));
}
}
/// <summary>
/// Determines whether the decoded image should be resized.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="image">The decoded image.</param>
/// <returns><see langword="true"/> if the image should be resized, otherwise; <see langword="false"/>.</returns>
private static bool ShouldResize(DecoderOptions options, Image image)
{
if (options.TargetSize is null)
{
return false;
}
Size targetSize = options.TargetSize.Value;
Size currentSize = image.Size();
return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height;
}
internal static IImageInfo Identify(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
{
using BufferedReadStream bufferedReadStream = new(configuration, stream);
using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken);
try
{
@ -91,7 +51,7 @@ internal static class ImageDecoderUtilities
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
using BufferedReadStream bufferedReadStream = new(configuration, stream);
using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken);
try
{

95
src/ImageSharp/Formats/ImageEncoder.cs

@ -1,43 +1,94 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// The base class for all image encoders.
/// Acts as a base class for image encoders.
/// Types that inherit this encoder are required to implement cancellable synchronous encoding operations only.
/// </summary>
public abstract class ImageEncoder : IImageEncoder
{
/// <summary>
/// Gets a value indicating whether to ignore decoded metadata when encoding.
/// </summary>
/// <inheritdoc/>
public bool SkipMetadata { get; init; }
/// <inheritdoc/>
public abstract void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>;
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> this.EncodeWithSeekableStream(image, stream, default);
/// <inheritdoc/>
public abstract Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
}
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> this.EncodeWithSeekableStreamAsync(image, stream, cancellationToken);
/// <summary>
/// The base class for all image encoders that allow color palette generation via quantization.
/// </summary>
public abstract class QuantizingImageEncoder : ImageEncoder
{
/// <summary>
/// Gets the quantizer used to generate the color palette.
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}" />.
/// </summary>
public IQuantizer Quantizer { get; init; } = KnownQuantizers.Octree;
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}" /> to encode from.</param>
/// <param name="stream">The <see cref="Stream" /> to encode the image data to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
protected abstract void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Gets the <see cref="IPixelSamplingStrategy"/> used for quantization when building color palettes.
/// </summary>
public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
private void EncodeWithSeekableStream<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Configuration configuration = image.GetConfiguration();
if (stream.CanSeek)
{
this.Encode(image, stream, cancellationToken);
}
else
{
using ChunkedMemoryStream ms = new(configuration.MemoryAllocator);
this.Encode(image, stream, cancellationToken);
ms.Position = 0;
ms.CopyTo(stream, configuration.StreamProcessingBufferSize);
}
}
private async Task EncodeWithSeekableStreamAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Configuration configuration = image.GetConfiguration();
if (stream.CanSeek)
{
await DoEncodeAsync(stream).ConfigureAwait(false);
}
else
{
using ChunkedMemoryStream ms = new(configuration.MemoryAllocator);
await DoEncodeAsync(ms);
ms.Position = 0;
await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken)
.ConfigureAwait(false);
}
Task DoEncodeAsync(Stream innerStream)
{
try
{
// TODO: Are synchronous IO writes OK? We avoid reads.
this.Encode(image, innerStream, cancellationToken);
return Task.CompletedTask;
}
catch (OperationCanceledException)
{
return Task.FromCanceled(cancellationToken);
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}
}
}

56
src/ImageSharp/Formats/ImageEncoderUtilities.cs

@ -1,56 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
internal static class ImageEncoderUtilities
{
public static async Task EncodeAsync<TPixel>(
this IImageEncoderInternals encoder,
Image<TPixel> image,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Configuration configuration = image.GetConfiguration();
if (stream.CanSeek)
{
await DoEncodeAsync(stream).ConfigureAwait(false);
}
else
{
using MemoryStream ms = new();
await DoEncodeAsync(ms);
ms.Position = 0;
await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken)
.ConfigureAwait(false);
}
Task DoEncodeAsync(Stream innerStream)
{
try
{
encoder.Encode(image, innerStream, cancellationToken);
return Task.CompletedTask;
}
catch (OperationCanceledException)
{
return Task.FromCanceled(cancellationToken);
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}
}
public static void Encode<TPixel>(
this IImageEncoderInternals encoder,
Image<TPixel> image,
Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> encoder.Encode(image, stream, default);
}

272
src/ImageSharp/Formats/ImageExtensions.Save.cs

@ -26,7 +26,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsBmp(this Image source, string path) => SaveAsBmp(source, path, null);
public static void SaveAsBmp(this Image source, string path) => SaveAsBmp(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -35,7 +35,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsBmpAsync(this Image source, string path) => SaveAsBmpAsync(source, path, null);
public static Task SaveAsBmpAsync(this Image source, string path) => SaveAsBmpAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -46,7 +46,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsBmpAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsBmpAsync(source, path, null, cancellationToken);
=> SaveAsBmpAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -69,11 +69,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
cancellationToken);
public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -82,7 +82,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp(this Image source, Stream stream)
=> SaveAsBmp(source, stream, null);
=> SaveAsBmp(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -93,7 +93,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsBmpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsBmpAsync(source, stream, null, cancellationToken);
=> SaveAsBmpAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -104,8 +104,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Bmp format.
@ -116,11 +116,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
cancellationToken);
public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -128,7 +128,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsGif(this Image source, string path) => SaveAsGif(source, path, null);
public static void SaveAsGif(this Image source, string path) => SaveAsGif(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -137,7 +137,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsGifAsync(this Image source, string path) => SaveAsGifAsync(source, path, null);
public static Task SaveAsGifAsync(this Image source, string path) => SaveAsGifAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -148,7 +148,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsGifAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsGifAsync(source, path, null, cancellationToken);
=> SaveAsGifAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -171,11 +171,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
cancellationToken);
public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -184,7 +184,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif(this Image source, Stream stream)
=> SaveAsGif(source, stream, null);
=> SaveAsGif(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -195,7 +195,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsGifAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsGifAsync(source, stream, null, cancellationToken);
=> SaveAsGifAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -206,8 +206,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Gif format.
@ -218,11 +218,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
cancellationToken);
public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -230,7 +230,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsJpeg(this Image source, string path) => SaveAsJpeg(source, path, null);
public static void SaveAsJpeg(this Image source, string path) => SaveAsJpeg(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -239,7 +239,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsJpegAsync(this Image source, string path) => SaveAsJpegAsync(source, path, null);
public static Task SaveAsJpegAsync(this Image source, string path) => SaveAsJpegAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -250,7 +250,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsJpegAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsJpegAsync(source, path, null, cancellationToken);
=> SaveAsJpegAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -273,11 +273,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
cancellationToken);
public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -286,7 +286,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg(this Image source, Stream stream)
=> SaveAsJpeg(source, stream, null);
=> SaveAsJpeg(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -297,7 +297,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsJpegAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsJpegAsync(source, stream, null, cancellationToken);
=> SaveAsJpegAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -308,8 +308,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Jpeg format.
@ -320,11 +320,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
cancellationToken);
public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -332,7 +332,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsPbm(this Image source, string path) => SaveAsPbm(source, path, null);
public static void SaveAsPbm(this Image source, string path) => SaveAsPbm(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -341,7 +341,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path) => SaveAsPbmAsync(source, path, null);
public static Task SaveAsPbmAsync(this Image source, string path) => SaveAsPbmAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -352,7 +352,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsPbmAsync(source, path, null, cancellationToken);
=> SaveAsPbmAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -375,11 +375,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -388,7 +388,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPbm(this Image source, Stream stream)
=> SaveAsPbm(source, stream, null);
=> SaveAsPbm(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -399,7 +399,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsPbmAsync(source, stream, null, cancellationToken);
=> SaveAsPbmAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -410,8 +410,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Pbm format.
@ -422,11 +422,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -434,7 +434,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsPng(this Image source, string path) => SaveAsPng(source, path, null);
public static void SaveAsPng(this Image source, string path) => SaveAsPng(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -443,7 +443,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPngAsync(this Image source, string path) => SaveAsPngAsync(source, path, null);
public static Task SaveAsPngAsync(this Image source, string path) => SaveAsPngAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -454,7 +454,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPngAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsPngAsync(source, path, null, cancellationToken);
=> SaveAsPngAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -477,11 +477,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
cancellationToken);
public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -490,7 +490,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng(this Image source, Stream stream)
=> SaveAsPng(source, stream, null);
=> SaveAsPng(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -501,7 +501,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPngAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsPngAsync(source, stream, null, cancellationToken);
=> SaveAsPngAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -512,8 +512,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Png format.
@ -524,11 +524,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
cancellationToken);
public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -536,7 +536,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsTga(this Image source, string path) => SaveAsTga(source, path, null);
public static void SaveAsTga(this Image source, string path) => SaveAsTga(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -545,7 +545,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTgaAsync(this Image source, string path) => SaveAsTgaAsync(source, path, null);
public static Task SaveAsTgaAsync(this Image source, string path) => SaveAsTgaAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -556,7 +556,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTgaAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsTgaAsync(source, path, null, cancellationToken);
=> SaveAsTgaAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -579,11 +579,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
cancellationToken);
public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -592,7 +592,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsTga(this Image source, Stream stream)
=> SaveAsTga(source, stream, null);
=> SaveAsTga(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -603,7 +603,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTgaAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsTgaAsync(source, stream, null, cancellationToken);
=> SaveAsTgaAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -614,8 +614,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsTga(this Image source, Stream stream, TgaEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tga format.
@ -626,11 +626,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
cancellationToken);
public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -638,7 +638,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsWebp(this Image source, string path) => SaveAsWebp(source, path, null);
public static void SaveAsWebp(this Image source, string path) => SaveAsWebp(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -647,7 +647,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebpAsync(this Image source, string path) => SaveAsWebpAsync(source, path, null);
public static Task SaveAsWebpAsync(this Image source, string path) => SaveAsWebpAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -658,7 +658,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebpAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsWebpAsync(source, path, null, cancellationToken);
=> SaveAsWebpAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -681,11 +681,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken);
public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -694,7 +694,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsWebp(this Image source, Stream stream)
=> SaveAsWebp(source, stream, null);
=> SaveAsWebp(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -705,7 +705,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsWebpAsync(source, stream, null, cancellationToken);
=> SaveAsWebpAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -716,8 +716,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Webp format.
@ -728,11 +728,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken);
public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -740,7 +740,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null);
public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -749,7 +749,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null);
public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -760,7 +760,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsTiffAsync(source, path, null, cancellationToken);
=> SaveAsTiffAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -783,11 +783,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -796,7 +796,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsTiff(this Image source, Stream stream)
=> SaveAsTiff(source, stream, null);
=> SaveAsTiff(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -807,7 +807,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsTiffAsync(source, stream, null, cancellationToken);
=> SaveAsTiffAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -818,8 +818,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Tiff format.
@ -830,10 +830,10 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
}

34
src/ImageSharp/Formats/ImageExtensions.Save.tt

@ -45,7 +45,7 @@ public static partial class ImageExtensions
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, null);
public static void SaveAs<#= fmt #>(this Image source, string path) => SaveAs<#= fmt #>(source, path, default);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -54,7 +54,7 @@ public static partial class ImageExtensions
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, null);
public static Task SaveAs<#= fmt #>Async(this Image source, string path) => SaveAs<#= fmt #>Async(source, path, default);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -65,7 +65,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAs<#= fmt #>Async(this Image source, string path, CancellationToken cancellationToken)
=> SaveAs<#= fmt #>Async(source, path, null, cancellationToken);
=> SaveAs<#= fmt #>Async(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -88,11 +88,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAs<#= fmt #>Async(this Image source, string path, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance),
cancellationToken);
public static Task SaveAs<#= fmt #>Async(this Image source, string path, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -101,7 +101,7 @@ public static partial class ImageExtensions
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAs<#= fmt #>(this Image source, Stream stream)
=> SaveAs<#= fmt #>(source, stream, null);
=> SaveAs<#= fmt #>(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -112,7 +112,7 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAs<#= fmt #>Async(source, stream, null, cancellationToken);
=> SaveAs<#= fmt #>Async(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -123,8 +123,8 @@ public static partial class ImageExtensions
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAs<#= fmt #>(this Image source, Stream stream, <#= fmt #>Encoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance));
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance));
/// <summary>
/// Saves the image to the given stream with the <#= fmt #> format.
@ -135,11 +135,11 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance),
cancellationToken);
public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance),
cancellationToken);
<#
}

6
src/ImageSharp/Formats/ImageFormatManager.cs

@ -23,7 +23,7 @@ public class ImageFormatManager
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// The list of supported <see cref="IImageDecoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new();
@ -123,7 +123,7 @@ public class ImageFormatManager
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (_, _) => encoder);
}
/// <summary>
@ -136,7 +136,7 @@ public class ImageFormatManager
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (_, _) => decoder);
}
/// <summary>

6
src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg;
@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public sealed class JpegConfigurationModule : IConfigurationModule
public sealed class JpegConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder());
configuration.ImageFormatsManager.SetDecoder(JpegFormat.Instance, new JpegDecoder());
configuration.ImageFormatsManager.SetDecoder(JpegFormat.Instance, JpegDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector());
}
}

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

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

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

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

11
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -48,16 +48,9 @@ public sealed class JpegEncoder : ImageEncoder
public JpegEncodingColor? ColorType { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
JpegEncoderCore encoder = new(this);
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
JpegEncoderCore encoder = new(this);
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

6
src/ImageSharp/Formats/Jpeg/JpegFormat.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg;
@ -13,7 +13,7 @@ public sealed class JpegFormat : IImageFormat<JpegMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static JpegFormat Instance { get; } = new JpegFormat();
@ -30,5 +30,5 @@ public sealed class JpegFormat : IImageFormat<JpegMetadata>
public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
/// <inheritdoc/>
public JpegMetadata CreateDefaultFormatMetadata() => new JpegMetadata();
public JpegMetadata CreateDefaultFormatMetadata() => new();
}

4
src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Pbm;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the Pbm format.
/// </summary>
public sealed class PbmConfigurationModule : IConfigurationModule
public sealed class PbmConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(PbmFormat.Instance, new PbmEncoder());
configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, new PbmDecoder());
configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, PbmDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new PbmImageFormatDetector());
}
}

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

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

13
src/ImageSharp/Formats/Pbm/PbmEncoder.cs

@ -42,21 +42,14 @@ public sealed class PbmEncoder : ImageEncoder
public PbmColorType? ColorType { get; init; }
/// <summary>
/// Gets the Data Type of the pixel components.
/// Gets the data type of the pixel components.
/// </summary>
public PbmComponentType? ComponentType { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
PbmEncoderCore encoder = new(image.GetConfiguration(), this);
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
PbmEncoderCore encoder = new(image.GetConfiguration(), this);
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

2
src/ImageSharp/Formats/Pbm/PbmFormat.cs

@ -13,7 +13,7 @@ public sealed class PbmFormat : IImageFormat<PbmMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static PbmFormat Instance { get; } = new();

6
src/ImageSharp/Formats/Png/PngConfigurationModule.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png;
@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
public sealed class PngConfigurationModule : IConfigurationModule
public sealed class PngConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(PngFormat.Instance, new PngEncoder());
configuration.ImageFormatsManager.SetDecoder(PngFormat.Instance, new PngDecoder());
configuration.ImageFormatsManager.SetDecoder(PngFormat.Instance, PngDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
}
}

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

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

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

@ -177,7 +177,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.InitializeImage(metadata, out image);
}
this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata);
this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata, cancellationToken);
break;
case PngChunkType.Palette:
@ -556,7 +556,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="chunk">The png chunk containing the compressed scanline data.</param>
/// <param name="image"> The pixel data.</param>
/// <param name="pngMetadata">The png metadata</param>
private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata)
/// <param name="cancellationToken">The cancellation token.</param>
private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
using ZlibInflateStream deframeStream = new(this.currentStream, this.ReadNextDataChunk);
@ -565,11 +566,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata, cancellationToken);
}
else
{
this.DecodePixelData(dataStream, image, pngMetadata);
this.DecodePixelData(dataStream, image, pngMetadata, cancellationToken);
}
}
@ -580,11 +581,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The image to decode to.</param>
/// <param name="pngMetadata">The png metadata</param>
private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata)
/// <param name="cancellationToken">The CancellationToken</param>
private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
while (this.currentRow < this.header.Height)
{
cancellationToken.ThrowIfCancellationRequested();
Span<byte> scanlineSpan = this.scanline.GetSpan();
while (this.currentRowBytesRead < this.bytesPerScanline)
{
@ -640,7 +643,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The current image.</param>
/// <param name="pngMetadata">The png metadata.</param>
private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata)
/// <param name="cancellationToken">The cancellation token.</param>
private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
int pass = 0;
@ -662,6 +666,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
while (this.currentRow < this.header.Height)
{
cancellationToken.ThrowIfCancellationRequested();
while (this.currentRowBytesRead < bytesPerInterlaceScanline)
{
int bytesRead = compressedStream.Read(this.scanline.GetSpan(), this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);

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

@ -75,19 +75,9 @@ public class PngEncoder : QuantizingImageEncoder
public PngTransparentColorMode TransparentColorMode { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
using PngEncoderCore encoder = new(image.GetMemoryAllocator(), image.GetConfiguration(), this);
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override async Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
// The introduction of a local variable that refers to an object the implements
// IDisposable means you must use async/await, where the compiler generates the
// state machine and a continuation.
using PngEncoderCore encoder = new(image.GetMemoryAllocator(), image.GetConfiguration(), this);
await encoder.EncodeAsync(image, stream, cancellationToken).ConfigureAwait(false);
encoder.Encode(image, stream, cancellationToken);
}
}

6
src/ImageSharp/Formats/Png/PngFormat.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png;
@ -13,7 +13,7 @@ public sealed class PngFormat : IImageFormat<PngMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static PngFormat Instance { get; } = new PngFormat();
@ -30,5 +30,5 @@ public sealed class PngFormat : IImageFormat<PngMetadata>
public IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
/// <inheritdoc/>
public PngMetadata CreateDefaultFormatMetadata() => new PngMetadata();
public PngMetadata CreateDefaultFormatMetadata() => new();
}

23
src/ImageSharp/Formats/QuantizingImageEncoder.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Acts as a base class for all image encoders that allow color palette generation via quantization.
/// </summary>
public abstract class QuantizingImageEncoder : ImageEncoder
{
/// <summary>
/// Gets the quantizer used to generate the color palette.
/// </summary>
public IQuantizer Quantizer { get; init; } = KnownQuantizers.Octree;
/// <summary>
/// Gets the <see cref="IPixelSamplingStrategy"/> used for quantization when building color palettes.
/// </summary>
public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
}

91
src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs

@ -0,0 +1,91 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Acts as a base class for specialized image decoders.
/// Specialized decoders allow for additional options to be passed to the decoder.
/// Types that inherit this decoder are required to implement cancellable synchronous decoding operations only.
/// </summary>
/// <typeparam name="T">The type of specialized options.</typeparam>
public abstract class SpecializedImageDecoder<T> : ImageDecoder, ISpecializedImageDecoder<T>
where T : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(T options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableStream(
options.GeneralOptions,
stream,
s => this.Decode<TPixel>(options, s, default));
/// <inheritdoc/>
public Image Decode(T options, Stream stream)
=> WithSeekableStream(
options.GeneralOptions,
stream,
s => this.Decode(options, s, default));
/// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(T options, Stream stream, CancellationToken cancellationToken = default)
where TPixel : unmanaged, IPixel<TPixel>
=> WithSeekableMemoryStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => this.Decode<TPixel>(options, s, ct),
cancellationToken);
/// <inheritdoc/>
public Task<Image> DecodeAsync(T options, Stream stream, CancellationToken cancellationToken = default)
=> WithSeekableMemoryStreamAsync(
options.GeneralOptions,
stream,
(s, ct) => this.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>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Decodes the image from the specified stream to an <see cref="Image" /> of a specific pixel type.
/// </summary>
/// <remarks>
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
/// </remarks>
/// <param name="options">The specialized decoder options.</param>
/// <param name="stream">The <see cref="Stream" /> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="Image{TPixel}" />.</returns>
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
protected abstract Image Decode(T options, Stream stream, CancellationToken cancellationToken);
/// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<TPixel>(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
/// <inheritdoc/>
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken);
/// <summary>
/// A factory method for creating the default specialized options.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <returns>The new <typeparamref name="T" />.</returns>
protected abstract T CreateDefaultSpecializedOptions(DecoderOptions options);
}

4
src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Tga;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the tga format.
/// </summary>
public sealed class TgaConfigurationModule : IConfigurationModule
public sealed class TgaConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(TgaFormat.Instance, new TgaEncoder());
configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder());
configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, TgaDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector());
}
}

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

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

11
src/ImageSharp/Formats/Tga/TgaEncoder.cs

@ -21,16 +21,9 @@ public sealed class TgaEncoder : ImageEncoder
public TgaCompression Compression { get; init; } = TgaCompression.RunLength;
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
TgaEncoderCore encoder = new(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
TgaEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

4
src/ImageSharp/Formats/Tga/TgaFormat.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tga;
public sealed class TgaFormat : IImageFormat<TgaMetadata>
{
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static TgaFormat Instance { get; } = new TgaFormat();
@ -26,5 +26,5 @@ public sealed class TgaFormat : IImageFormat<TgaMetadata>
public IEnumerable<string> FileExtensions => TgaConstants.FileExtensions;
/// <inheritdoc/>
public TgaMetadata CreateDefaultFormatMetadata() => new TgaMetadata();
public TgaMetadata CreateDefaultFormatMetadata() => new();
}

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

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

4
src/ImageSharp/Formats/Tiff/TiffConfigurationModule.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the TIFF format.
/// </summary>
public sealed class TiffConfigurationModule : IConfigurationModule
public sealed class TiffConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder());
configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder());
configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, TiffDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector());
}
}

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

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

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

@ -114,7 +114,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
public FaxCompressionOptions FaxCompressionOptions { get; set; }
/// <summary>
/// Gets or sets the the logical order of bits within a byte.
/// Gets or sets the logical order of bits within a byte.
/// </summary>
public TiffFillOrder FillOrder { get; set; }

11
src/ImageSharp/Formats/Tiff/TiffEncoder.cs

@ -40,16 +40,9 @@ public class TiffEncoder : QuantizingImageEncoder
public TiffPredictor? HorizontalPredictor { get; init; }
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
TiffEncoderCore encode = new(this, image.GetMemoryAllocator());
encode.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
TiffEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
encode.Encode(image, stream, cancellationToken);
}
}

2
src/ImageSharp/Formats/Tiff/TiffFormat.cs

@ -15,7 +15,7 @@ public sealed class TiffFormat : IImageFormat<TiffMetadata, TiffFrameMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static TiffFormat Instance { get; } = new TiffFormat();

4
src/ImageSharp/Formats/Webp/WebpConfigurationModule.cs

@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the webp format.
/// </summary>
public sealed class WebpConfigurationModule : IConfigurationModule
public sealed class WebpConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetDecoder(WebpFormat.Instance, new WebpDecoder());
configuration.ImageFormatsManager.SetDecoder(WebpFormat.Instance, WebpDecoder.Instance);
configuration.ImageFormatsManager.SetEncoder(WebpFormat.Instance, new WebpEncoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new WebpImageFormatDetector());
}

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

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

11
src/ImageSharp/Formats/Webp/WebpEncoder.cs

@ -80,16 +80,9 @@ public sealed class WebpEncoder : ImageEncoder
public int NearLosslessQuality { get; init; } = 100;
/// <inheritdoc/>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
WebpEncoderCore encoder = new(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
WebpEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
encoder.Encode(image, stream, cancellationToken);
}
}

2
src/ImageSharp/Formats/Webp/WebpFormat.cs

@ -13,7 +13,7 @@ public sealed class WebpFormat : IImageFormat<WebpMetadata, WebpFrameMetadata>
}
/// <summary>
/// Gets the current instance.
/// Gets the shared instance.
/// </summary>
public static WebpFormat Instance { get; } = new();

11
src/ImageSharp/IO/BufferedReadStream.cs

@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.IO;
/// </summary>
internal sealed class BufferedReadStream : Stream
{
private readonly CancellationToken cancellationToken;
private readonly int maxBufferIndex;
private readonly byte[] readBuffer;
@ -33,12 +35,15 @@ internal sealed class BufferedReadStream : Stream
/// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="stream">The input stream.</param>
public BufferedReadStream(Configuration configuration, Stream stream)
/// <param name="cancellationToken">The optional stream-level cancellation token to detect cancellation in synchronous methods.</param>
public BufferedReadStream(Configuration configuration, Stream stream, CancellationToken cancellationToken = default)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream must be readable.");
Guard.IsTrue(stream.CanSeek, nameof(stream), "Stream must be seekable.");
this.cancellationToken = cancellationToken;
// Ensure all underlying buffers have been flushed before we attempt to read the stream.
// User streams may have opted to throw from Flush if CanWrite is false
// (although the abstract Stream does not do so).
@ -85,6 +90,7 @@ internal sealed class BufferedReadStream : Stream
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.Position));
this.cancellationToken.ThrowIfCancellationRequested();
// Only reset readBufferIndex if we are out of bounds of our working buffer
// otherwise we should simply move the value by the diff.
@ -163,6 +169,8 @@ internal sealed class BufferedReadStream : Stream
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(Span<byte> buffer)
{
this.cancellationToken.ThrowIfCancellationRequested();
// Too big for our buffer. Read directly from the stream.
int count = buffer.Length;
if (count > this.BufferSize)
@ -255,6 +263,7 @@ internal sealed class BufferedReadStream : Stream
[MethodImpl(MethodImplOptions.NoInlining)]
private void FillReadBuffer()
{
this.cancellationToken.ThrowIfCancellationRequested();
Stream baseStream = this.BaseStream;
if (this.readerPosition != baseStream.Position)
{

1
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -45,6 +45,7 @@ internal sealed class ChunkedMemoryStream : Stream
/// <summary>
/// Initializes a new instance of the <see cref="ChunkedMemoryStream"/> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
public ChunkedMemoryStream(MemoryAllocator allocator)
{
Guard.NotNull(allocator, nameof(allocator));

64
src/ImageSharp/Image.Decode.cs

@ -114,12 +114,27 @@ public abstract partial class Image
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>
/// A new <see cref="Image{TPixel}"/>.
/// </returns>
private static (Image<TPixel> Image, IImageFormat Format) Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
private static (Image<TPixel> Image, IImageFormat Format) Decode<TPixel>(DecoderOptions options, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
Image<TPixel> img = decoder.Decode<TPixel>(options, stream);
return (img, format);
}
private static async Task<(Image<TPixel> Image, IImageFormat Format)> DecodeAsync<TPixel>(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
@ -128,11 +143,26 @@ public abstract partial class Image
return (null, null);
}
Image<TPixel> img = decoder.Decode<TPixel>(options, stream, cancellationToken);
Image<TPixel> img = await decoder.DecodeAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
}
private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
Image img = decoder.Decode(options, stream);
return (img, format);
}
private static (Image Image, IImageFormat Format) Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
private static async Task<(Image Image, IImageFormat Format)> DecodeAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is null)
@ -140,10 +170,25 @@ public abstract partial class Image
return (null, null);
}
Image img = decoder.Decode(options, stream, cancellationToken);
Image img = await decoder.DecodeAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (img, format);
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="options">The general decoder options.</param>
/// <param name="stream">The stream.</param>
/// <returns>
/// The <see cref="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentify(DecoderOptions options, Stream stream)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
IImageInfo info = decoder?.Identify(options, stream);
return (info, format);
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
@ -153,16 +198,19 @@ public abstract partial class Image
/// <returns>
/// The <see cref="IImageInfo"/> or null if a suitable info detector is not found.
/// </returns>
private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
private static async Task<(IImageInfo ImageInfo, IImageFormat Format)> InternalIdentifyAsync(
DecoderOptions options,
Stream stream,
CancellationToken cancellationToken)
{
IImageDecoder decoder = DiscoverDecoder(options, stream, out IImageFormat format);
if (decoder is not IImageInfoDetector detector)
if (decoder is null)
{
return (null, null);
}
IImageInfo info = detector?.Identify(options, stream, cancellationToken);
IImageInfo info = await decoder.IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
return (info, format);
}
}

45
src/ImageSharp/Image.FromStream.cs

@ -62,7 +62,7 @@ public abstract partial class Image
=> WithSeekableStreamAsync(
options,
stream,
(s, _) => InternalDetectFormat(options.Configuration, s),
(s, _) => Task.FromResult(InternalDetectFormat(options.Configuration, s)),
cancellationToken);
/// <summary>
@ -160,7 +160,7 @@ public abstract partial class Image
/// </returns>
public static IImageInfo Identify(DecoderOptions options, Stream stream, out IImageFormat format)
{
(IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentity(options, s));
(IImageInfo ImageInfo, IImageFormat Format) data = WithSeekableStream(options, stream, s => InternalIdentify(options, s));
format = data.Format;
return data.ImageInfo;
@ -205,7 +205,7 @@ public abstract partial class Image
=> WithSeekableStreamAsync(
options,
stream,
(s, ct) => InternalIdentity(options, s, ct),
(s, ct) => InternalIdentifyAsync(options, s, ct),
cancellationToken);
/// <summary>
@ -289,11 +289,7 @@ public abstract partial class Image
/// <exception cref="InvalidImageContentException">Image contains invalid content.</exception>
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
public static async Task<Image> LoadAsync(DecoderOptions options, Stream stream, CancellationToken cancellationToken = default)
{
(Image Image, IImageFormat Format) fmt = await LoadWithFormatAsync(options, stream, cancellationToken)
.ConfigureAwait(false);
return fmt.Image;
}
=> (await LoadWithFormatAsync(options, stream, cancellationToken).ConfigureAwait(false)).Image;
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
@ -416,7 +412,7 @@ public abstract partial class Image
CancellationToken cancellationToken = default)
{
(Image Image, IImageFormat Format) data =
await WithSeekableStreamAsync(options, stream, (s, ct) => Decode(options, s, ct), cancellationToken)
await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
@ -447,7 +443,7 @@ public abstract partial class Image
where TPixel : unmanaged, IPixel<TPixel>
{
(Image<TPixel> Image, IImageFormat Format) data =
await WithSeekableStreamAsync(options, stream, (s, ct) => Decode<TPixel>(options, s, ct), cancellationToken)
await WithSeekableStreamAsync(options, stream, (s, ct) => DecodeAsync<TPixel>(options, s, ct), cancellationToken)
.ConfigureAwait(false);
if (data.Image is null)
@ -542,7 +538,6 @@ public abstract partial class Image
return action(stream);
}
// We want to be able to load images from things like HttpContext.Request.Body
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
memoryStream.Position = 0;
@ -563,7 +558,7 @@ public abstract partial class Image
internal static async Task<T> WithSeekableStreamAsync<T>(
DecoderOptions options,
Stream stream,
Func<Stream, CancellationToken, T> action,
Func<Stream, CancellationToken, Task<T>> action,
CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
@ -575,33 +570,21 @@ public abstract partial class Image
}
Configuration configuration = options.Configuration;
if (stream.CanSeek && configuration.ReadOrigin == ReadOrigin.Begin)
if (stream.CanSeek)
{
stream.Position = 0;
if (configuration.ReadOrigin == ReadOrigin.Begin)
{
stream.Position = 0;
}
// NOTE: We are explicitly not executing the action against the stream here as we do in WithSeekableStream() because that
// would incur synchronous IO reads which must be avoided in this asynchronous method. Instead, we will *always* run the
// code below to copy the stream to an in-memory buffer before invoking the action.
return await action(stream, cancellationToken).ConfigureAwait(false);
}
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
T Action(Stream ms, CancellationToken ct)
{
// Reset the position of the seekable stream if we did not read to the end
// to allow additional reads.
T result = action(ms, ct);
if (stream.CanSeek && ms.Position != ms.Length)
{
stream.Position = ms.Position;
}
return result;
}
return Action(memoryStream, cancellationToken);
return await action(memoryStream, cancellationToken).ConfigureAwait(false);
}
[DoesNotReturn]

5
src/ImageSharp/Image.cs

@ -41,6 +41,11 @@ public abstract partial class Image : IImage, IConfigurationProvider
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="pixelType">The <see cref="PixelTypeInfo"/>.</param>
/// <param name="metadata">The <see cref="ImageMetadata"/>.</param>
/// <param name="width">The width in px units.</param>
/// <param name="height">The height in px units.</param>
internal Image(
Configuration configuration,
PixelTypeInfo pixelType,

12
src/ImageSharp/ImageExtensions.cs

@ -48,10 +48,8 @@ public static partial class ImageExtensions
{
Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
{
source.Save(fs, encoder);
}
using Stream fs = source.GetConfiguration().FileSystem.Create(path);
source.Save(fs, encoder);
}
/// <summary>
@ -73,10 +71,8 @@ public static partial class ImageExtensions
Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
{
await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
}
using Stream fs = source.GetConfiguration().FileSystem.Create(path);
await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
}
/// <summary>

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

@ -16,7 +16,7 @@ public class DecodeJpeg
private void GenericSetup(string imageSubpath)
{
this.decoder = new JpegDecoder();
this.decoder = JpegDecoder.Instance;
byte[] bytes = File.ReadAllBytes(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, imageSubpath));
this.preloadedImageStream = new MemoryStream(bytes);
}

3
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs

@ -37,8 +37,7 @@ public class DecodeJpegParseStreamOnly
{
using var memoryStream = new MemoryStream(this.jpegBytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream);
var options = new JpegDecoderOptions();
options.GeneralOptions.SkipMetadata = true;
var options = new JpegDecoderOptions() { GeneralOptions = new() { SkipMetadata = true } };
using var decoder = new JpegDecoderCore(options);
var spectralConverter = new NoopSpectralConverter();

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

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

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

@ -208,8 +208,7 @@ public class LoadResizeSaveStressRunner
TargetSize = new ImageSharpSize(this.ThumbnailSize, this.ThumbnailSize)
};
var decoder = new JpegDecoder();
using ImageSharpImage image = decoder.Decode(options, inputStream);
using ImageSharpImage image = JpegDecoder.Instance.Decode(options, inputStream);
this.LogImageProcessed(image.Width, image.Height);
// Reduce the size of the file

7
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -3,6 +3,7 @@
using Microsoft.DotNet.RemoteExecutor;
using Moq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -86,7 +87,7 @@ public class ConfigurationTests
[Fact]
public void ConstructorCallConfigureOnFormatProvider()
{
var provider = new Mock<IConfigurationModule>();
var provider = new Mock<IImageFormatConfigurationModule>();
var config = new Configuration(provider.Object);
provider.Verify(x => x.Configure(config));
@ -95,7 +96,7 @@ public class ConfigurationTests
[Fact]
public void AddFormatCallsConfig()
{
var provider = new Mock<IConfigurationModule>();
var provider = new Mock<IImageFormatConfigurationModule>();
var config = new Configuration();
config.Configure(provider.Object);
@ -178,7 +179,7 @@ public class ConfigurationTests
}
}
private class MockConfigurationModule : IConfigurationModule
private class MockConfigurationModule : IImageFormatConfigurationModule
{
public void Configure(Configuration configuration)
{

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

@ -25,8 +25,6 @@ public class BmpDecoderTests
public static readonly string[] BitfieldsBmpFiles = BitFields;
private static BmpDecoder BmpDecoder => new();
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new()
{
@ -40,7 +38,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_MiscellaneousBitmaps<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
if (TestEnvironment.IsWindows)
@ -61,7 +59,7 @@ public class BmpDecoderTests
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<Rgba32> image = provider.GetImage(BmpDecoder);
using Image<Rgba32> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider, nonContiguousBuffersStr);
if (TestEnvironment.IsWindows)
@ -81,7 +79,7 @@ public class BmpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10);
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(BmpDecoder));
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(BmpDecoder.Instance));
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
@ -90,7 +88,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBitfields<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -101,7 +99,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_Inverted<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -112,7 +110,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_1Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder());
}
@ -123,7 +121,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_2Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Reference decoder cant decode 2-bit, compare to reference output instead.
@ -135,7 +133,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_4Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -145,7 +143,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -155,7 +153,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -165,7 +163,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -175,7 +173,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_32BitV4Header_Fast<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -190,7 +188,7 @@ public class BmpDecoderTests
RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette;
BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -203,7 +201,7 @@ public class BmpDecoderTests
RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette;
BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -217,7 +215,7 @@ public class BmpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
if (TestEnvironment.IsWindows)
{
@ -232,7 +230,7 @@ public class BmpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -251,7 +249,7 @@ public class BmpDecoderTests
}
BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -272,7 +270,7 @@ public class BmpDecoderTests
}
BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black };
using Image<TPixel> image = provider.GetImage(BmpDecoder, options);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance, options);
image.DebugSave(provider);
// Neither System.Drawing nor MagickReferenceDecoder decode this file.
@ -285,7 +283,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeAlphaBitfields<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Neither System.Drawing nor MagickReferenceDecoder decode this file.
@ -298,7 +296,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -308,7 +306,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Choosing large tolerance of 6.1 here, because for some reason with the MagickReferenceDecoder the alpha channel
@ -325,7 +323,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBmpv2<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Do not validate. Reference files will fail validation.
@ -337,7 +335,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBmpv3<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -347,7 +345,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeLessThanFullPalette<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -358,7 +356,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeOversizedPalette<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
if (TestEnvironment.IsWindows)
{
@ -372,7 +370,7 @@ public class BmpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
=> Assert.Throws<InvalidImageContentException>(() =>
{
using (provider.GetImage(BmpDecoder))
using (provider.GetImage(BmpDecoder.Instance))
{
}
});
@ -384,7 +382,7 @@ public class BmpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
=> Assert.Throws<NotSupportedException>(() =>
{
using (provider.GetImage(BmpDecoder))
using (provider.GetImage(BmpDecoder.Instance))
{
}
});
@ -394,7 +392,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeAdobeBmpv3<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -404,7 +402,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
@ -414,7 +412,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBmpv4<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -425,7 +423,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecodeBmpv5<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -435,7 +433,7 @@ public class BmpDecoderTests
public void BmpDecoder_RespectsFileHeaderOffset<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -445,7 +443,7 @@ public class BmpDecoderTests
public void BmpDecoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -455,7 +453,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode4BytePerEntryPalette<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -507,8 +505,7 @@ public class BmpDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new BmpDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = BmpDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -520,7 +517,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_Os2v2XShortHeader<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Neither System.Drawing or MagickReferenceDecoder can correctly decode this file.
@ -533,7 +530,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_Os2v2Header<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// System.Drawing can not decode this image. MagickReferenceDecoder can decode it,
@ -554,7 +551,7 @@ public class BmpDecoderTests
public void BmpDecoder_CanDecode_Os2BitmapArray<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
using Image<TPixel> image = provider.GetImage(BmpDecoder.Instance);
image.DebugSave(provider);
// Neither System.Drawing or MagickReferenceDecoder can correctly decode this file.

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

@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp;
[Trait("Format", "Bmp")]
public class BmpEncoderTests
{
private static BmpDecoder BmpDecoder => new();
private static BmpEncoder BmpEncoder => new();
public static readonly TheoryData<BmpBitsPerPixel> BitsPerPixel =
@ -200,7 +198,7 @@ public class BmpEncoderTests
// arrange
var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel };
using var memoryStream = new MemoryStream();
using Image<TPixel> input = provider.GetImage(BmpDecoder);
using Image<TPixel> input = provider.GetImage(BmpDecoder.Instance);
// act
encoder.Encode(input, memoryStream);
@ -222,7 +220,7 @@ public class BmpEncoderTests
// arrange
var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel };
using var memoryStream = new MemoryStream();
using Image<TPixel> input = provider.GetImage(BmpDecoder);
using Image<TPixel> input = provider.GetImage(BmpDecoder.Instance);
// act
encoder.Encode(input, memoryStream);
@ -280,7 +278,7 @@ public class BmpEncoderTests
// We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
@ -311,7 +309,7 @@ public class BmpEncoderTests
// We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using FileStream stream = File.OpenRead(actualOutputFile);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream, default);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, stream);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
@ -331,7 +329,7 @@ public class BmpEncoderTests
public void Encode_PreservesColorProfile<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> input = provider.GetImage(new BmpDecoder(), new());
using Image<TPixel> input = provider.GetImage(BmpDecoder.Instance, new());
ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile;
byte[] expectedProfileBytes = expectedProfile.ToByteArray();

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

@ -51,7 +51,7 @@ public class BmpMetadataTests
public void Decoder_CanReadColorProfile<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new BmpDecoder()))
using (Image<TPixel> image = provider.GetImage(BmpDecoder.Instance))
{
ImageSharp.Metadata.ImageMetadata metaData = image.Metadata;
Assert.NotNull(metaData);

27
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities;
namespace SixLabors.ImageSharp.Tests.Formats;
@ -47,7 +48,7 @@ public class GeneralFormatTests
}
[Fact]
public void ReadOriginIsRespectedOnLoad()
public void ChainedReadOriginIsRespectedForSeekableStreamsOnLoad()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using Image<Rgb24> i = Image.Load<Rgb24>(stream);
@ -62,7 +63,18 @@ public class GeneralFormatTests
}
[Fact]
public async Task ReadOriginIsRespectedOnLoadAsync()
public void ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using NonSeekableStream wrapper = new(stream);
using Image<Rgb24> i = Image.Load<Rgb24>(wrapper);
Assert.Equal(stream.Length, stream.Position);
Assert.Throws<UnknownImageFormatException>(() => { using Image<Rgb24> j = Image.Load<Rgb24>(wrapper); });
}
[Fact]
public async Task ChainedReadOriginIsRespectedForSeekableStreamsOnLoadAsync()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using Image<Rgb24> i = await Image.LoadAsync<Rgb24>(stream);
@ -76,6 +88,17 @@ public class GeneralFormatTests
Assert.NotEqual(i[5, 5], j[5, 5]);
}
[Fact]
public async Task ChainedReadOnLoadNonSeekable_ThrowsUnknownImageFormatException_Async()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using NonSeekableStream wrapper = new(stream);
using Image<Rgb24> i = await Image.LoadAsync<Rgb24>(wrapper);
Assert.Equal(stream.Length, stream.Position);
await Assert.ThrowsAsync<UnknownImageFormatException>(async () => { using Image<Rgb24> j = await Image.LoadAsync<Rgb24>(wrapper); });
}
[Fact]
public void ImageCanEncodeToString()
{

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

@ -18,8 +18,6 @@ public class GifDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
private static GifDecoder GifDecoder => new();
public static readonly string[] MultiFrameTestFiles =
{
TestImages.Gif.Giphy, TestImages.Gif.Kumin
@ -46,7 +44,7 @@ public class GifDecoderTests
MaxFrames = 1
};
using Image<TPixel> image = provider.GetImage(GifDecoder, options);
using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -68,7 +66,7 @@ public class GifDecoderTests
fixed (byte* data = testFile.Bytes.AsSpan(0, length))
{
using var stream = new UnmanagedMemoryStream(data, length);
using Image<Rgba32> image = GifDecoder.Decode<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = GifDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
Assert.Equal((200, 200), (image.Width, image.Height));
}
}
@ -102,7 +100,7 @@ public class GifDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { MaxFrames = 1 };
using Image<TPixel> image = provider.GetImage(new GifDecoder(), options);
using Image<TPixel> image = provider.GetImage(GifDecoder.Instance, options);
Assert.Equal(1, image.Frames.Count);
}
@ -111,7 +109,7 @@ public class GifDecoderTests
public void CanDecodeAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(new GifDecoder());
using Image<TPixel> image = provider.GetImage(GifDecoder.Instance);
Assert.True(image.Frames.Count > 1);
}
@ -137,7 +135,7 @@ public class GifDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(GifDecoder);
using Image<TPixel> image = provider.GetImage(GifDecoder.Instance);
});
Assert.NotNull(ex);
Assert.Contains("Width or height should not be 0", ex.Message);
@ -149,7 +147,7 @@ public class GifDecoderTests
public void Decode_WithMaxDimensions_Works<TPixel>(TestImageProvider<TPixel> provider, int expectedWidth, int expectedHeight)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(GifDecoder);
using Image<TPixel> image = provider.GetImage(GifDecoder.Instance);
Assert.Equal(expectedWidth, image.Width);
Assert.Equal(expectedHeight, image.Height);
}
@ -216,7 +214,7 @@ public class GifDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10);
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(GifDecoder));
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(GifDecoder.Instance));
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
@ -233,7 +231,7 @@ public class GifDecoderTests
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<Rgba32> image = provider.GetImage(GifDecoder);
using Image<Rgba32> image = provider.GetImage(GifDecoder.Instance);
image.DebugSave(provider, nonContiguousBuffersStr);
image.CompareToOriginal(provider);
}

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

@ -56,7 +56,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(TestImages.Gif.Rings);
using Image<Rgba32> image = testFile.CreateRgba32Image(new GifDecoder());
using Image<Rgba32> image = testFile.CreateRgba32Image(GifDecoder.Instance);
GifMetadata metadata = image.Metadata.GetGifMetadata();
Assert.Equal(1, metadata.Comments.Count);
Assert.Equal("ImageSharp", metadata.Comments[0]);
@ -72,7 +72,7 @@ public class GifMetadataTests
var testFile = TestFile.Create(TestImages.Gif.Rings);
using Image<Rgba32> image = testFile.CreateRgba32Image(new GifDecoder(), options);
using Image<Rgba32> image = testFile.CreateRgba32Image(GifDecoder.Instance, options);
GifMetadata metadata = image.Metadata.GetGifMetadata();
Assert.Equal(0, metadata.Comments.Count);
}
@ -82,7 +82,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(TestImages.Gif.LargeComment);
using Image<Rgba32> image = testFile.CreateRgba32Image(new GifDecoder());
using Image<Rgba32> image = testFile.CreateRgba32Image(GifDecoder.Instance);
GifMetadata metadata = image.Metadata.GetGifMetadata();
Assert.Equal(2, metadata.Comments.Count);
Assert.Equal(new string('c', 349), metadata.Comments[0]);
@ -92,7 +92,7 @@ public class GifMetadataTests
[Fact]
public void Encode_PreservesTextData()
{
var decoder = new GifDecoder();
var decoder = GifDecoder.Instance;
var testFile = TestFile.Create(TestImages.Gif.LargeComment);
using Image<Rgba32> input = testFile.CreateRgba32Image(decoder);
@ -113,8 +113,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -127,8 +126,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream);
IImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -141,8 +139,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = GifDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -155,8 +152,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
using Image<Rgba32> image = await decoder.DecodeAsync<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = await GifDecoder.Instance.DecodeAsync<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -169,8 +165,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream);
GifMetadata meta = image.Metadata.GetGifMetadata();
Assert.Equal(repeatCount, meta.RepeatCount);
}
@ -181,8 +176,7 @@ public class GifMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new GifDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = GifDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
GifMetadata meta = image.Metadata.GetGifMetadata();
Assert.Equal(repeatCount, meta.RepeatCount);
}

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

@ -102,8 +102,8 @@ public class ImageFormatManagerTests
[Fact]
public void AddFormatCallsConfig()
{
var provider = new Mock<IConfigurationModule>();
var config = new Configuration();
Mock<IImageFormatConfigurationModule> provider = new();
Configuration config = new();
config.Configure(provider.Object);
provider.Verify(x => x.Configure(config));
@ -113,9 +113,9 @@ public class ImageFormatManagerTests
public void DetectFormatAllocatesCleanBuffer()
{
byte[] jpegImage;
using (var buffer = new MemoryStream())
using (MemoryStream buffer = new())
{
using var image = new Image<Rgba32>(100, 100);
using Image<Rgba32> image = new(100, 100);
image.SaveAsJpeg(buffer);
jpegImage = buffer.ToArray();
}

5
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -28,7 +29,7 @@ public partial class JpegDecoderTests
provider.LimitAllocatorBufferCapacity().InPixels(16_000);
}
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
provider.Utility.TestName = DecodeBaselineJpegOutputName;
@ -57,7 +58,7 @@ public partial class JpegDecoderTests
public void DecodeJpeg_WithArithmeticCoding<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Tolerant(0.002f), ReferenceDecoder);
}

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

@ -65,7 +65,7 @@ public partial class JpegDecoderTests
bool exifProfilePresent,
bool iccProfilePresent) => TestMetadataImpl(
useIdentify,
JpegDecoder,
JpegDecoder.Instance,
imagePath,
expectedPixelSize,
exifProfilePresent,
@ -77,8 +77,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder();
using Image image = decoder.Decode(DecoderOptions.Default, stream);
using Image image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -91,8 +90,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -105,8 +103,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder();
IImageInfo image = await decoder.IdentifyAsync(DecoderOptions.Default, stream);
IImageInfo image = await JpegDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -119,8 +116,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new JpegDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
}
@ -131,7 +127,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
using Image image = JpegDecoder.Decode(DecoderOptions.Default, stream);
using Image image = JpegDecoder.Instance.Decode(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
}
@ -142,7 +138,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
using Image image = await JpegDecoder.DecodeAsync(DecoderOptions.Default, stream);
using Image image = await JpegDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality);
}
@ -159,7 +155,7 @@ public partial class JpegDecoderTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo image = JpegDecoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = JpegDecoder.Instance.Identify(DecoderOptions.Default, stream);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(expectedColorType, meta.ColorType);
}
@ -173,7 +169,7 @@ public partial class JpegDecoderTests
public void Decode_DetectsCorrectColorType<TPixel>(TestImageProvider<TPixel> provider, JpegEncodingColor expectedColorType)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(expectedColorType, meta.ColorType);
}
@ -184,12 +180,12 @@ public partial class JpegDecoderTests
using var stream = new MemoryStream(testFile.Bytes, false);
if (useIdentify)
{
IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream, default);
IImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream);
test(imageInfo);
}
else
{
using Image<Rgba32> img = decoder.Decode<Rgba32>(DecoderOptions.Default, stream, default);
using Image<Rgba32> img = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
test(img);
}
}
@ -253,9 +249,9 @@ public partial class JpegDecoderTests
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
// Snake.jpg has both Exif and ICC profiles defined:
var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake);
TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake);
using Image<Rgba32> image = testFile.CreateRgba32Image(JpegDecoder, options);
using Image<Rgba32> image = testFile.CreateRgba32Image(JpegDecoder.Instance, options);
if (ignoreMetadata)
{
Assert.Null(image.Metadata.ExifProfile);
@ -273,7 +269,7 @@ public partial class JpegDecoderTests
[InlineData(true)]
public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) => TestImageInfo(
TestImages.Jpeg.Baseline.Floorplan,
JpegDecoder,
JpegDecoder.Instance,
useIdentify,
imageInfo =>
{
@ -286,7 +282,7 @@ public partial class JpegDecoderTests
[InlineData(true)]
public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) => TestImageInfo(
TestImages.Jpeg.Baseline.Jpeg420Exif,
JpegDecoder,
JpegDecoder.Instance,
useIdentify,
imageInfo =>
{
@ -301,7 +297,7 @@ public partial class JpegDecoderTests
{
Exception ex = Record.Exception(() =>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
});
Assert.Null(ex);
}
@ -313,7 +309,7 @@ public partial class JpegDecoderTests
{
Exception ex = Record.Exception(() =>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
ExifProfile clone = image.Metadata.ExifProfile.DeepClone();
});
Assert.Null(ex);

7
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using Microsoft.DotNet.RemoteExecutor;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -19,7 +20,7 @@ public partial class JpegDecoderTests
public void DecodeProgressiveJpeg<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
provider.Utility.TestName = DecodeProgressiveJpegOutputName;
@ -35,7 +36,7 @@ public partial class JpegDecoderTests
public void DecodeProgressiveJpeg_WithArithmeticCoding<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Tolerant(0.004f), ReferenceDecoder);
}
@ -51,7 +52,7 @@ public partial class JpegDecoderTests
provider.LimitAllocatorBufferCapacity().InBytesSqrt(200);
using Image<Rgb24> image = provider.GetImage(JpegDecoder);
using Image<Rgb24> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider, nonContiguousBuffersStr);
provider.Utility.TestName = DecodeProgressiveJpegOutputName;

77
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -8,7 +8,6 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit.Abstractions;
@ -64,8 +63,6 @@ public partial class JpegDecoderTests
private ITestOutputHelper Output { get; }
private static JpegDecoder JpegDecoder => new();
[Fact]
public void ParseStream_BasicPropertiesAreCorrect()
{
@ -105,7 +102,7 @@ public partial class JpegDecoderTests
public void JpegDecoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
provider.Utility.TestName = DecodeBaselineJpegOutputName;
@ -121,7 +118,7 @@ public partial class JpegDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } };
using Image<TPixel> image = provider.GetImage(JpegDecoder, options);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -143,7 +140,7 @@ public partial class JpegDecoderTests
TargetSize = new() { Width = 150, Height = 150 },
Sampler = KnownResamplers.Bicubic
};
using Image<TPixel> image = provider.GetImage(JpegDecoder, options);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -167,7 +164,7 @@ public partial class JpegDecoderTests
ResizeMode = JpegDecoderResizeMode.IdctOnly
};
using Image<TPixel> image = provider.GetImage(JpegDecoder, specializedOptions);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance, specializedOptions);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -191,7 +188,7 @@ public partial class JpegDecoderTests
ResizeMode = JpegDecoderResizeMode.ScaleOnly
};
using Image<TPixel> image = provider.GetImage(JpegDecoder, specializedOptions);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance, specializedOptions);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -215,7 +212,7 @@ public partial class JpegDecoderTests
ResizeMode = JpegDecoderResizeMode.Combined
};
using Image<TPixel> image = provider.GetImage(JpegDecoder, specializedOptions);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance, specializedOptions);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -234,7 +231,7 @@ public partial class JpegDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InBytesSqrt(10);
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(JpegDecoder));
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(JpegDecoder.Instance));
this.Output.WriteLine(ex.Message);
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
@ -246,66 +243,18 @@ public partial class JpegDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InBytesSqrt(10);
InvalidImageContentException ex = await Assert.ThrowsAsync<InvalidImageContentException>(() => provider.GetImageAsync(JpegDecoder));
InvalidImageContentException ex = await Assert.ThrowsAsync<InvalidImageContentException>(() => provider.GetImageAsync(JpegDecoder.Instance));
this.Output.WriteLine(ex.Message);
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
[Fact]
public async Task DecodeAsync_IsCancellable()
{
var cts = new CancellationTokenSource();
string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
using var pausedStream = new PausedStream(file);
pausedStream.OnWaiting(_ =>
{
cts.Cancel();
pausedStream.Release();
});
var configuration = Configuration.CreateDefaultInstance();
configuration.FileSystem = new SingleStreamFileSystem(pausedStream);
DecoderOptions options = new()
{
Configuration = configuration
};
await Assert.ThrowsAsync<TaskCanceledException>(async () =>
{
using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token);
});
}
[Fact]
public async Task Identify_IsCancellable()
{
var cts = new CancellationTokenSource();
string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
using var pausedStream = new PausedStream(file);
pausedStream.OnWaiting(_ =>
{
cts.Cancel();
pausedStream.Release();
});
var configuration = Configuration.CreateDefaultInstance();
configuration.FileSystem = new SingleStreamFileSystem(pausedStream);
DecoderOptions options = new()
{
Configuration = configuration
};
await Assert.ThrowsAsync<TaskCanceledException>(async () => await Image.IdentifyAsync(options, "someFakeFile", cts.Token));
}
[Theory]
[WithFileCollection(nameof(UnsupportedTestJpegs), PixelTypes.Rgba32)]
public void ThrowsNotSupported_WithUnsupportedJpegs<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
=> Assert.Throws<NotSupportedException>(() =>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
});
// https://github.com/SixLabors/ImageSharp/pull/1732
@ -314,7 +263,7 @@ public partial class JpegDecoderTests
public void Issue1732_DecodesWithRgbColorSpace<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -325,7 +274,7 @@ public partial class JpegDecoderTests
public void Issue2057_DecodeWorks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -336,7 +285,7 @@ public partial class JpegDecoderTests
public void Issue2133_DeduceColorSpace<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -347,7 +296,7 @@ public partial class JpegDecoderTests
public void Issue2136_DecodeWorks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}

11
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs

@ -100,10 +100,11 @@ public partial class JpegEncoderTests
Exception ex = Record.Exception(() =>
{
var encoder = new JpegEncoder();
var stream = new MemoryStream();
using Image<TPixel> image = provider.GetImage(JpegDecoder);
image.Save(stream, encoder);
using (var stream = new MemoryStream())
{
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.Save(stream, encoder);
}
});
Assert.Null(ex);
@ -162,7 +163,7 @@ public partial class JpegEncoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
using Image<TPixel> input = provider.GetImage(JpegDecoder);
using Image<TPixel> input = provider.GetImage(JpegDecoder.Instance);
using var memoryStream = new MemoryStream();
// act

2
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -13,8 +13,6 @@ public partial class JpegEncoderTests
{
private static JpegEncoder JpegEncoder => new();
private static JpegDecoder JpegDecoder => new();
private static readonly TheoryData<int> TestQualities = new()
{
40,

2
tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs

@ -108,7 +108,7 @@ public class PbmDecoderTests
TargetSize = new() { Width = 150, Height = 150 }
};
using Image<TPixel> image = provider.GetImage(new PbmDecoder(), options);
using Image<TPixel> image = provider.GetImage(PbmDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";

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

@ -70,10 +70,8 @@ public partial class PngDecoderTests
WriteChunk(memStream, chunkName);
WriteDataChunk(memStream);
var decoder = new PngDecoder();
ImageFormatException exception =
Assert.Throws<InvalidImageContentException>(() => decoder.Decode<Rgb24>(DecoderOptions.Default, memStream));
Assert.Throws<InvalidImageContentException>(() => PngDecoder.Instance.Decode<Rgb24>(DecoderOptions.Default, memStream));
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
}

66
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -19,8 +19,6 @@ public partial class PngDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
private static PngDecoder PngDecoder => new();
public static readonly string[] CommonTestImages =
{
TestImages.Png.Splash,
@ -102,7 +100,7 @@ public partial class PngDecoderTests
public void Decode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -117,7 +115,7 @@ public partial class PngDecoderTests
TargetSize = new() { Width = 150, Height = 150 }
};
using Image<TPixel> image = provider.GetImage(PngDecoder, options);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -135,7 +133,7 @@ public partial class PngDecoderTests
public void Decode_WithAverageFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -146,7 +144,7 @@ public partial class PngDecoderTests
public void Decode_WithSubFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -156,7 +154,7 @@ public partial class PngDecoderTests
public void Decode_WithUpFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -167,7 +165,7 @@ public partial class PngDecoderTests
public void Decode_WithPaethFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -178,7 +176,7 @@ public partial class PngDecoderTests
public void Decode_GrayWithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -190,7 +188,7 @@ public partial class PngDecoderTests
public void Decode_Interlaced<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -205,7 +203,7 @@ public partial class PngDecoderTests
public void Decode_Indexed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -216,7 +214,7 @@ public partial class PngDecoderTests
public void Decode_48Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -227,7 +225,7 @@ public partial class PngDecoderTests
public void Decode_64Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -240,7 +238,7 @@ public partial class PngDecoderTests
public void Decoder_L8bitInterlaced<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -250,7 +248,7 @@ public partial class PngDecoderTests
public void Decode_L16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -261,7 +259,7 @@ public partial class PngDecoderTests
public void Decode_GrayAlpha16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -271,7 +269,7 @@ public partial class PngDecoderTests
public void Decoder_CanDecode_Grey8bitInterlaced_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -281,7 +279,7 @@ public partial class PngDecoderTests
public void Decoder_CanDecode_CorruptedImages<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -291,7 +289,7 @@ public partial class PngDecoderTests
public void Decoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
@ -319,7 +317,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
});
Assert.NotNull(ex);
@ -335,7 +333,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
});
Assert.NotNull(ex);
@ -350,7 +348,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
});
Assert.Null(ex);
@ -365,7 +363,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
});
Assert.NotNull(ex);
@ -381,7 +379,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
});
Assert.NotNull(ex);
@ -396,7 +394,7 @@ public partial class PngDecoderTests
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
});
Assert.NotNull(ex);
Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message);
@ -411,7 +409,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
});
@ -427,7 +425,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
});
@ -443,7 +441,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
});
@ -459,7 +457,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
// We don't have another x-plat reference decoder that can be compared for this image.
@ -480,7 +478,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
});
@ -493,7 +491,7 @@ public partial class PngDecoderTests
public void Issue2209_Decode_HasTransparencyIsTrue<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
PngMetadata metadata = image.Metadata.GetPngMetadata();
Assert.True(metadata.HasTransparency);
@ -520,7 +518,7 @@ public partial class PngDecoderTests
Exception ex = Record.Exception(
() =>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
// We don't have another x-plat reference decoder that can be compared for this image.
@ -540,7 +538,7 @@ public partial class PngDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10);
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(PngDecoder));
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(PngDecoder.Instance));
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
@ -555,7 +553,7 @@ public partial class PngDecoderTests
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<Rgba32> image = provider.GetImage(PngDecoder);
using Image<Rgba32> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
image.CompareToOriginal(provider);
}

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

@ -284,9 +284,7 @@ public partial class PngEncoderTests
stream.Seek(0, SeekOrigin.Begin);
var decoder = new PngDecoder();
Image image = decoder.Decode(DecoderOptions.Default, stream);
using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream);
PngMetadata metadata = image.Metadata.GetPngMetadata();
Assert.Equal(pngColorType, metadata.ColorType);
@ -542,7 +540,7 @@ public partial class PngEncoderTests
// https://github.com/SixLabors/ImageSharp/issues/935
using var ms = new MemoryStream();
var testFile = TestFile.Create(TestImages.Png.Issue935);
using Image<Rgba32> image = testFile.CreateRgba32Image(new PngDecoder());
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance);
image.Save(ms, new PngEncoder { ColorType = PngColorType.RgbWithAlpha });
}
@ -594,11 +592,11 @@ public partial class PngEncoderTests
// occurs within the encoder itself leaving the input image unaffected.
// This means we are benefiting from testing our decoder also.
using FileStream fileStream = File.OpenRead(actualOutputFile);
using Image<TPixel> imageSharpImage = new PngDecoder().Decode<TPixel>(DecoderOptions.Default, fileStream);
using Image<TPixel> imageSharpImage = PngDecoder.Instance.Decode<TPixel>(DecoderOptions.Default, fileStream);
fileStream.Position = 0;
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, fileStream, default);
using Image<TPixel> referenceImage = referenceDecoder.Decode<TPixel>(DecoderOptions.Default, fileStream);
ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage);
}
}

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

@ -52,7 +52,7 @@ public class PngMetadataTests
public void Decoder_CanReadTextData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(new PngDecoder());
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
VerifyTextDataIsPresent(meta);
}
@ -62,13 +62,12 @@ public class PngMetadataTests
public void Encoder_PreservesTextData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new PngDecoder();
using Image<TPixel> input = provider.GetImage(decoder);
using Image<TPixel> input = provider.GetImage(PngDecoder.Instance);
using var memoryStream = new MemoryStream();
input.Save(memoryStream, new PngEncoder());
memoryStream.Position = 0;
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
using Image<Rgba32> image = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
VerifyTextDataIsPresent(meta);
}
@ -78,7 +77,7 @@ public class PngMetadataTests
public void Decoder_IgnoresInvalidTextData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(new PngDecoder());
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.DoesNotContain(meta.TextData, m => m.Value is "leading space");
Assert.DoesNotContain(meta.TextData, m => m.Value is "trailing space");
@ -93,8 +92,7 @@ public class PngMetadataTests
public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new PngDecoder();
using Image<TPixel> input = provider.GetImage(decoder);
using Image<TPixel> input = provider.GetImage(PngDecoder.Instance);
using var memoryStream = new MemoryStream();
// This will be a zTXt chunk.
@ -111,7 +109,7 @@ public class PngMetadataTests
});
memoryStream.Position = 0;
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
using Image<Rgba32> image = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, memoryStream);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Contains(meta.TextData, m => m.Equals(expectedText));
Assert.Contains(meta.TextData, m => m.Equals(expectedTextNoneLatin));
@ -127,7 +125,7 @@ public class PngMetadataTests
SkipMetadata = false
};
using Image<TPixel> image = provider.GetImage(new PngDecoder(), options);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, options);
Assert.NotNull(image.Metadata.ExifProfile);
ExifProfile exif = image.Metadata.ExifProfile;
VerifyExifDataIsPresent(exif);
@ -143,9 +141,7 @@ public class PngMetadataTests
SkipMetadata = true
};
PngDecoder decoder = new();
using Image<TPixel> image = provider.GetImage(decoder, options);
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, options);
Assert.Null(image.Metadata.ExifProfile);
}
@ -159,7 +155,7 @@ public class PngMetadataTests
var testFile = TestFile.Create(TestImages.Png.Blur);
using Image<Rgba32> image = testFile.CreateRgba32Image(new PngDecoder(), options);
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance, options);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Equal(1, meta.TextData.Count);
@ -178,7 +174,7 @@ public class PngMetadataTests
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata);
using Image<Rgba32> image = testFile.CreateRgba32Image(new PngDecoder(), options);
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance, options);
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Equal(0, meta.TextData.Count);
}
@ -189,8 +185,7 @@ public class PngMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new PngDecoder();
using Image<Rgba32> image = decoder.Decode<Rgba32>(DecoderOptions.Default, stream);
using Image<Rgba32> image = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
@ -202,7 +197,7 @@ public class PngMetadataTests
public void Encode_PreservesColorProfile<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> input = provider.GetImage(new PngDecoder());
using Image<TPixel> input = provider.GetImage(PngDecoder.Instance);
ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile;
byte[] expectedProfileBytes = expectedProfile.ToByteArray();
@ -224,8 +219,7 @@ public class PngMetadataTests
{
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
var decoder = new PngDecoder();
IImageInfo image = decoder.Identify(DecoderOptions.Default, stream);
IImageInfo image = PngDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);

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

@ -24,7 +24,7 @@ public class PngSmokeTests
// image.Save(provider.Utility.GetTestOutputFileName("bmp"));
image.Save(ms, new PngEncoder());
ms.Position = 0;
using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms);
using Image<Rgba32> img2 = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, ms);
ImageComparer.Tolerant().VerifySimilarity(image, img2);
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
@ -45,7 +45,7 @@ public class PngSmokeTests
// image.Save(provider.Utility.GetTestOutputFileName("png", "resize"));
image.Save(ms, new PngEncoder());
ms.Position = 0;
using Image<Rgba32> img2 = new PngDecoder().Decode<Rgba32>(DecoderOptions.Default, ms);
using Image<Rgba32> img2 = PngDecoder.Instance.Decode<Rgba32>(DecoderOptions.Default, ms);
ImageComparer.Tolerant().VerifySimilarity(image, img2);
}
}

124
tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs

@ -17,14 +17,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga;
[ValidateDisposedMemoryAllocations]
public class TgaDecoderTests
{
private static TgaDecoder TgaDecoder => new();
[Theory]
[WithFile(Gray8BitTopLeft, PixelTypes.Rgba32)]
public void TgaDecoder_CanDecode_Gray_WithTopLeftOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -36,7 +34,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -48,7 +46,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -60,7 +58,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -72,7 +70,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopLeftOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -84,7 +82,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -96,7 +94,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -108,7 +106,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_8Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -120,7 +118,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -135,7 +133,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithBottomLeftOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -150,7 +148,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithBottomRightOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -165,7 +163,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Gray_WithTopRightOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -180,7 +178,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -195,7 +193,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -210,7 +208,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -225,7 +223,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
@ -240,7 +238,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_15Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -252,7 +250,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_15Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -264,7 +262,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithBottomLeftOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -276,7 +274,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithPalette_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -288,7 +286,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithTopLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -300,7 +298,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithBottomLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -312,7 +310,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithTopRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -324,7 +322,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithBottomRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -336,7 +334,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -348,7 +346,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -360,7 +358,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -372,7 +370,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_Palette_WithTopLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -384,7 +382,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithTopLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -396,7 +394,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -408,7 +406,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithBottomLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -420,7 +418,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithBottomRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -432,7 +430,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -444,7 +442,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -456,7 +454,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -468,7 +466,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -480,7 +478,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -492,7 +490,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -504,7 +502,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_Paletted_WithTopLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -516,7 +514,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -528,7 +526,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_WithTopRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -540,7 +538,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_Paletted_WithBottomRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -552,7 +550,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -564,7 +562,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPaletteTopLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -576,7 +574,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPaletteTopRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -588,7 +586,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -600,7 +598,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPaletteBottomRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -612,7 +610,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_WithPaletteTopLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -624,7 +622,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_WithPaletteTopRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -636,7 +634,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_WithPaletteBottomLeftOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -648,7 +646,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_RLE_WithPaletteBottomRightOrigin_24Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -660,7 +658,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPalette_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -672,7 +670,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPalette_WithBottomLeftOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -684,7 +682,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPalette_WithBottomRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -696,7 +694,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WithPalette_WithTopRightOrigin_32Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -709,7 +707,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet_16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -722,7 +720,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
// Using here the reference output instead of the the reference decoder,
// because the reference decoder does not ignore the alpha data here.
@ -738,7 +736,7 @@ public class TgaDecoderTests
public void TgaDecoder_CanDecode_LegacyFormat<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TgaDecoder))
using (Image<TPixel> image = provider.GetImage(TgaDecoder.Instance))
{
image.DebugSave(provider);
ImageComparingUtils.CompareWithReferenceDecoder(provider, image);
@ -755,7 +753,7 @@ public class TgaDecoderTests
TargetSize = new() { Width = 150, Height = 150 }
};
using Image<TPixel> image = provider.GetImage(TgaDecoder, options);
using Image<TPixel> image = provider.GetImage(TgaDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -775,7 +773,7 @@ public class TgaDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(10);
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(TgaDecoder));
InvalidImageContentException ex = Assert.Throws<InvalidImageContentException>(() => provider.GetImage(TgaDecoder.Instance));
Assert.IsType<InvalidMemoryOperationException>(ex.InnerException);
}
@ -790,7 +788,7 @@ public class TgaDecoderTests
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<Rgba32> image = provider.GetImage(TgaDecoder);
using Image<Rgba32> image = provider.GetImage(TgaDecoder.Instance);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
if (TestEnvironment.IsWindows)

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

@ -39,7 +39,7 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester
{
Assert.Throws<ImageDifferenceIsOverThresholdException>(() => TestTiffDecoder(provider));
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
ExifProfile exif = image.Frames.RootFrame.Metadata.ExifProfile;
// PhotometricInterpretation is required tag: https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
@ -104,7 +104,7 @@ public class BigTiffDecoderTests : TiffDecoderBaseTester
public void TiffDecoder_SubIfd8<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
ExifProfile meta = image.Frames.RootFrame.Metadata.ExifProfile;

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

@ -12,14 +12,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff;
public abstract class TiffDecoderBaseTester
{
protected static TiffDecoder TiffDecoder => new();
protected static MagickReferenceDecoder ReferenceDecoder => new();
protected static void TestTiffDecoder<TPixel>(TestImageProvider<TPixel> provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(
provider,

26
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -21,7 +21,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
[WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)]
[WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)]
public void ThrowsNotSupported<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<NotSupportedException>(() => provider.GetImage(TiffDecoder));
where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<NotSupportedException>(() => provider.GetImage(TiffDecoder.Instance));
[Theory]
[InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)]
@ -201,7 +201,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -258,7 +258,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -285,7 +285,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -387,7 +387,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -427,7 +427,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -466,7 +466,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -496,7 +496,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
if (TestEnvironment.IsMacOS)
{
// Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
image.DebugSave(provider);
return;
}
@ -667,7 +667,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { MaxFrames = 1 };
using Image<TPixel> image = provider.GetImage(new TiffDecoder(), options);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance, options);
Assert.Equal(1, image.Frames.Count);
}
@ -696,7 +696,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
{
MaxFrames = 1
};
using Image<TPixel> image = provider.GetImage(TiffDecoder, decoderOptions);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance, decoderOptions);
image.DebugSave(provider);
image.CompareToOriginal(
provider,
@ -723,7 +723,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<ImageFormatException>(
() =>
{
using (provider.GetImage(TiffDecoder))
using (provider.GetImage(TiffDecoder.Instance))
{
}
});
@ -739,7 +739,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
public void DecodeMultiframe<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
Assert.True(image.Frames.Count > 1);
image.DebugSave(provider);
@ -759,7 +759,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
TargetSize = new() { Width = 150, Height = 150 }
};
using Image<TPixel> image = provider.GetImage(TiffDecoder, options);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";

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

@ -295,7 +295,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester
[Theory]
[WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)]
public void TiffEncoder_EncodePlanar_AndReload_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, imageDecoder: new TiffDecoder());
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, imageDecoder: TiffDecoder.Instance);
[Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]

14
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff;
[Trait("Format", "Tiff")]
public class TiffMetadataTests
{
private static TiffDecoder TiffDecoder => new();
private class NumberComparer : IEqualityComparer<Number>
{
public bool Equals(Number x, Number y) => x.Equals(y);
@ -46,7 +44,7 @@ public class TiffMetadataTests
public void TiffFrameMetadata_CloneIsDeep<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone();
VerifyExpectedTiffFrameMetaDataIsPresent(cloneSameAsSampleMetaData);
@ -113,7 +111,7 @@ public class TiffMetadataTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
using Image<TPixel> image = provider.GetImage(new TiffDecoder(), options);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance, options);
TiffMetadata meta = image.Metadata.GetTiffMetadata();
ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata;
@ -137,7 +135,7 @@ public class TiffMetadataTests
public void CanDecodeImage_WithIptcDataAsLong<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
IptcProfile iptcProfile = image.Frames.RootFrame.Metadata.IptcProfile;
Assert.NotNull(iptcProfile);
@ -151,7 +149,7 @@ public class TiffMetadataTests
public void BaselineTags<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
ImageFrame<TPixel> rootFrame = image.Frames.RootFrame;
Assert.Equal(32, rootFrame.Width);
Assert.Equal(32, rootFrame.Height);
@ -208,7 +206,7 @@ public class TiffMetadataTests
public void SubfileType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
TiffMetadata meta = image.Metadata.GetTiffMetadata();
Assert.NotNull(meta);
@ -232,7 +230,7 @@ public class TiffMetadataTests
{
// Load Tiff image
DecoderOptions options = new() { SkipMetadata = false };
using Image<TPixel> image = provider.GetImage(TiffDecoder, options);
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance, options);
ImageMetadata inputMetaData = image.Metadata;
ImageFrame<TPixel> rootFrameInput = image.Frames.RootFrame;

60
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp;
[ValidateDisposedMemoryAllocations]
public class WebpDecoderTests
{
private static WebpDecoder WebpDecoder => new();
private static MagickReferenceDecoder ReferenceDecoder => new();
private static string TestImageLossyHorizontalFilterPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.AlphaCompressedHorizontalFilter);
@ -63,7 +61,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithoutFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -77,7 +75,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithSimpleFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -98,7 +96,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithComplexFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -111,7 +109,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_VerySmall<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -128,7 +126,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithPartitions<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -140,7 +138,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithSegmentation<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -155,7 +153,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -178,7 +176,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossy_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -188,7 +186,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -199,7 +197,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithoutTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -216,7 +214,7 @@ public class WebpDecoderTests
TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -230,7 +228,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -241,7 +239,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithPredictorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -252,7 +250,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -274,7 +272,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithTwoTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -291,7 +289,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -301,7 +299,7 @@ public class WebpDecoderTests
public void Decode_AnimatedLossless_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
@ -318,7 +316,7 @@ public class WebpDecoderTests
public void Decode_AnimatedLossy_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
@ -336,7 +334,7 @@ public class WebpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { MaxFrames = 1 };
using Image<TPixel> image = provider.GetImage(new WebpDecoder(), options);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, options);
Assert.Equal(1, image.Frames.Count);
}
@ -348,7 +346,7 @@ public class WebpDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
// Just make sure no exception is thrown. The reference decoder fails to load the image.
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
}
@ -362,7 +360,7 @@ public class WebpDecoderTests
TargetSize = new() { Width = 150, Height = 150 }
};
using Image<TPixel> image = provider.GetImage(WebpDecoder, options);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, options);
FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}";
@ -380,7 +378,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Issue1594<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -391,7 +389,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Issue2243<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -402,7 +400,7 @@ public class WebpDecoderTests
public void WebpDecoder_CanDecode_Issue2257<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -414,7 +412,7 @@ public class WebpDecoderTests
Assert.Throws<ImageFormatException>(
() =>
{
using (provider.GetImage(WebpDecoder))
using (provider.GetImage(WebpDecoder.Instance))
{
}
});
@ -422,7 +420,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithHorizontalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -430,7 +428,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithVerticalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -438,7 +436,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithSimpleFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
@ -446,7 +444,7 @@ public class WebpDecoderTests
private static void RunDecodeLossyWithComplexFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}

16
tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp;
[Trait("Format", "Webp")]
public class WebpMetaDataTests
{
private static WebpDecoder WebpDecoder => new();
[Theory]
[WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, false)]
[WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, true)]
@ -21,7 +19,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
using Image<TPixel> image = provider.GetImage(WebpDecoder, options);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, options);
if (ignoreMetadata)
{
Assert.Null(image.Metadata.ExifProfile);
@ -42,7 +40,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
using Image<TPixel> image = provider.GetImage(WebpDecoder, options);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, options);
if (ignoreMetadata)
{
Assert.Null(image.Metadata.ExifProfile);
@ -67,7 +65,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
using Image<TPixel> image = provider.GetImage(WebpDecoder, options);
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, options);
if (ignoreMetadata)
{
Assert.Null(image.Metadata.IccProfile);
@ -86,7 +84,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions options = new() { SkipMetadata = ignoreMetadata };
using Image<TPixel> image = await provider.GetImageAsync(WebpDecoder, options);
using Image<TPixel> image = await provider.GetImageAsync(WebpDecoder.Instance, options);
if (ignoreMetadata)
{
Assert.Null(image.Metadata.XmpProfile);
@ -129,7 +127,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
using Image<TPixel> input = provider.GetImage(WebpDecoder);
using Image<TPixel> input = provider.GetImage(WebpDecoder.Instance);
using var memoryStream = new MemoryStream();
ExifProfile expectedExif = input.Metadata.ExifProfile;
@ -150,7 +148,7 @@ public class WebpMetaDataTests
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
using Image<TPixel> input = provider.GetImage(WebpDecoder);
using Image<TPixel> input = provider.GetImage(WebpDecoder.Instance);
using var memoryStream = new MemoryStream();
ExifProfile expectedExif = input.Metadata.ExifProfile;
@ -171,7 +169,7 @@ public class WebpMetaDataTests
public void Encode_PreservesColorProfile<TPixel>(TestImageProvider<TPixel> provider, WebpFileFormatType fileFormat)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> input = provider.GetImage(WebpDecoder);
using Image<TPixel> input = provider.GetImage(WebpDecoder.Instance);
ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile;
byte[] expectedProfileBytes = expectedProfile.ToByteArray();

4
tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs

@ -14,8 +14,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp;
[Trait("Format", "Webp")]
public class YuvConversionTests
{
private static WebpDecoder WebpDecoder => new();
private static MagickReferenceDecoder ReferenceDecoder => new();
private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Webp.Lossy.NoFilter06);
@ -23,7 +21,7 @@ public class YuvConversionTests
public static void RunUpSampleYuvToRgbTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyFullPath);
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
using Image<Rgba32> image = provider.GetImage(WebpDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}

180
tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs

@ -2,7 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
namespace SixLabors.ImageSharp.Tests;
@ -10,153 +10,89 @@ public partial class ImageTests
{
public class Decode_Cancellation : ImageLoadTestBase
{
private bool isTestStreamSeekable;
private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new(0);
private readonly SemaphoreSlim continueSemaphore = new(0);
private readonly CancellationTokenSource cts = new();
public Decode_Cancellation() => this.TopLevelConfiguration.StreamProcessingBufferSize = 128;
[Theory]
[InlineData(false)]
[InlineData(true)]
public Task LoadAsync_Specific_Stream(bool isInputStreamSeekable)
public static readonly string[] TestFileForEachCodec = new[]
{
this.isTestStreamSeekable = isInputStreamSeekable;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
TestImages.Jpeg.Baseline.Snake,
DecoderOptions options = new()
{
Configuration = this.TopLevelConfiguration
};
// TODO: Figure out Unix cancellation failures, and validate cancellation for each decoder.
//TestImages.Bmp.Car,
//TestImages.Png.Bike,
//TestImages.Tiff.RgbUncompressed,
//TestImages.Gif.Kumin,
//TestImages.Tga.Bit32BottomRight,
//TestImages.Webp.Lossless.WithExif,
//TestImages.Pbm.GrayscaleBinaryWide
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync<Rgb24>(options, this.DataStream, this.cts.Token));
}
public static object[][] IdentifyData { get; } = TestFileForEachCodec.Select(f => new object[] { f }).ToArray();
[Theory]
[InlineData(false)]
[InlineData(true)]
public Task LoadAsync_Agnostic_Stream(bool isInputStreamSeekable)
[MemberData(nameof(IdentifyData))]
public async Task IdentifyAsync_PreCancelled(string file)
{
this.isTestStreamSeekable = isInputStreamSeekable;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
DecoderOptions options = new()
{
Configuration = this.TopLevelConfiguration
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync(options, this.DataStream, this.cts.Token));
using FileStream fs = File.OpenRead(TestFile.GetInputFileFullPath(file));
CancellationToken preCancelled = new(canceled: true);
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await Image.IdentifyAsync(fs, preCancelled));
}
[Fact]
public Task LoadAsync_Agnostic_Path()
private static TheoryData<bool, string, double> CreateLoadData()
{
this.isTestStreamSeekable = true;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
double[] percentages = new[] { 0, 0.3, 0.7 };
DecoderOptions options = new()
{
Configuration = this.TopLevelConfiguration
};
TheoryData<bool, string, double> data = new();
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token));
}
[Fact]
public Task LoadAsync_Specific_Path()
{
this.isTestStreamSeekable = true;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
DecoderOptions options = new()
foreach (string file in TestFileForEachCodec)
{
Configuration = this.TopLevelConfiguration
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync<Rgb24>(options, this.MockFilePath, this.cts.Token));
foreach (double p in percentages)
{
data.Add(false, file, p);
data.Add(true, file, p);
}
}
return data;
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public Task IdentifyAsync_Stream(bool isInputStreamSeekable)
{
this.isTestStreamSeekable = isInputStreamSeekable;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
DecoderOptions options = new()
{
Configuration = this.TopLevelConfiguration
};
public static TheoryData<bool, string, double> LoadData { get; } = CreateLoadData();
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyAsync(options, this.DataStream, this.cts.Token));
}
[Fact]
public Task IdentifyAsync_CustomConfiguration_Path()
// TODO: Figure out cancellation failures on Linux
[ConditionalTheory(typeof(TestEnvironment), nameof(TestEnvironment.IsWindows))]
[MemberData(nameof(LoadData))]
public async Task LoadAsync_IsCancellable(bool useMemoryStream, string file, double percentageOfStreamReadToCancel)
{
this.isTestStreamSeekable = true;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
CancellationTokenSource cts = new();
using IPausedStream pausedStream = useMemoryStream ?
new PausedMemoryStream(TestFile.Create(file).Bytes) :
new PausedStream(TestFile.GetInputFileFullPath(file));
DecoderOptions options = new()
pausedStream.OnWaiting(s =>
{
Configuration = this.TopLevelConfiguration
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyAsync(options, this.MockFilePath, this.cts.Token));
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public Task IdentifyWithFormatAsync_CustomConfiguration_Stream(bool isInputStreamSeekable)
{
this.isTestStreamSeekable = isInputStreamSeekable;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
if (s.Position >= s.Length * percentageOfStreamReadToCancel)
{
cts.Cancel();
pausedStream.Release();
}
else
{
pausedStream.Next();
}
});
Configuration configuration = Configuration.CreateDefaultInstance();
configuration.FileSystem = new SingleStreamFileSystem((Stream)pausedStream);
configuration.StreamProcessingBufferSize = (int)Math.Min(128, pausedStream.Length / 4);
DecoderOptions options = new()
{
Configuration = this.TopLevelConfiguration
Configuration = configuration
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(options, this.DataStream, this.cts.Token));
}
[Fact]
public Task IdentifyWithFormatAsync_CustomConfiguration_Path()
{
this.isTestStreamSeekable = true;
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
DecoderOptions options = new()
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
{
Configuration = this.TopLevelConfiguration
};
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(options, this.MockFilePath, this.cts.Token));
}
[Fact]
public Task IdentifyWithFormatAsync_DefaultConfiguration_Stream()
{
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning);
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(this.DataStream, this.cts.Token));
using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token);
});
}
private async Task DoCancel()
{
// wait until we reach the middle of the steam
await this.notifyWaitPositionReachedSemaphore.WaitAsync();
// set the cancellation
this.cts.Cancel();
// continue processing the stream
this.continueSemaphore.Release();
}
protected override Stream CreateStream() => this.TestFormat.CreateAsyncSemaphoreStream(this.notifyWaitPositionReachedSemaphore, this.continueSemaphore, this.isTestStreamSeekable);
}
}

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

@ -59,14 +59,16 @@ public partial class ImageTests
this.localImageInfoMock = new Mock<IImageInfo>();
this.localImageFormatMock = new Mock<IImageFormat>();
var detector = new Mock<IImageInfoDetector>();
detector.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
this.localDecoder = new Mock<IImageDecoder>();
this.localDecoder.Setup(x => x.Identify(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Returns(this.localImageInfoMock.Object);
this.localDecoder = detector.As<IImageDecoder>();
this.localDecoder.Setup(x => x.IdentifyAsync(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(this.localImageInfoMock.Object));
this.localDecoder
.Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((c, s, ct) =>
.Setup(x => x.Decode<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Callback<DecoderOptions, Stream>((c, s) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
@ -75,8 +77,8 @@ public partial class ImageTests
.Returns(this.localStreamReturnImageRgba32);
this.localDecoder
.Setup(x => x.Decode(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((c, s, ct) =>
.Setup(x => x.Decode(It.IsAny<DecoderOptions>(), It.IsAny<Stream>()))
.Callback<DecoderOptions, Stream>((c, s) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
@ -84,6 +86,26 @@ public partial class ImageTests
})
.Returns(this.localStreamReturnImageAgnostic);
this.localDecoder
.Setup(x => x.DecodeAsync<Rgba32>(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((_, s, _) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})
.Returns(Task.FromResult(this.localStreamReturnImageRgba32));
this.localDecoder
.Setup(x => x.DecodeAsync(It.IsAny<DecoderOptions>(), It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback<DecoderOptions, Stream, CancellationToken>((_, s, _) =>
{
using var ms = new MemoryStream();
s.CopyTo(ms);
this.DecodedData = ms.ToArray();
})
.Returns(Task.FromResult<Image>(this.localStreamReturnImageAgnostic));
this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object);
this.LocalConfiguration = new Configuration();

64
tests/ImageSharp.Tests/Image/NonSeekableStream.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Tests;
@ -16,17 +16,73 @@ internal class NonSeekableStream : Stream
public override bool CanWrite => false;
public override bool CanTimeout => this.dataStream.CanTimeout;
public override long Length => throw new NotSupportedException();
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override int ReadTimeout
{
get => this.dataStream.ReadTimeout;
set => this.dataStream.ReadTimeout = value;
}
public override int WriteTimeout
{
get => this.dataStream.WriteTimeout;
set => this.dataStream.WriteTimeout = value;
}
public override void Flush() => this.dataStream.Flush();
public override int Read(byte[] buffer, int offset, int count) => this.dataStream.Read(buffer, offset, count);
public override int ReadByte() => this.dataStream.ReadByte();
public override int Read(byte[] buffer, int offset, int count)
=> this.dataStream.Read(buffer, offset, count);
public override int Read(Span<byte> buffer)
=> this.dataStream.Read(buffer);
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
=> this.dataStream.BeginRead(buffer, offset, count, callback, state);
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
=> this.dataStream.ReadAsync(buffer, offset, count, cancellationToken);
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
=> this.dataStream.ReadAsync(buffer, cancellationToken);
public override int EndRead(IAsyncResult asyncResult)
=> this.dataStream.EndRead(asyncResult);
public override void WriteByte(byte value) => this.dataStream.WriteByte(value);
public override void Write(ReadOnlySpan<byte> buffer) => this.dataStream.Write(buffer);
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
=> this.dataStream.BeginWrite(buffer, offset, count, callback, state);
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
=> this.dataStream.WriteAsync(buffer, offset, count, cancellationToken);
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
=> this.dataStream.WriteAsync(buffer, cancellationToken);
public override void EndWrite(IAsyncResult asyncResult) => this.dataStream.EndWrite(asyncResult);
public override void CopyTo(Stream destination, int bufferSize) => this.dataStream.CopyTo(destination, bufferSize);
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
=> this.dataStream.CopyToAsync(destination, bufferSize, cancellationToken);
public override Task FlushAsync(CancellationToken cancellationToken) => this.dataStream.FlushAsync(cancellationToken);
public override void Close() => this.dataStream.Close();
public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();

12
tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

@ -10,10 +10,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC;
public class IptcProfileTests
{
private static JpegDecoder JpegDecoder => new();
private static TiffDecoder TiffDecoder => new();
public static IEnumerable<object[]> AllIptcTags()
{
foreach (object tag in Enum.GetValues(typeof(IptcTag)))
@ -117,7 +113,7 @@ public class IptcProfileTests
public void ReadIptcMetadata_FromJpg_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(JpegDecoder))
using (Image<TPixel> image = provider.GetImage(JpegDecoder.Instance))
{
Assert.NotNull(image.Metadata.IptcProfile);
var iptcValues = image.Metadata.IptcProfile.Values.ToList();
@ -130,7 +126,7 @@ public class IptcProfileTests
public void ReadIptcMetadata_FromTiff_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TiffDecoder))
using (Image<TPixel> image = provider.GetImage(TiffDecoder.Instance))
{
IptcProfile iptc = image.Frames.RootFrame.Metadata.IptcProfile;
Assert.NotNull(iptc);
@ -166,7 +162,7 @@ public class IptcProfileTests
public void ReadApp13_WithEmptyIptc_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
Assert.Null(image.Metadata.IptcProfile);
}
@ -231,7 +227,7 @@ public class IptcProfileTests
public void WritingImage_PreservesIptcProfile()
{
// arrange
var image = new Image<Rgba32>(1, 1);
using Image<Rgba32> image = new(1, 1);
image.Metadata.IptcProfile = new IptcProfile();
const string expectedCaptionWriter = "unittest";
const string expectedCaption = "test";

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save