diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index f36f3d09b..61ec85b47 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; 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.Webp; @@ -195,39 +196,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); } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 863fed359..5dc30575d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -21,7 +21,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. @@ -104,22 +104,15 @@ 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; } /// - 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 @@ -205,7 +198,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 PixelTypeInfo(this.infoHeader.BitsPerPixel), new(this.infoHeader.Width, this.infoHeader.Height), this.metadata); @@ -1369,6 +1362,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/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index aecbbbbc7..b1d357f86 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; @@ -287,6 +280,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 1215768e4..1daa713cb 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -19,7 +19,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 06fb59764..000000000 --- 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/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index ebb45d701..549a28d40 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -189,7 +189,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); @@ -206,7 +206,7 @@ public abstract class ImageDecoder : IImageDecoder if (stream.CanSeek) { - return PeformActionAndResetPosition(stream, stream.Position); + return PerformActionAndResetPosition(stream, stream.Position); } Configuration configuration = options.Configuration; @@ -231,7 +231,7 @@ public abstract class ImageDecoder : IImageDecoder throw new NotSupportedException("Cannot read from the stream."); } - Task PeformActionAndResetPosition(Stream s, long position, CancellationToken ct) + Task PerformActionAndResetPosition(Stream s, long position, CancellationToken ct) { try { @@ -263,15 +263,15 @@ public abstract class ImageDecoder : IImageDecoder // code below to copy the stream to an in-memory buffer before invoking the action. if (stream is MemoryStream ms) { - return PeformActionAndResetPosition(ms, ms.Position, cancellationToken); + return PerformActionAndResetPosition(ms, ms.Position, cancellationToken); } if (stream is ChunkedMemoryStream cms) { - return PeformActionAndResetPosition(cms, cms.Position, cancellationToken); + return PerformActionAndResetPosition(cms, cms.Position, cancellationToken); } - return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken); + return CopyToMemoryStreamAndActionAsync(options, stream, PerformActionAndResetPosition, cancellationToken); } private static async Task CopyToMemoryStreamAndActionAsync( @@ -282,7 +282,7 @@ public abstract class ImageDecoder : IImageDecoder { long position = stream.CanSeek ? stream.Position : 0; Configuration configuration = options.Configuration; - using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); + await using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; return await action(memoryStream, position, cancellationToken).ConfigureAwait(false); diff --git a/src/ImageSharp/Formats/ImageDecoderCore.cs b/src/ImageSharp/Formats/ImageDecoderCore.cs new file mode 100644 index 000000000..adf0107da --- /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 a1abd7dc3..000000000 --- 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 ccace190f..b0bc66008 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(); @@ -1216,6 +1209,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/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 3fe339865..73a5085c9 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); @@ -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/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index dd5e16d7b..1eeac42b8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -28,7 +28,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. @@ -130,8 +130,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; @@ -141,8 +141,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; @@ -153,14 +153,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(); @@ -335,7 +328,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/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index deb0a37f0..8552e164d 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); @@ -68,7 +63,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/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 34f4c2bcf..e2dd919d2 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 { @@ -653,7 +646,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( @@ -933,6 +926,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/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index 416472e83..76d0bb641 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 aed6d4ec6..96e6d2a89 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 = new(); List framesMetadata = new(); @@ -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/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 21a25860c..781b8246f 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]); @@ -186,36 +179,39 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable Span buffer = stackalloc byte[4]; WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); + WebpImageInfo webpImageInfo; WebpFeatures features = new(); switch (chunkType) { case WebpChunkType.Vp8: webpMetadata.FileFormat = WebpFileFormatType.Lossy; - return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); + webpImageInfo = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); + break; case WebpChunkType.Vp8L: webpMetadata.FileFormat = WebpFileFormatType.Lossless; - return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); + webpImageInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); + break; case WebpChunkType.Vp8X: - WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(stream, buffer, features); + webpImageInfo = WebpChunkParsingUtils.ReadVp8XHeader(stream, buffer, features); while (stream.Position < stream.Length) { chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); if (chunkType == WebpChunkType.Vp8) { webpMetadata.FileFormat = WebpFileFormatType.Lossy; - webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); + webpImageInfo = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); } else if (chunkType == WebpChunkType.Vp8L) { webpMetadata.FileFormat = WebpFileFormatType.Lossless; - webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); + webpImageInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); } else if (WebpChunkParsingUtils.IsOptionalVp8XChunk(chunkType)) { bool isAnimationChunk = this.ParseOptionalExtendedChunks(stream, metadata, chunkType, features, ignoreAlpha, buffer); if (isAnimationChunk) { - return webpInfos; + break; } } else @@ -226,12 +222,17 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable } } - return webpInfos; + break; default: WebpThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); - return - new WebpImageInfo(); // this return will never be reached, because throw helper will throw an exception. + + // This return will never be reached, because throw helper will throw an exception. + webpImageInfo = new(); + break; } + + this.Dimensions = new Size((int)webpImageInfo.Width, (int)webpImageInfo.Height); + return webpImageInfo; } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 2fe428260..97be5d838 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 978978989..a3fbe4018 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;