diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index f36f3d09b4..0d5faabe18 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -10,10 +10,13 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Qoi; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -129,6 +132,7 @@ internal static class AotCompilerTools AotCompileImageDecoderInternals(); AotCompileImageEncoders(); AotCompileImageDecoders(); + AotCompileSpectralConverter(); AotCompileImageProcessors(); AotCompileGenericImageProcessors(); AotCompileResamplers(); @@ -195,39 +199,41 @@ internal static class AotCompilerTools => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext(default, default, default); /// - /// This method pre-seeds the all in the AoT compiler. + /// This method pre-seeds the all core encoders in the AoT compiler. /// /// The pixel format. [Preserve] private static void AotCompileImageEncoderInternals() where TPixel : unmanaged, IPixel { - default(WebpEncoderCore).Encode(default, default, default); default(BmpEncoderCore).Encode(default, default, default); default(GifEncoderCore).Encode(default, default, default); default(JpegEncoderCore).Encode(default, default, default); default(PbmEncoderCore).Encode(default, default, default); default(PngEncoderCore).Encode(default, default, default); + default(QoiEncoderCore).Encode(default, default, default); default(TgaEncoderCore).Encode(default, default, default); default(TiffEncoderCore).Encode(default, default, default); + default(WebpEncoderCore).Encode(default, default, default); } /// - /// This method pre-seeds the all in the AoT compiler. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. [Preserve] private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { - default(WebpDecoderCore).Decode(default, default); - default(BmpDecoderCore).Decode(default, default); - default(GifDecoderCore).Decode(default, default); - default(JpegDecoderCore).Decode(default, default); - default(PbmDecoderCore).Decode(default, default); - default(PngDecoderCore).Decode(default, default); - default(TgaDecoderCore).Decode(default, default); - default(TiffDecoderCore).Decode(default, default); + default(BmpDecoderCore).Decode(default, default, default); + default(GifDecoderCore).Decode(default, default, default); + default(JpegDecoderCore).Decode(default, default, default); + default(PbmDecoderCore).Decode(default, default, default); + default(PngDecoderCore).Decode(default, default, default); + default(QoiDecoderCore).Decode(default, default, default); + default(TgaDecoderCore).Decode(default, default, default); + default(TiffDecoderCore).Decode(default, default, default); + default(WebpDecoderCore).Decode(default, default, default); } /// @@ -266,6 +272,17 @@ internal static class AotCompilerTools AotCompileImageDecoder(); } + [Preserve] + private static void AotCompileSpectralConverter() + where TPixel : unmanaged, IPixel + { + default(SpectralConverter).GetPixelBuffer(default); + default(GrayJpegSpectralConverter).GetPixelBuffer(default); + default(RgbJpegSpectralConverter).GetPixelBuffer(default); + default(TiffJpegSpectralConverter).GetPixelBuffer(default); + default(TiffOldJpegSpectralConverter).GetPixelBuffer(default); + } + /// /// This method pre-seeds the in the AoT compiler. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 101919bc36..94257517d2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// A useful decoding source example can be found at /// -internal sealed class BmpDecoderCore : IImageDecoderInternals +internal sealed class BmpDecoderCore : ImageDecoderCore { /// /// The default mask for the red part of the color for 16 bit rgb bitmaps. @@ -114,8 +114,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// The options. public BmpDecoderCore(BmpDecoderOptions options) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; @@ -125,14 +125,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height); - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { Image? image = null; try @@ -224,7 +217,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ReadImageHeaders(stream, out _, out _); 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.InfoHeaderType = infoHeaderType; this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; + + this.Dimensions = new(this.infoHeader.Width, this.infoHeader.Height); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 16ff4ed833..85786949d8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// -internal sealed class BmpEncoderCore : IImageEncoderInternals +internal sealed class BmpEncoderCore { /// /// The amount to pad each row by. diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 608280a8f9..a99b5862dd 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Performs the gif decoding operation. /// -internal sealed class GifDecoderCore : IImageDecoderInternals +internal sealed class GifDecoderCore : ImageDecoderCore { /// /// The temp buffer used to reduce allocations. @@ -94,8 +94,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// The decoder options. public GifDecoderCore(DecoderOptions options) + : base(options) { - this.Options = options; this.configuration = options.Configuration; this.skipMetadata = options.SkipMetadata; this.maxFrames = options.MaxFrames; @@ -103,14 +103,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height); - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { uint frameCount = 0; Image? image = null; @@ -181,7 +174,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { uint frameCount = 0; ImageFrameMetadata? previousFrame = null; @@ -286,6 +279,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0"); } + + this.Dimensions = new(this.imageDescriptor.Width, this.imageDescriptor.Height); } /// diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 90177ccc1b..11185d90b0 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif; /// /// Implements the GIF encoding protocol. /// -internal sealed class GifEncoderCore : IImageEncoderInternals +internal sealed class GifEncoderCore { /// /// Used for allocating memory during processing operations. diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs deleted file mode 100644 index 06fb597640..0000000000 --- a/src/ImageSharp/Formats/IImageDecoderInternals.cs +++ /dev/null @@ -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; - -/// -/// Abstraction for shared internals for XXXDecoderCore implementations to be used with . -/// -internal interface IImageDecoderInternals -{ - /// - /// Gets the general decoder options. - /// - DecoderOptions Options { get; } - - /// - /// Gets the dimensions of the image being decoded. - /// - Size Dimensions { get; } - - /// - /// Decodes the image from the specified stream. - /// - /// The pixel format. - /// The stream, where the image should be decoded from. Cannot be null. - /// The token to monitor for cancellation requests. - /// is null. - /// The decoded image. - /// - /// Cancellable synchronous method. In case of cancellation, - /// an shall be thrown which will be handled on the call site. - /// - Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - /// The token to monitor for cancellation requests. - /// The . - /// - /// Cancellable synchronous method. In case of cancellation, - /// an shall be thrown which will be handled on the call site. - /// - ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken); -} diff --git a/src/ImageSharp/Formats/IImageEncoderInternals.cs b/src/ImageSharp/Formats/IImageEncoderInternals.cs deleted file mode 100644 index 131949c968..0000000000 --- a/src/ImageSharp/Formats/IImageEncoderInternals.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats; - -/// -/// Abstraction for shared internals for ***DecoderCore implementations. -/// -internal interface IImageEncoderInternals -{ - /// - /// Encodes the image. - /// - /// The image. - /// The stream. - /// The token to monitor for cancellation requests. - /// The pixel type. - void Encode(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel; -} diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index 42e265dae8..e58cd6a6de 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -190,7 +190,7 @@ public abstract class ImageDecoder : IImageDecoder throw new NotSupportedException("Cannot read from the stream."); } - T PeformActionAndResetPosition(Stream s, long position) + T PerformActionAndResetPosition(Stream s, long position) { T result = action(s); @@ -207,7 +207,7 @@ public abstract class ImageDecoder : IImageDecoder if (stream.CanSeek) { - return PeformActionAndResetPosition(stream, stream.Position); + return PerformActionAndResetPosition(stream, stream.Position); } Configuration configuration = options.Configuration; diff --git a/src/ImageSharp/Formats/ImageDecoderCore.cs b/src/ImageSharp/Formats/ImageDecoderCore.cs new file mode 100644 index 0000000000..adf0107da0 --- /dev/null +++ b/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; + +/// +/// The base class for all stateful image decoders. +/// +internal abstract class ImageDecoderCore +{ + /// + /// Initializes a new instance of the class. + /// + /// The general decoder options. + protected ImageDecoderCore(DecoderOptions options) + => this.Options = options; + + /// + /// Gets the general decoder options. + /// + public DecoderOptions Options { get; } + + /// + /// Gets or sets the dimensions of the image being decoded. + /// + public Size Dimensions { get; protected internal set; } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The shared configuration. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + 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; + } + } + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The shared configuration. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public Image Decode( + Configuration configuration, + Stream stream, + CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + // 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(bufferedReadStream, cancellationToken); + } + catch (InvalidMemoryOperationException ex) + { + throw new InvalidImageContentException(this.Dimensions, ex); + } + catch (Exception) + { + throw; + } + finally + { + if (bufferedReadStream != stream) + { + bufferedReadStream.Dispose(); + } + } + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// + /// Cancellable synchronous method. In case of cancellation, + /// an shall be thrown which will be handled on the call site. + /// + protected abstract ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken); + + /// + /// Decodes the image from the specified stream. + /// + /// The pixel format. + /// The stream, where the image should be decoded from. Cannot be null. + /// The token to monitor for cancellation requests. + /// is null. + /// The decoded image. + /// + /// Cancellable synchronous method. In case of cancellation, an shall + /// be thrown which will be handled on the call site. + /// + protected abstract Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; +} diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs deleted file mode 100644 index a1abd7dc30..0000000000 --- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs +++ /dev/null @@ -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; - -/// -/// Utility methods for . -/// -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 Decode( - this IImageDecoderInternals decoder, - Configuration configuration, - Stream stream, - CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); - - internal static Image Decode( - this IImageDecoderInternals decoder, - Configuration configuration, - Stream stream, - Func largeImageExceptionFactory, - CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel - { - // 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(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); -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 759cc700f5..2320fe1791 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// -internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals +internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData { /// /// Whether the image has an EXIF marker. @@ -117,8 +117,8 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// /// The decoder options. public JpegDecoderCore(JpegDecoderOptions options) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.resizeMode = options.ResizeMode; this.configuration = options.GeneralOptions.Configuration; 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. private static ReadOnlySpan SupportedPrecisions => new byte[] { 8, 12 }; - /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => this.Frame.PixelSize; - /// /// Gets the frame /// @@ -198,8 +192,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { using SpectralConverter spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); this.ParseStream(stream, spectralConverter, cancellationToken); @@ -216,7 +209,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ParseStream(stream, spectralConverter: null, cancellationToken); this.InitExifProfile(); @@ -1237,6 +1230,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); + this.Dimensions = new(frameWidth, frameHeight); this.Metadata.GetJpegMetadata().Progressive = this.Frame.Progressive; remaining -= length; diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index fbc9286068..a6ff623660 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Image encoder for writing an image to a stream as a jpeg. /// -internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals +internal sealed unsafe partial class JpegEncoderCore { /// /// The available encodable frame configs. diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 590fb2fb07..9d3dd3ea4c 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm; /// /// Performs the PBM decoding operation. /// -internal sealed class PbmDecoderCore : IImageDecoderInternals +internal sealed class PbmDecoderCore : ImageDecoderCore { private int maxPixelValue; @@ -52,20 +52,13 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals /// /// The decoder options. public PbmDecoderCore(DecoderOptions options) + : base(options) { - this.Options = options; this.configuration = options.Configuration; } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => this.pixelSize; - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { this.ProcessHeader(stream); @@ -83,7 +76,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ProcessHeader(stream); return new ImageInfo( @@ -179,6 +172,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals } this.pixelSize = new Size(width, height); + this.Dimensions = this.pixelSize; this.metadata = new ImageMetadata(); PbmMetadata meta = this.metadata.GetPbmMetadata(); meta.Encoding = this.encoding; diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs index 5ae37d4e69..843f1880e6 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Pbm; /// /// Image encoder for writing an image to a stream as a PGM, PBM, PPM or PAM bitmap. /// -internal sealed class PbmEncoderCore : IImageEncoderInternals +internal sealed class PbmEncoderCore { private const byte NewLine = (byte)'\n'; private const byte Space = (byte)' '; diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 01f20d40f9..01f038141a 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Png; /// /// Performs the png decoding operation. /// -internal sealed class PngDecoderCore : IImageDecoderInternals +internal sealed class PngDecoderCore : ImageDecoderCore { /// /// The general decoder options. @@ -136,8 +136,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// The decoder options. public PngDecoderCore(PngDecoderOptions options) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.configuration = options.GeneralOptions.Configuration; this.maxFrames = options.GeneralOptions.MaxFrames; this.skipMetadata = options.GeneralOptions.SkipMetadata; @@ -147,8 +147,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals } internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.colorMetadataOnly = colorMetadataOnly; this.maxFrames = options.GeneralOptions.MaxFrames; this.skipMetadata = true; @@ -159,14 +159,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => new(this.header.Width, this.header.Height); - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { uint frameCount = 0; ImageMetadata metadata = new(); @@ -341,7 +334,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { uint frameCount = 0; ImageMetadata metadata = new(); @@ -1339,6 +1332,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals pngMetadata.InterlaceMethod = this.header.InterlaceMethod; this.pngColorType = this.header.ColorType; + this.Dimensions = new(this.header.Width, this.header.Height); } /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 243fd1bccc..4bbb68358f 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Png; /// /// Performs the png encoding operation. /// -internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable +internal sealed class PngEncoderCore : IDisposable { /// /// The maximum block size, defaults at 64k for uncompressed blocks. diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index 5f408fe0b3..85fac7ea26 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -13,7 +13,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Qoi; -internal class QoiDecoderCore : IImageDecoderInternals +internal class QoiDecoderCore : ImageDecoderCore { /// /// The global configuration. @@ -31,19 +31,14 @@ internal class QoiDecoderCore : IImageDecoderInternals private QoiHeader header; public QoiDecoderCore(DecoderOptions options) + : base(options) { - this.Options = options; this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } - public DecoderOptions Options { get; } - - public Size Dimensions { get; } - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { // Process the header to get metadata this.ProcessHeader(stream); @@ -62,7 +57,7 @@ internal class QoiDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ProcessHeader(stream); PixelTypeInfo pixelType = new(8 * (int)this.header.Channels); diff --git a/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs index 53f67e765d..88d87a3825 100644 --- a/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Qoi; /// /// Image encoder for writing an image to a stream as a QOi image /// -internal class QoiEncoderCore : IImageEncoderInternals +internal class QoiEncoderCore { /// /// The encoder with options @@ -41,7 +41,13 @@ internal class QoiEncoderCore : IImageEncoderInternals this.memoryAllocator = configuration.MemoryAllocator; } - /// + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The token to request cancellation. public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 912b82e8e6..dc6b33422f 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga; /// /// Performs the tga decoding operation. /// -internal sealed class TgaDecoderCore : IImageDecoderInternals +internal sealed class TgaDecoderCore : ImageDecoderCore { /// /// General configuration options. @@ -52,21 +52,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// The options. public TgaDecoderCore(DecoderOptions options) + : base(options) { - this.Options = options; this.configuration = options.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { try { @@ -642,7 +635,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ReadFileHeader(stream); return new ImageInfo( @@ -914,6 +907,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals stream.Read(buffer, 0, TgaFileHeader.Size); this.fileHeader = TgaFileHeader.Parse(buffer); + this.Dimensions = new Size(this.fileHeader.Width, this.fileHeader.Height); + this.metadata = new ImageMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 8e13096568..1e05a9f716 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tga; /// /// Image encoder for writing an image to a stream as a truevision targa image. /// -internal sealed class TgaEncoderCore : IImageEncoderInternals +internal sealed class TgaEncoderCore { /// /// Used for allocating memory during processing operations. @@ -105,11 +105,11 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals stream.Write(buffer, 0, TgaFileHeader.Size); if (this.compression is TgaCompression.RunLength) { - this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame); + this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame, cancellationToken); } else { - this.WriteImage(image.Configuration, stream, image.Frames.RootFrame); + this.WriteImage(image.Configuration, stream, image.Frames.RootFrame, cancellationToken); } stream.Flush(); @@ -121,29 +121,28 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The pixel format. /// The global configuration. /// The to write to. - /// - /// The containing pixel data. - /// - private void WriteImage(Configuration configuration, Stream stream, ImageFrame image) + /// /// The containing pixel data. + /// The token to request cancellation. + private void WriteImage(Configuration configuration, Stream stream, ImageFrame image, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Buffer2D pixels = image.PixelBuffer; switch (this.bitsPerPixel) { case TgaBitsPerPixel.Bit8: - this.Write8Bit(configuration, stream, pixels); + this.Write8Bit(configuration, stream, pixels, cancellationToken); break; case TgaBitsPerPixel.Bit16: - this.Write16Bit(configuration, stream, pixels); + this.Write16Bit(configuration, stream, pixels, cancellationToken); break; case TgaBitsPerPixel.Bit24: - this.Write24Bit(configuration, stream, pixels); + this.Write24Bit(configuration, stream, pixels, cancellationToken); break; case TgaBitsPerPixel.Bit32: - this.Write32Bit(configuration, stream, pixels); + this.Write32Bit(configuration, stream, pixels, cancellationToken); break; } } @@ -154,7 +153,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The pixel type. /// The stream to write the image to. /// The image to encode. - private void WriteRunLengthEncodedImage(Stream stream, ImageFrame image) + /// The token to request cancellation. + private void WriteRunLengthEncodedImage(Stream stream, ImageFrame image, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Buffer2D pixels = image.PixelBuffer; @@ -164,6 +164,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int y = 0; y < image.Height; y++) { + cancellationToken.ThrowIfCancellationRequested(); + Span pixelRow = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToRgba32(image.Configuration, pixelRow, rgbaRow); @@ -313,7 +315,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write8Bit(Configuration configuration, Stream stream, Buffer2D pixels) + /// The token to request cancellation. + private void Write8Bit(Configuration configuration, Stream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 1); @@ -321,6 +324,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int y = pixels.Height - 1; y >= 0; y--) { + cancellationToken.ThrowIfCancellationRequested(); + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToL8Bytes( configuration, @@ -338,7 +343,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write16Bit(Configuration configuration, Stream stream, Buffer2D pixels) + /// The token to request cancellation. + private void Write16Bit(Configuration configuration, Stream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 2); @@ -346,6 +352,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int y = pixels.Height - 1; y >= 0; y--) { + cancellationToken.ThrowIfCancellationRequested(); + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra5551Bytes( configuration, @@ -363,7 +371,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write24Bit(Configuration configuration, Stream stream, Buffer2D pixels) + /// The token to request cancellation. + private void Write24Bit(Configuration configuration, Stream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 3); @@ -371,6 +380,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int y = pixels.Height - 1; y >= 0; y--) { + cancellationToken.ThrowIfCancellationRequested(); + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgr24Bytes( configuration, @@ -388,7 +399,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write32Bit(Configuration configuration, Stream stream, Buffer2D pixels) + /// The token to request cancellation. + private void Write32Bit(Configuration configuration, Stream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -396,6 +408,8 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals for (int y = pixels.Height - 1; y >= 0; y--) { + cancellationToken.ThrowIfCancellationRequested(); + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra32Bytes( configuration, diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index 416472e830..76d0bb6418 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -32,8 +32,8 @@ internal class WebpTiffCompression : TiffBaseDecompressor /// protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { - using WebpDecoderCore decoder = new(new WebpDecoderOptions()); - using Image image = decoder.Decode(stream, cancellationToken); + using WebpDecoderCore decoder = new(new WebpDecoderOptions() { GeneralOptions = this.options }); + using Image image = decoder.Decode(this.options.Configuration, stream, cancellationToken); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index a31a29bdd7..2356d45e47 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Performs the tiff decoding operation. /// -internal class TiffDecoderCore : IImageDecoderInternals +internal class TiffDecoderCore : ImageDecoderCore { /// /// General configuration options. @@ -60,8 +60,8 @@ internal class TiffDecoderCore : IImageDecoderInternals /// /// The decoder options. public TiffDecoderCore(DecoderOptions options) + : base(options) { - this.Options = options; this.configuration = options.Configuration; this.skipMetadata = options.SkipMetadata; this.maxFrames = options.MaxFrames; @@ -154,14 +154,7 @@ internal class TiffDecoderCore : IImageDecoderInternals public TiffPredictor Predictor { get; set; } /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions { get; private set; } - - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { List> frames = []; List framesMetadata = []; @@ -215,7 +208,7 @@ internal class TiffDecoderCore : IImageDecoderInternals } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.inputStream = stream; DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index c8eeb1e31f..5f91fd7393 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// /// Performs the TIFF encoding operation. /// -internal sealed class TiffEncoderCore : IImageEncoderInternals +internal sealed class TiffEncoderCore { private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 8562ecb7df..9ca6e2bee3 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Performs the webp decoding operation. /// -internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable +internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable { /// /// General configuration options. @@ -61,8 +61,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// The decoder options. public WebpDecoderCore(WebpDecoderOptions options) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.backgroundColorHandling = options.BackgroundColorHandling; this.configuration = options.GeneralOptions.Configuration; this.skipMetadata = options.GeneralOptions.SkipMetadata; @@ -70,15 +70,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable this.memoryAllocator = this.configuration.MemoryAllocator; } - /// - public DecoderOptions Options { get; } - - /// - public Size Dimensions => new((int)this.webImageInfo!.Width, (int)this.webImageInfo.Height); - /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) { Image? image = null; try @@ -136,7 +129,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable } /// - public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ReadImageHeader(stream, stackalloc byte[4]); diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index 21ce444b11..e37462fda4 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Image encoder for writing an image to a stream in the Webp format. /// -internal sealed class WebpEncoderCore : IImageEncoderInternals +internal sealed class WebpEncoderCore { /// /// Used for allocating memory during processing operations. diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 69f008e522..950265bd56 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -67,11 +67,11 @@ public partial class JpegDecoderTests public void ParseStream_BasicPropertiesAreCorrect() { JpegDecoderOptions options = new(); + Configuration configuration = options.GeneralOptions.Configuration; byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using MemoryStream ms = new(bytes); - using BufferedReadStream bufferedStream = new(Configuration.Default, ms); using JpegDecoderCore decoder = new(options); - using Image image = decoder.Decode(bufferedStream, cancellationToken: default); + using Image image = decoder.Decode(configuration, ms, cancellationToken: default); // I don't know why these numbers are different. All I know is that the decoder works // and spectral data is exactly correct also. diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index 978978989e..a3fbe4018e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Text; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; @@ -216,18 +215,17 @@ public class JpegFixture : MeasureFixture internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false) { byte[] bytes = TestFile.Create(testFileName).Bytes; - using var ms = new MemoryStream(bytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); - - JpegDecoderOptions options = new(); - var decoder = new JpegDecoderCore(options); + using MemoryStream ms = new(bytes); + JpegDecoderOptions decoderOptions = new(); + Configuration configuration = decoderOptions.GeneralOptions.Configuration; + JpegDecoderCore decoder = new(decoderOptions); if (metaDataOnly) { - decoder.Identify(bufferedStream, cancellationToken: default); + decoder.Identify(configuration, ms, default); } else { - using Image image = decoder.Decode(bufferedStream, cancellationToken: default); + using Image image = decoder.Decode(configuration, ms, default); } return decoder;