Browse Source

Convert jpeg encoder.

pull/2269/head
James Jackson-South 4 years ago
parent
commit
6485dde42d
  1. 31
      src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
  2. 33
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  3. 41
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

31
src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg;
/// <summary>
/// Encoder for writing the data image to a stream in jpeg format.
/// </summary>
internal interface IJpegEncoderOptions
{
/// <summary>
/// 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 <value>75</value>.
/// </summary>
public int? Quality { get; set; }
/// <summary>
/// Gets or sets the component encoding mode.
/// </summary>
/// <remarks>
/// Interleaved encoding mode encodes all color components in a single scan.
/// Non-interleaved encoding mode encodes each color component in a separate scan.
/// </remarks>
public bool? Interleaved { get; set; }
/// <summary>
/// Gets or sets jpeg color for encoding.
/// </summary>
public JpegEncodingColor? ColorType { get; set; }
}

33
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;
/// <summary>
/// Encoder for writing the data image to a stream in jpeg format.
/// </summary>
public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
public sealed class JpegEncoder : ImageEncoder
{
/// <summary>
/// Backing field for <see cref="Quality"/>.
/// </summary>
private int? quality;
/// <inheritdoc/>
/// <summary>
/// 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 <value>75</value>.
/// </summary>
/// <exception cref="ArgumentException">Quality factor must be in [1..100] range.</exception>
public int? Quality
{
get => this.quality;
@ -30,10 +33,18 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
}
}
/// <inheritdoc/>
/// <summary>
/// Gets or sets the component encoding mode.
/// </summary>
/// <remarks>
/// Interleaved encoding mode encodes all color components in a single scan.
/// Non-interleaved encoding mode encodes each color component in a separate scan.
/// </remarks>
public bool? Interleaved { get; set; }
/// <inheritdoc/>
/// <summary>
/// Gets or sets jpeg color for encoding.
/// </summary>
public JpegEncodingColor? ColorType { get; set; }
/// <summary>
@ -42,10 +53,9 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
public override void Encode<TPixel>(Image<TPixel> 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
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
var encoder = new JpegEncoderCore(this);
JpegEncoderCore encoder = new(this);
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}

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

@ -29,7 +29,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
/// </summary>
private readonly byte[] buffer = new byte[20];
private readonly IJpegEncoderOptions options;
private readonly JpegEncoder encoder;
/// <summary>
/// The output stream. All attempted writes after the first error become no-ops.
@ -39,9 +39,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
/// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderCore"/> class.
/// </summary>
/// <param name="options">The options.</param>
public JpegEncoderCore(IJpegEncoderOptions options)
=> this.options = options;
/// <param name="encoder">The parent encoder.</param>
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<TPixel>(frame, image, this.QuantizationTables);
using SpectralConverter<TPixel> 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
/// <summary>
/// Writes the Define Huffman Table marker and tables.
/// </summary>
/// <param name="tableConfigs">The table configuration.</param>
/// <param name="scanEncoder">The scan encoder.</param>
/// <exception cref="ArgumentNullException"><paramref name="tableConfigs"/> is <see langword="null"/>.</exception>
private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder)
{
if (tableConfigs is null)
@ -203,6 +206,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
/// <summary>
/// Writes the APP14 marker to indicate the image is in RGB color space.
/// </summary>
/// <param name="colorTransform">The color transform byte.</param>
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
/// <summary>
/// Writes the Start Of Frame (Baseline) marker.
/// </summary>
/// <param name="width">The frame width.</param>
/// <param name="height">The frame height.</param>
/// <param name="frame">The frame configuration.</param>
private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame)
{
JpegComponentConfig[] components = frame.Components;
@ -536,6 +543,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals
/// <summary>
/// Writes the StartOfScan marker.
/// </summary>
/// <param name="components">The collecction of component configuration items.</param>
private void WriteStartOfScan(Span<JpegComponentConfig> 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
/// <summary>
/// Writes scans for given config.
/// </summary>
private void WriteHuffmanScans<TPixel>(JpegFrame frame, JpegFrameConfig frameConfig, SpectralConverter<TPixel> spectralConverter, HuffmanScanEncoder encoder, CancellationToken cancellationToken)
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
/// <param name="frame">The current frame.</param>
/// <param name="frameConfig">The frame configuration.</param>
/// <param name="spectralConverter">The spectral converter.</param>
/// <param name="encoder">The scan encoder.</param>
/// <param name="cancellationToken">The cancellation token.</param>
private void WriteHuffmanScans<TPixel>(
JpegFrame frame,
JpegFrameConfig frameConfig,
SpectralConverter<TPixel> spectralConverter,
HuffmanScanEncoder encoder,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
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);

Loading…
Cancel
Save