Browse Source

Merge branch 'release/3.1.x' into js/format-conversion

pull/2751/head
James Jackson-South 2 years ago
parent
commit
c79a248d76
  1. 39
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 17
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 2
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  4. 17
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  5. 2
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  6. 50
      src/ImageSharp/Formats/IImageDecoderInternals.cs
  7. 22
      src/ImageSharp/Formats/IImageEncoderInternals.cs
  8. 4
      src/ImageSharp/Formats/ImageDecoder.cs
  9. 127
      src/ImageSharp/Formats/ImageDecoderCore.cs
  10. 81
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  11. 16
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  12. 2
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  13. 16
      src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
  14. 2
      src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
  15. 18
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  16. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  17. 13
      src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs
  18. 10
      src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs
  19. 17
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  20. 46
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  21. 4
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
  22. 15
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  23. 2
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  24. 15
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  25. 2
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  26. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  27. 14
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

39
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -10,10 +10,13 @@ using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -129,6 +132,7 @@ internal static class AotCompilerTools
AotCompileImageDecoderInternals<TPixel>(); AotCompileImageDecoderInternals<TPixel>();
AotCompileImageEncoders<TPixel>(); AotCompileImageEncoders<TPixel>();
AotCompileImageDecoders<TPixel>(); AotCompileImageDecoders<TPixel>();
AotCompileSpectralConverter<TPixel>();
AotCompileImageProcessors<TPixel>(); AotCompileImageProcessors<TPixel>();
AotCompileGenericImageProcessors<TPixel>(); AotCompileGenericImageProcessors<TPixel>();
AotCompileResamplers<TPixel>(); AotCompileResamplers<TPixel>();
@ -195,39 +199,41 @@ internal static class AotCompilerTools
=> default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext<TPixel>(default, default, default); => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext<TPixel>(default, default, default);
/// <summary> /// <summary>
/// This method pre-seeds the all <see cref="IImageEncoderInternals"/> in the AoT compiler. /// This method pre-seeds the all core encoders in the AoT compiler.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve] [Preserve]
private static void AotCompileImageEncoderInternals<TPixel>() private static void AotCompileImageEncoderInternals<TPixel>()
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
default(BmpEncoderCore).Encode<TPixel>(default, default, default); default(BmpEncoderCore).Encode<TPixel>(default, default, default);
default(GifEncoderCore).Encode<TPixel>(default, default, default); default(GifEncoderCore).Encode<TPixel>(default, default, default);
default(JpegEncoderCore).Encode<TPixel>(default, default, default); default(JpegEncoderCore).Encode<TPixel>(default, default, default);
default(PbmEncoderCore).Encode<TPixel>(default, default, default); default(PbmEncoderCore).Encode<TPixel>(default, default, default);
default(PngEncoderCore).Encode<TPixel>(default, default, default); default(PngEncoderCore).Encode<TPixel>(default, default, default);
default(QoiEncoderCore).Encode<TPixel>(default, default, default);
default(TgaEncoderCore).Encode<TPixel>(default, default, default); default(TgaEncoderCore).Encode<TPixel>(default, default, default);
default(TiffEncoderCore).Encode<TPixel>(default, default, default); default(TiffEncoderCore).Encode<TPixel>(default, default, default);
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
} }
/// <summary> /// <summary>
/// This method pre-seeds the all <see cref="IImageDecoderInternals"/> in the AoT compiler. /// This method pre-seeds the all <see cref="ImageDecoderCore"/> in the AoT compiler.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve] [Preserve]
private static void AotCompileImageDecoderInternals<TPixel>() private static void AotCompileImageDecoderInternals<TPixel>()
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
default(WebpDecoderCore).Decode<TPixel>(default, default); default(BmpDecoderCore).Decode<TPixel>(default, default, default);
default(BmpDecoderCore).Decode<TPixel>(default, default); default(GifDecoderCore).Decode<TPixel>(default, default, default);
default(GifDecoderCore).Decode<TPixel>(default, default); default(JpegDecoderCore).Decode<TPixel>(default, default, default);
default(JpegDecoderCore).Decode<TPixel>(default, default); default(PbmDecoderCore).Decode<TPixel>(default, default, default);
default(PbmDecoderCore).Decode<TPixel>(default, default); default(PngDecoderCore).Decode<TPixel>(default, default, default);
default(PngDecoderCore).Decode<TPixel>(default, default); default(QoiDecoderCore).Decode<TPixel>(default, default, default);
default(TgaDecoderCore).Decode<TPixel>(default, default); default(TgaDecoderCore).Decode<TPixel>(default, default, default);
default(TiffDecoderCore).Decode<TPixel>(default, default); default(TiffDecoderCore).Decode<TPixel>(default, default, default);
default(WebpDecoderCore).Decode<TPixel>(default, default, default);
} }
/// <summary> /// <summary>
@ -266,6 +272,17 @@ internal static class AotCompilerTools
AotCompileImageDecoder<TPixel, TiffDecoder>(); AotCompileImageDecoder<TPixel, TiffDecoder>();
} }
[Preserve]
private static void AotCompileSpectralConverter<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
default(SpectralConverter<TPixel>).GetPixelBuffer(default);
default(GrayJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(RgbJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(TiffJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(TiffOldJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
}
/// <summary> /// <summary>
/// This method pre-seeds the <see cref="IImageEncoder"/> in the AoT compiler. /// This method pre-seeds the <see cref="IImageEncoder"/> in the AoT compiler.
/// </summary> /// </summary>

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <remarks> /// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/> /// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks> /// </remarks>
internal sealed class BmpDecoderCore : IImageDecoderInternals internal sealed class BmpDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps. /// The default mask for the red part of the color for 16 bit rgb bitmaps.
@ -114,8 +114,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public BmpDecoderCore(BmpDecoderOptions options) public BmpDecoderCore(BmpDecoderOptions options)
: base(options.GeneralOptions)
{ {
this.Options = options.GeneralOptions;
this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.rleSkippedPixelHandling = options.RleSkippedPixelHandling;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
@ -125,14 +125,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc />
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
Image<TPixel>? image = null; Image<TPixel>? image = null;
try try
@ -224,7 +217,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.ReadImageHeaders(stream, out _, out _); this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new(this.infoHeader.Width, this.infoHeader.Height), this.metadata); return new ImageInfo(new(this.infoHeader.Width, this.infoHeader.Height), this.metadata);
@ -1463,6 +1456,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
this.bmpMetadata = this.metadata.GetBmpMetadata(); this.bmpMetadata = this.metadata.GetBmpMetadata();
this.bmpMetadata.InfoHeaderType = infoHeaderType; this.bmpMetadata.InfoHeaderType = infoHeaderType;
this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
this.Dimensions = new(this.infoHeader.Width, this.infoHeader.Height);
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap. /// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary> /// </summary>
internal sealed class BmpEncoderCore : IImageEncoderInternals internal sealed class BmpEncoderCore
{ {
/// <summary> /// <summary>
/// The amount to pad each row by. /// The amount to pad each row by.

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
internal sealed class GifDecoderCore : IImageDecoderInternals internal sealed class GifDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// The temp buffer used to reduce allocations.
@ -94,8 +94,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public GifDecoderCore(DecoderOptions options) public GifDecoderCore(DecoderOptions options)
: base(options)
{ {
this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames; this.maxFrames = options.MaxFrames;
@ -103,14 +103,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc />
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
uint frameCount = 0; uint frameCount = 0;
Image<TPixel>? image = null; Image<TPixel>? image = null;
@ -181,7 +174,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
uint frameCount = 0; uint frameCount = 0;
ImageFrameMetadata? previousFrame = null; ImageFrameMetadata? previousFrame = null;
@ -286,6 +279,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{ {
GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0"); GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0");
} }
this.Dimensions = new(this.imageDescriptor.Width, this.imageDescriptor.Height);
} }
/// <summary> /// <summary>

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary> /// <summary>
/// Implements the GIF encoding protocol. /// Implements the GIF encoding protocol.
/// </summary> /// </summary>
internal sealed class GifEncoderCore : IImageEncoderInternals internal sealed class GifEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.

50
src/ImageSharp/Formats/IImageDecoderInternals.cs

@ -1,50 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Abstraction for shared internals for XXXDecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary>
internal interface IImageDecoderInternals
{
/// <summary>
/// Gets the general decoder options.
/// </summary>
DecoderOptions Options { get; }
/// <summary>
/// Gets the dimensions of the image being decoded.
/// </summary>
Size Dimensions { get; }
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
/// <returns>The decoded image.</returns>
/// <remarks>
/// Cancellable synchronous method. In case of cancellation,
/// an <see cref="OperationCanceledException"/> shall be thrown which will be handled on the call site.
/// </remarks>
Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <remarks>
/// Cancellable synchronous method. In case of cancellation,
/// an <see cref="OperationCanceledException"/> shall be thrown which will be handled on the call site.
/// </remarks>
ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken);
}

22
src/ImageSharp/Formats/IImageEncoderInternals.cs

@ -1,22 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Abstraction for shared internals for ***DecoderCore implementations.
/// </summary>
internal interface IImageEncoderInternals
{
/// <summary>
/// Encodes the image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="stream">The stream.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
}

4
src/ImageSharp/Formats/ImageDecoder.cs

@ -190,7 +190,7 @@ public abstract class ImageDecoder : IImageDecoder
throw new NotSupportedException("Cannot read from the stream."); throw new NotSupportedException("Cannot read from the stream.");
} }
T PeformActionAndResetPosition(Stream s, long position) T PerformActionAndResetPosition(Stream s, long position)
{ {
T result = action(s); T result = action(s);
@ -207,7 +207,7 @@ public abstract class ImageDecoder : IImageDecoder
if (stream.CanSeek) if (stream.CanSeek)
{ {
return PeformActionAndResetPosition(stream, stream.Position); return PerformActionAndResetPosition(stream, stream.Position);
} }
Configuration configuration = options.Configuration; Configuration configuration = options.Configuration;

127
src/ImageSharp/Formats/ImageDecoderCore.cs

@ -0,0 +1,127 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// The base class for all stateful image decoders.
/// </summary>
internal abstract class ImageDecoderCore
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageDecoderCore"/> class.
/// </summary>
/// <param name="options">The general decoder options.</param>
protected ImageDecoderCore(DecoderOptions options)
=> this.Options = options;
/// <summary>
/// Gets the general decoder options.
/// </summary>
public DecoderOptions Options { get; }
/// <summary>
/// Gets or sets the dimensions of the image being decoded.
/// </summary>
public Size Dimensions { get; protected internal set; }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="configuration">The shared configuration.</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="ImageInfo" />.</returns>
/// <exception cref="InvalidImageContentException">Thrown if the encoded image contains errors.</exception>
public ImageInfo Identify(
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
{
using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken);
try
{
return this.Identify(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
throw new InvalidImageContentException(this.Dimensions, ex);
}
catch (Exception)
{
throw;
}
}
/// <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="configuration">The shared configuration.</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="InvalidImageContentException">Thrown if the encoded image contains errors.</exception>
public Image<TPixel> Decode<TPixel>(
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
// Test may pass a BufferedReadStream in order to monitor EOF hits, if so, use the existing instance.
BufferedReadStream bufferedReadStream =
stream as BufferedReadStream ?? new BufferedReadStream(configuration, stream, cancellationToken);
try
{
return this.Decode<TPixel>(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
throw new InvalidImageContentException(this.Dimensions, ex);
}
catch (Exception)
{
throw;
}
finally
{
if (bufferedReadStream != stream)
{
bufferedReadStream.Dispose();
}
}
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The <see cref="ImageInfo"/>.</returns>
/// <remarks>
/// Cancellable synchronous method. In case of cancellation,
/// an <see cref="OperationCanceledException"/> shall be thrown which will be handled on the call site.
/// </remarks>
protected abstract ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken);
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
/// <returns>The decoded image.</returns>
/// <remarks>
/// Cancellable synchronous method. In case of cancellation, an <see cref="OperationCanceledException"/> shall
/// be thrown which will be handled on the call site.
/// </remarks>
protected abstract Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>;
}

81
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -1,81 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Utility methods for <see cref="IImageDecoderInternals"/>.
/// </summary>
internal static class ImageDecoderUtilities
{
internal static ImageInfo Identify(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
{
using BufferedReadStream bufferedReadStream = new(configuration, stream, cancellationToken);
try
{
return decoder.Identify(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
throw new InvalidImageContentException(decoder.Dimensions, ex);
}
catch (Exception)
{
throw;
}
}
internal static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
=> decoder.Decode<TPixel>(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
internal static Image<TPixel> Decode<TPixel>(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
Func<InvalidMemoryOperationException, Size, InvalidImageContentException> largeImageExceptionFactory,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
// Test may pass a BufferedReadStream in order to monitor EOF hits, if so, use the existing instance.
BufferedReadStream bufferedReadStream = stream as BufferedReadStream ?? new BufferedReadStream(configuration, stream, cancellationToken);
try
{
return decoder.Decode<TPixel>(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
throw largeImageExceptionFactory(ex, decoder.Dimensions);
}
catch (Exception)
{
throw;
}
finally
{
if (bufferedReadStream != stream)
{
bufferedReadStream.Dispose();
}
}
}
private static InvalidImageContentException DefaultLargeImageExceptionFactory(
InvalidMemoryOperationException memoryOperationException,
Size dimensions) =>
new(dimensions, memoryOperationException);
}

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

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
/// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/> /// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/>
/// with additional fixes for both performance and common encoding errors. /// with additional fixes for both performance and common encoding errors.
/// </summary> /// </summary>
internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData
{ {
/// <summary> /// <summary>
/// Whether the image has an EXIF marker. /// Whether the image has an EXIF marker.
@ -117,8 +117,8 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public JpegDecoderCore(JpegDecoderOptions options) public JpegDecoderCore(JpegDecoderOptions options)
: base(options.GeneralOptions)
{ {
this.Options = options.GeneralOptions;
this.resizeMode = options.ResizeMode; this.resizeMode = options.ResizeMode;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
@ -130,12 +130,6 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
// Refers to assembly's static data segment, no allocation occurs. // Refers to assembly's static data segment, no allocation occurs.
private static ReadOnlySpan<byte> SupportedPrecisions => new byte[] { 8, 12 }; private static ReadOnlySpan<byte> SupportedPrecisions => new byte[] { 8, 12 };
/// <inheritdoc />
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => this.Frame.PixelSize;
/// <summary> /// <summary>
/// Gets the frame /// Gets the frame
/// </summary> /// </summary>
@ -198,8 +192,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
using SpectralConverter<TPixel> spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); using SpectralConverter<TPixel> spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize);
this.ParseStream(stream, spectralConverter, cancellationToken); this.ParseStream(stream, spectralConverter, cancellationToken);
@ -216,7 +209,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.ParseStream(stream, spectralConverter: null, cancellationToken); this.ParseStream(stream, spectralConverter: null, cancellationToken);
this.InitExifProfile(); this.InitExifProfile();
@ -1237,6 +1230,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
} }
this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount);
this.Dimensions = new(frameWidth, frameHeight);
this.Metadata.GetJpegMetadata().Progressive = this.Frame.Progressive; this.Metadata.GetJpegMetadata().Progressive = this.Frame.Progressive;
remaining -= length; remaining -= length;

2
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a jpeg. /// Image encoder for writing an image to a stream as a jpeg.
/// </summary> /// </summary>
internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals internal sealed unsafe partial class JpegEncoderCore
{ {
/// <summary> /// <summary>
/// The available encodable frame configs. /// The available encodable frame configs.

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

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm;
/// <summary> /// <summary>
/// Performs the PBM decoding operation. /// Performs the PBM decoding operation.
/// </summary> /// </summary>
internal sealed class PbmDecoderCore : IImageDecoderInternals internal sealed class PbmDecoderCore : ImageDecoderCore
{ {
private int maxPixelValue; private int maxPixelValue;
@ -52,20 +52,13 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PbmDecoderCore(DecoderOptions options) public PbmDecoderCore(DecoderOptions options)
: base(options)
{ {
this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
} }
/// <inheritdoc/> /// <inheritdoc/>
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc/>
public Size Dimensions => this.pixelSize;
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
this.ProcessHeader(stream); this.ProcessHeader(stream);
@ -83,7 +76,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.ProcessHeader(stream); this.ProcessHeader(stream);
return new ImageInfo( return new ImageInfo(
@ -179,6 +172,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
} }
this.pixelSize = new Size(width, height); this.pixelSize = new Size(width, height);
this.Dimensions = this.pixelSize;
this.metadata = new ImageMetadata(); this.metadata = new ImageMetadata();
PbmMetadata meta = this.metadata.GetPbmMetadata(); PbmMetadata meta = this.metadata.GetPbmMetadata();
meta.Encoding = this.encoding; meta.Encoding = this.encoding;

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

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a PGM, PBM, PPM or PAM bitmap. /// Image encoder for writing an image to a stream as a PGM, PBM, PPM or PAM bitmap.
/// </summary> /// </summary>
internal sealed class PbmEncoderCore : IImageEncoderInternals internal sealed class PbmEncoderCore
{ {
private const byte NewLine = (byte)'\n'; private const byte NewLine = (byte)'\n';
private const byte Space = (byte)' '; private const byte Space = (byte)' ';

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

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Performs the png decoding operation. /// Performs the png decoding operation.
/// </summary> /// </summary>
internal sealed class PngDecoderCore : IImageDecoderInternals internal sealed class PngDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// The general decoder options. /// The general decoder options.
@ -136,8 +136,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PngDecoderCore(PngDecoderOptions options) public PngDecoderCore(PngDecoderOptions options)
: base(options.GeneralOptions)
{ {
this.Options = options.GeneralOptions;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
@ -147,8 +147,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
} }
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
: base(options.GeneralOptions)
{ {
this.Options = options.GeneralOptions;
this.colorMetadataOnly = colorMetadataOnly; this.colorMetadataOnly = colorMetadataOnly;
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = true; this.skipMetadata = true;
@ -159,14 +159,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc/>
public Size Dimensions => new(this.header.Width, this.header.Height);
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
uint frameCount = 0; uint frameCount = 0;
ImageMetadata metadata = new(); ImageMetadata metadata = new();
@ -341,7 +334,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
uint frameCount = 0; uint frameCount = 0;
ImageMetadata metadata = new(); ImageMetadata metadata = new();
@ -1339,6 +1332,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
pngMetadata.InterlaceMethod = this.header.InterlaceMethod; pngMetadata.InterlaceMethod = this.header.InterlaceMethod;
this.pngColorType = this.header.ColorType; this.pngColorType = this.header.ColorType;
this.Dimensions = new(this.header.Width, this.header.Height);
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Performs the png encoding operation. /// Performs the png encoding operation.
/// </summary> /// </summary>
internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable internal sealed class PngEncoderCore : IDisposable
{ {
/// <summary> /// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks. /// The maximum block size, defaults at 64k for uncompressed blocks.

13
src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs

@ -13,7 +13,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Qoi; namespace SixLabors.ImageSharp.Formats.Qoi;
internal class QoiDecoderCore : IImageDecoderInternals internal class QoiDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// The global configuration. /// The global configuration.
@ -31,19 +31,14 @@ internal class QoiDecoderCore : IImageDecoderInternals
private QoiHeader header; private QoiHeader header;
public QoiDecoderCore(DecoderOptions options) public QoiDecoderCore(DecoderOptions options)
: base(options)
{ {
this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
public DecoderOptions Options { get; }
public Size Dimensions { get; }
/// <inheritdoc /> /// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
// Process the header to get metadata // Process the header to get metadata
this.ProcessHeader(stream); this.ProcessHeader(stream);
@ -62,7 +57,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.ProcessHeader(stream); this.ProcessHeader(stream);
PixelTypeInfo pixelType = new(8 * (int)this.header.Channels); PixelTypeInfo pixelType = new(8 * (int)this.header.Channels);

10
src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Qoi;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a QOi image /// Image encoder for writing an image to a stream as a QOi image
/// </summary> /// </summary>
internal class QoiEncoderCore : IImageEncoderInternals internal class QoiEncoderCore
{ {
/// <summary> /// <summary>
/// The encoder with options /// The encoder with options
@ -41,7 +41,13 @@ internal class QoiEncoderCore : IImageEncoderInternals
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = configuration.MemoryAllocator;
} }
/// <inheritdoc /> /// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{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 request cancellation.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

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

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga;
/// <summary> /// <summary>
/// Performs the tga decoding operation. /// Performs the tga decoding operation.
/// </summary> /// </summary>
internal sealed class TgaDecoderCore : IImageDecoderInternals internal sealed class TgaDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// General configuration options. /// General configuration options.
@ -52,21 +52,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public TgaDecoderCore(DecoderOptions options) public TgaDecoderCore(DecoderOptions options)
: base(options)
{ {
this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc /> /// <inheritdoc />
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc />
public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height);
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
try try
{ {
@ -642,7 +635,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc /> /// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.ReadFileHeader(stream); this.ReadFileHeader(stream);
return new ImageInfo( return new ImageInfo(
@ -914,6 +907,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
stream.Read(buffer, 0, TgaFileHeader.Size); stream.Read(buffer, 0, TgaFileHeader.Size);
this.fileHeader = TgaFileHeader.Parse(buffer); this.fileHeader = TgaFileHeader.Parse(buffer);
this.Dimensions = new Size(this.fileHeader.Width, this.fileHeader.Height);
this.metadata = new ImageMetadata(); this.metadata = new ImageMetadata();
this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata();
this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth;

46
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tga;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a truevision targa image. /// Image encoder for writing an image to a stream as a truevision targa image.
/// </summary> /// </summary>
internal sealed class TgaEncoderCore : IImageEncoderInternals internal sealed class TgaEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.
@ -105,11 +105,11 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
stream.Write(buffer, 0, TgaFileHeader.Size); stream.Write(buffer, 0, TgaFileHeader.Size);
if (this.compression is TgaCompression.RunLength) if (this.compression is TgaCompression.RunLength)
{ {
this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame); this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame, cancellationToken);
} }
else else
{ {
this.WriteImage(image.Configuration, stream, image.Frames.RootFrame); this.WriteImage(image.Configuration, stream, image.Frames.RootFrame, cancellationToken);
} }
stream.Flush(); stream.Flush();
@ -121,29 +121,28 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param> /// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> /// <param name="image"> /// The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data. /// <param name="cancellationToken">The token to request cancellation.</param>
/// </param> private void WriteImage<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image, CancellationToken cancellationToken)
private void WriteImage<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Buffer2D<TPixel> pixels = image.PixelBuffer; Buffer2D<TPixel> pixels = image.PixelBuffer;
switch (this.bitsPerPixel) switch (this.bitsPerPixel)
{ {
case TgaBitsPerPixel.Bit8: case TgaBitsPerPixel.Bit8:
this.Write8Bit(configuration, stream, pixels); this.Write8Bit(configuration, stream, pixels, cancellationToken);
break; break;
case TgaBitsPerPixel.Bit16: case TgaBitsPerPixel.Bit16:
this.Write16Bit(configuration, stream, pixels); this.Write16Bit(configuration, stream, pixels, cancellationToken);
break; break;
case TgaBitsPerPixel.Bit24: case TgaBitsPerPixel.Bit24:
this.Write24Bit(configuration, stream, pixels); this.Write24Bit(configuration, stream, pixels, cancellationToken);
break; break;
case TgaBitsPerPixel.Bit32: case TgaBitsPerPixel.Bit32:
this.Write32Bit(configuration, stream, pixels); this.Write32Bit(configuration, stream, pixels, cancellationToken);
break; break;
} }
} }
@ -154,7 +153,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The stream to write the image to.</param> /// <param name="stream">The stream to write the image to.</param>
/// <param name="image">The image to encode.</param> /// <param name="image">The image to encode.</param>
private void WriteRunLengthEncodedImage<TPixel>(Stream stream, ImageFrame<TPixel> image) /// <param name="cancellationToken">The token to request cancellation.</param>
private void WriteRunLengthEncodedImage<TPixel>(Stream stream, ImageFrame<TPixel> image, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Buffer2D<TPixel> pixels = image.PixelBuffer; Buffer2D<TPixel> pixels = image.PixelBuffer;
@ -164,6 +164,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
cancellationToken.ThrowIfCancellationRequested();
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgba32(image.Configuration, pixelRow, rgbaRow); PixelOperations<TPixel>.Instance.ToRgba32(image.Configuration, pixelRow, rgbaRow);
@ -313,7 +315,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <param name="configuration">The global configuration.</param> /// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write8Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels) /// <param name="cancellationToken">The token to request cancellation.</param>
private void Write8Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 1); using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 1);
@ -321,6 +324,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
cancellationToken.ThrowIfCancellationRequested();
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8Bytes( PixelOperations<TPixel>.Instance.ToL8Bytes(
configuration, configuration,
@ -338,7 +343,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <param name="configuration">The global configuration.</param> /// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write16Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels) /// <param name="cancellationToken">The token to request cancellation.</param>
private void Write16Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 2); using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 2);
@ -346,6 +352,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
cancellationToken.ThrowIfCancellationRequested();
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra5551Bytes( PixelOperations<TPixel>.Instance.ToBgra5551Bytes(
configuration, configuration,
@ -363,7 +371,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <param name="configuration">The global configuration.</param> /// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels) /// <param name="cancellationToken">The token to request cancellation.</param>
private void Write24Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 3); using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 3);
@ -371,6 +380,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
cancellationToken.ThrowIfCancellationRequested();
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes( PixelOperations<TPixel>.Instance.ToBgr24Bytes(
configuration, configuration,
@ -388,7 +399,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <param name="configuration">The global configuration.</param> /// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels) /// <param name="cancellationToken">The token to request cancellation.</param>
private void Write32Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4); using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4);
@ -396,6 +408,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
cancellationToken.ThrowIfCancellationRequested();
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes( PixelOperations<TPixel>.Instance.ToBgra32Bytes(
configuration, configuration,

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

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

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

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff;
/// <summary> /// <summary>
/// Performs the tiff decoding operation. /// Performs the tiff decoding operation.
/// </summary> /// </summary>
internal class TiffDecoderCore : IImageDecoderInternals internal class TiffDecoderCore : ImageDecoderCore
{ {
/// <summary> /// <summary>
/// General configuration options. /// General configuration options.
@ -60,8 +60,8 @@ internal class TiffDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public TiffDecoderCore(DecoderOptions options) public TiffDecoderCore(DecoderOptions options)
: base(options)
{ {
this.Options = options;
this.configuration = options.Configuration; this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata; this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames; this.maxFrames = options.MaxFrames;
@ -154,14 +154,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
public TiffPredictor Predictor { get; set; } public TiffPredictor Predictor { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public DecoderOptions Options { get; } protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
/// <inheritdoc/>
public Size Dimensions { get; private set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
List<ImageFrame<TPixel>> frames = []; List<ImageFrame<TPixel>> frames = [];
List<ImageFrameMetadata> framesMetadata = []; List<ImageFrameMetadata> framesMetadata = [];
@ -215,7 +208,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
} }
/// <inheritdoc/> /// <inheritdoc/>
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
this.inputStream = stream; this.inputStream = stream;
DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); DirectoryReader reader = new(stream, this.configuration.MemoryAllocator);

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

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff;
/// <summary> /// <summary>
/// Performs the TIFF encoding operation. /// Performs the TIFF encoding operation.
/// </summary> /// </summary>
internal sealed class TiffEncoderCore : IImageEncoderInternals internal sealed class TiffEncoderCore
{ {
private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian
? TiffConstants.ByteOrderLittleEndianShort ? TiffConstants.ByteOrderLittleEndianShort

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary> /// <summary>
/// Performs the webp decoding operation. /// Performs the webp decoding operation.
/// </summary> /// </summary>
internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
{ {
/// <summary> /// <summary>
/// General configuration options. /// General configuration options.
@ -61,8 +61,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public WebpDecoderCore(WebpDecoderOptions options) public WebpDecoderCore(WebpDecoderOptions options)
: base(options.GeneralOptions)
{ {
this.Options = options.GeneralOptions;
this.backgroundColorHandling = options.BackgroundColorHandling; this.backgroundColorHandling = options.BackgroundColorHandling;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
@ -70,15 +70,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
} }
/// <inheritdoc/>
public DecoderOptions Options { get; }
/// <inheritdoc/>
public Size Dimensions => new((int)this.webImageInfo!.Width, (int)this.webImageInfo.Height);
/// <inheritdoc /> /// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{ {
Image<TPixel>? image = null; Image<TPixel>? image = null;
try try
@ -136,7 +129,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
} }
/// <inheritdoc /> /// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
ReadImageHeader(stream, stackalloc byte[4]); ReadImageHeader(stream, stackalloc byte[4]);

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

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream in the Webp format. /// Image encoder for writing an image to a stream in the Webp format.
/// </summary> /// </summary>
internal sealed class WebpEncoderCore : IImageEncoderInternals internal sealed class WebpEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.

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

@ -67,11 +67,11 @@ public partial class JpegDecoderTests
public void ParseStream_BasicPropertiesAreCorrect() public void ParseStream_BasicPropertiesAreCorrect()
{ {
JpegDecoderOptions options = new(); JpegDecoderOptions options = new();
Configuration configuration = options.GeneralOptions.Configuration;
byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes;
using MemoryStream ms = new(bytes); using MemoryStream ms = new(bytes);
using BufferedReadStream bufferedStream = new(Configuration.Default, ms);
using JpegDecoderCore decoder = new(options); using JpegDecoderCore decoder = new(options);
using Image<Rgba32> image = decoder.Decode<Rgba32>(bufferedStream, cancellationToken: default); using Image<Rgba32> image = decoder.Decode<Rgba32>(configuration, ms, cancellationToken: default);
// I don't know why these numbers are different. All I know is that the decoder works // I don't know why these numbers are different. All I know is that the decoder works
// and spectral data is exactly correct also. // and spectral data is exactly correct also.

14
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -5,7 +5,6 @@ using System.Diagnostics;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -216,18 +215,17 @@ public class JpegFixture : MeasureFixture
internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false) internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false)
{ {
byte[] bytes = TestFile.Create(testFileName).Bytes; byte[] bytes = TestFile.Create(testFileName).Bytes;
using var ms = new MemoryStream(bytes); using MemoryStream ms = new(bytes);
using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); JpegDecoderOptions decoderOptions = new();
Configuration configuration = decoderOptions.GeneralOptions.Configuration;
JpegDecoderOptions options = new(); JpegDecoderCore decoder = new(decoderOptions);
var decoder = new JpegDecoderCore(options);
if (metaDataOnly) if (metaDataOnly)
{ {
decoder.Identify(bufferedStream, cancellationToken: default); decoder.Identify(configuration, ms, default);
} }
else else
{ {
using Image<Rgba32> image = decoder.Decode<Rgba32>(bufferedStream, cancellationToken: default); using Image<Rgba32> image = decoder.Decode<Rgba32>(configuration, ms, default);
} }
return decoder; return decoder;

Loading…
Cancel
Save