From 6485dde42d8436ab0b13a5941ab135397a4b9114 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 16 Oct 2022 13:12:40 +1000 Subject: [PATCH] Convert jpeg encoder. --- .../Formats/Jpeg/IJpegEncoderOptions.cs | 31 -------------- src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 33 +++++++++------ .../Formats/Jpeg/JpegEncoderCore.cs | 41 ++++++++++++++----- 3 files changed, 51 insertions(+), 54 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs deleted file mode 100644 index b7e6d5d614..0000000000 --- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Jpeg; - -/// -/// Encoder for writing the data image to a stream in jpeg format. -/// -internal interface IJpegEncoderOptions -{ - /// - /// Gets or sets the quality, that will be used to encode the image. Quality - /// index must be between 0 and 100 (compression from max to min). - /// Defaults to 75. - /// - public int? Quality { get; set; } - - /// - /// Gets or sets the component encoding mode. - /// - /// - /// Interleaved encoding mode encodes all color components in a single scan. - /// Non-interleaved encoding mode encodes each color component in a separate scan. - /// - public bool? Interleaved { get; set; } - - /// - /// Gets or sets jpeg color for encoding. - /// - public JpegEncodingColor? ColorType { get; set; } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 28d095eedc..fb48d2b443 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -1,21 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Formats.Jpeg; /// /// Encoder for writing the data image to a stream in jpeg format. /// -public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions +public sealed class JpegEncoder : ImageEncoder { /// /// Backing field for . /// private int? quality; - /// + /// + /// Gets or sets the quality, that will be used to encode the image. Quality + /// index must be between 0 and 100 (compression from max to min). + /// Defaults to 75. + /// + /// Quality factor must be in [1..100] range. public int? Quality { get => this.quality; @@ -30,10 +33,18 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions } } - /// + /// + /// Gets or sets the component encoding mode. + /// + /// + /// Interleaved encoding mode encodes all color components in a single scan. + /// Non-interleaved encoding mode encodes each color component in a separate scan. + /// public bool? Interleaved { get; set; } - /// + /// + /// Gets or sets jpeg color for encoding. + /// public JpegEncodingColor? ColorType { get; set; } /// @@ -42,10 +53,9 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) - where TPixel : unmanaged, IPixel + public override void Encode(Image image, Stream stream) { - var encoder = new JpegEncoderCore(this); + JpegEncoderCore encoder = new(this); encoder.Encode(image, stream); } @@ -57,10 +67,9 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions /// The to encode the image data to. /// The token to monitor for cancellation requests. /// A representing the asynchronous operation. - public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) { - var encoder = new JpegEncoderCore(this); + JpegEncoderCore encoder = new(this); return encoder.EncodeAsync(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 83c2e27e91..e915b74bc3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -29,7 +29,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// private readonly byte[] buffer = new byte[20]; - private readonly IJpegEncoderOptions options; + private readonly JpegEncoder encoder; /// /// The output stream. All attempted writes after the first error become no-ops. @@ -39,9 +39,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Initializes a new instance of the class. /// - /// The options. - public JpegEncoderCore(IJpegEncoderOptions options) - => this.options = options; + /// The parent encoder. + public JpegEncoderCore(JpegEncoder encoder) + => this.encoder = encoder; public Block8x8F[] QuantizationTables { get; } = new Block8x8F[4]; @@ -71,8 +71,8 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals JpegMetadata jpegMetadata = metadata.GetJpegMetadata(); JpegFrameConfig frameConfig = this.GetFrameConfig(jpegMetadata); - bool interleaved = this.options.Interleaved ?? jpegMetadata.Interleaved ?? true; - using var frame = new JpegFrame(image, frameConfig, interleaved); + bool interleaved = this.encoder.Interleaved ?? jpegMetadata.Interleaved ?? true; + using JpegFrame frame = new(image, frameConfig, interleaved); // Write the Start Of Image marker. this.WriteStartOfImage(); @@ -96,14 +96,14 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals this.WriteStartOfFrame(image.Width, image.Height, frameConfig); // Write the Huffman tables. - var scanEncoder = new HuffmanScanEncoder(frame.BlocksPerMcu, stream); + HuffmanScanEncoder scanEncoder = new(frame.BlocksPerMcu, stream); this.WriteDefineHuffmanTables(frameConfig.HuffmanTables, scanEncoder); // Write the quantization tables. - this.WriteDefineQuantizationTables(frameConfig.QuantizationTables, this.options.Quality, jpegMetadata); + this.WriteDefineQuantizationTables(frameConfig.QuantizationTables, this.encoder.Quality, jpegMetadata); // Write scans with actual pixel data - using var spectralConverter = new SpectralConverter(frame, image, this.QuantizationTables); + using SpectralConverter spectralConverter = new(frame, image, this.QuantizationTables); this.WriteHuffmanScans(frame, frameConfig, spectralConverter, scanEncoder, cancellationToken); // Write the End Of Image marker. @@ -172,6 +172,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Writes the Define Huffman Table marker and tables. /// + /// The table configuration. + /// The scan encoder. + /// is . private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder) { if (tableConfigs is null) @@ -203,6 +206,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Writes the APP14 marker to indicate the image is in RGB color space. /// + /// The color transform byte. private void WriteApp14Marker(byte colorTransform) { this.WriteMarkerHeader(JpegConstants.Markers.APP14, 2 + Components.Decoder.AdobeMarker.Length); @@ -498,6 +502,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Writes the Start Of Frame (Baseline) marker. /// + /// The frame width. + /// The frame height. + /// The frame configuration. private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame) { JpegComponentConfig[] components = frame.Components; @@ -536,6 +543,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Writes the StartOfScan marker. /// + /// The collecction of component configuration items. private void WriteStartOfScan(Span components) { // Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: @@ -588,7 +596,18 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Writes scans for given config. /// - private void WriteHuffmanScans(JpegFrame frame, JpegFrameConfig frameConfig, SpectralConverter spectralConverter, HuffmanScanEncoder encoder, CancellationToken cancellationToken) + /// The type of pixel format. + /// The current frame. + /// The frame configuration. + /// The spectral converter. + /// The scan encoder. + /// The cancellation token. + private void WriteHuffmanScans( + JpegFrame frame, + JpegFrameConfig frameConfig, + SpectralConverter spectralConverter, + HuffmanScanEncoder encoder, + CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { if (frame.Components.Length == 1) @@ -696,7 +715,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals private JpegFrameConfig GetFrameConfig(JpegMetadata metadata) { - JpegEncodingColor color = this.options.ColorType ?? metadata.ColorType ?? JpegEncodingColor.YCbCrRatio420; + JpegEncodingColor color = this.encoder.ColorType ?? metadata.ColorType ?? JpegEncodingColor.YCbCrRatio420; JpegFrameConfig frameConfig = Array.Find( FrameConfigs, cfg => cfg.EncodingColor == color);