diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs new file mode 100644 index 0000000000..97439182d5 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder2.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Image decoder for generating an image out of a Windows bitmap stream. + /// + public class BmpDecoder2 : ImageDecoder + { + /// + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + public override Image DecodeSpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.DecodeSpecialized(options, stream, cancellationToken); + + /// + public override IImageInfo IdentifySpecialized(BmpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => throw new NotImplementedException(); + } +} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs new file mode 100644 index 0000000000..74509d68f3 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Image decoder options for decoding Windows bitmap streams. + /// + public sealed class BmpDecoderOptions : ISpecializedDecoderOptions + { + /// + public DecoderOptions GeneralOptions { get; set; } = new(); + + /// + /// Gets the value indicating how to deal with skipped pixels, + /// which can occur during decoding run length encoded bitmaps. + /// + public RleSkippedPixelHandling RleSkippedPixelHandling { get; } + } +} diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs new file mode 100644 index 0000000000..a690b06075 --- /dev/null +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Provides general configuration options for decoding image formats. + /// + public sealed class DecoderOptions + { + /// + /// Gets or sets a custom Configuration instance to be used by the image processing pipeline. + /// + public Configuration Configuration { get; set; } = Configuration.Default; + + /// + /// Gets or sets the target size to decode the image into. + /// + public Size? TargetSize { get; set; } = null; + + /// + /// Gets or sets a value indicating whether to ignore encoded metadata when decoding. + /// + public bool SkipMetadata { get; set; } = false; + + /// + /// Gets or sets the number of image frames to decode. + /// Leave to decode all frames. + /// + // supported decoders may handle this internally but we will fallback to removeing additional frames after decode. + public int? MaxFrames { get; set; } = null; + } +} diff --git a/src/ImageSharp/Formats/IImageDecoder2.cs b/src/ImageSharp/Formats/IImageDecoder2.cs new file mode 100644 index 0000000000..76d1a754d4 --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoder2.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Encapsulates properties and methods required for decoding an image from a stream. + /// + public interface IImageDecoder2 + { + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an . + /// + /// The general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/IImageInfoDetector2.cs b/src/ImageSharp/Formats/IImageInfoDetector2.cs new file mode 100644 index 0000000000..3d340f06e8 --- /dev/null +++ b/src/ImageSharp/Formats/IImageInfoDetector2.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Encapsulates methods used for detecting the raw image information without fully decoding it. + /// + public interface IImageInfoDetector2 + { + /// + /// Reads the raw image information from the specified stream. + /// + /// The general decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The object. + /// Thrown if the encoded image contains errors. + IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs new file mode 100644 index 0000000000..b79938f08d --- /dev/null +++ b/src/ImageSharp/Formats/ISpecializedDecoderOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Provides specialized configuration options for decoding image formats. + /// + public interface ISpecializedDecoderOptions + { + /// + /// Gets or sets the general decoder options. + /// + DecoderOptions GeneralOptions { get; set; } + } +} diff --git a/src/ImageSharp/Formats/ImageDecoder{T}.cs b/src/ImageSharp/Formats/ImageDecoder{T}.cs new file mode 100644 index 0000000000..96080b3bed --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoder{T}.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// The base class for all image decoders. + /// + /// The type of specialized decoder options. + public abstract class ImageDecoder : IImageInfoDetector2, IImageDecoder2 + where T : ISpecializedDecoderOptions, new() + { + /// + public IImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + T specializedOptions = new() { GeneralOptions = options }; + return this.IdentifySpecialized(specializedOptions, stream, cancellationToken); + } + + /// + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + T specializedOptions = new() { GeneralOptions = options }; + Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); + + Resize(options, image); + + return image; + } + + /// + public Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + T specializedOptions = new() { GeneralOptions = options }; + Image image = this.DecodeSpecialized(specializedOptions, stream, cancellationToken); + + Resize(options, image); + + return image; + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The object. + /// Thrown if the encoded image contains errors. + public abstract IImageInfo IdentifySpecialized(T options, Stream stream, CancellationToken cancellationToken); + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The pixel format. + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel; + + /// + /// Decodes the image from the specified stream to an of a specific pixel type. + /// + /// The specialized decoder options. + /// The containing image data. + /// The token to monitor for cancellation requests. + /// The . + /// Thrown if the encoded image contains errors. + public abstract Image DecodeSpecialized(T options, Stream stream, CancellationToken cancellationToken); + + /// + /// Performs a resize operation against the decoded image. If the target size is not set, or the image size + /// already matches the target size, the image is untouched. + /// + /// The decoder options. + /// The decoded image. + protected static void Resize(DecoderOptions options, Image image) + { + if (ShouldResize(options, image)) + { + ResizeOptions resizeOptions = new() + { + Size = options.TargetSize.Value, + Sampler = KnownResamplers.Box, + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(resizeOptions)); + } + } + + /// + /// Determines whether the decoded image should be resized. + /// + /// The decoder options. + /// The decoded image. + /// if the image should be resized, otherwise; . + private static bool ShouldResize(DecoderOptions options, Image image) + { + if (options.TargetSize is null) + { + return false; + } + + Size targetSize = options.TargetSize.Value; + Size currentSize = image.Size(); + return currentSize.Width != targetSize.Width && currentSize.Height != targetSize.Height; + } + } +}