Browse Source

(WIP) quality

pull/1706/head
Dmitry Pentin 5 years ago
parent
commit
cf1ad8edc2
  1. 13
      src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
  2. 34
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  3. 35
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

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

@ -9,11 +9,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
internal interface IJpegEncoderOptions internal interface IJpegEncoderOptions
{ {
/// <summary> /// <summary>
/// Gets the quality, that will be used to encode the image. Quality /// Gets the quality, that will be used to encode the luminance data of the image.
/// index must be between 0 and 100 (compression from max to min). /// Quality index must be between 0 and 100 (compression from max to min).
/// </summary> /// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value> /// <value>The quality of the jpg image from 0 to 100.</value>
int? Quality { get; } int? LuminanceQuality { get; }
/// <summary>
/// Gets the quality, that will be used to encode the chrominance data of the image.
/// Quality index must be between 0 and 100 (compression from max to min).
/// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value>
int? ChrominanceQuality { get; }
/// <summary> /// <summary>
/// Gets the subsample ration, that will be used to encode the image. /// Gets the subsample ration, that will be used to encode the image.

34
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -18,7 +19,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// index must be between 0 and 100 (compression from max to min). /// index must be between 0 and 100 (compression from max to min).
/// Defaults to <value>75</value>. /// Defaults to <value>75</value>.
/// </summary> /// </summary>
public int? Quality { get; set; } public int? Quality
{
[Obsolete("This accessor will soon be deprecated. Use LuminanceQuality and ChrominanceQuality getters instead.", error: false)]
get
{
const int defaultQuality = 75;
int lumaQuality = this.LuminanceQuality ?? defaultQuality;
int chromaQuality = this.LuminanceQuality ?? lumaQuality;
return (int)Math.Round((lumaQuality + chromaQuality) / 2f);
}
set
{
this.LuminanceQuality = value;
this.ChrominanceQuality = value;
}
}
/// <summary>
/// Gets or sets the quality, that will be used to encode luminance image data.
/// Quality index must be between 0 and 100 (compression from max to min).
/// Defaults to <value>75</value>.
/// </summary>
public int? LuminanceQuality { get; set; }
/// <summary>
/// Gets or sets the quality, that will be used to encode chrominance image data.
/// Quality index must be between 0 and 100 (compression from max to min).
/// Defaults to <value>75</value>.
/// </summary>
public int? ChrominanceQuality { get; set; }
/// <summary> /// <summary>
/// Gets or sets the subsample ration, that will be used to encode the image. /// Gets or sets the subsample ration, that will be used to encode the image.

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

@ -23,6 +23,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals
{ {
/// <summary>
/// Default JPEG encoding quality for both luminance and chominance tables.
/// </summary>
private const int DefaultQualityValue = 75;
/// <summary> /// <summary>
/// The number of quantization tables. /// The number of quantization tables.
/// </summary> /// </summary>
@ -41,7 +46,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// The quality, that will be used to encode the image. /// The quality, that will be used to encode the image.
/// </summary> /// </summary>
private readonly int? quality; private readonly int? luminanceQuality;
/// <summary>
/// The quality, that will be used to encode the image.
/// </summary>
private readonly int? chrominanceQuality;
/// <summary> /// <summary>
/// Gets or sets the subsampling method to use. /// Gets or sets the subsampling method to use.
@ -59,7 +69,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="options">The options</param> /// <param name="options">The options</param>
public JpegEncoderCore(IJpegEncoderOptions options) public JpegEncoderCore(IJpegEncoderOptions options)
{ {
this.quality = options.Quality; this.luminanceQuality = options.LuminanceQuality;
this.chrominanceQuality = options.ChrominanceQuality;
this.subsample = options.Subsample; this.subsample = options.Subsample;
this.colorType = options.ColorType; this.colorType = options.ColorType;
} }
@ -86,19 +97,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream = stream; this.outputStream = stream;
ImageMetadata metadata = image.Metadata; ImageMetadata metadata = image.Metadata;
JpegMetadata jpegMetadata = metadata.GetJpegMetadata();
// Compute number of components based on color type in options. // Compute number of components based on color type in options.
int componentCount = (this.colorType == JpegColorType.Luminance) ? 1 : 3; int componentCount = (this.colorType == JpegColorType.Luminance) ? 1 : 3;
// System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100);
this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420;
// Initialize the quantization tables. // Initialize the quantization tables.
// TODO: This looks ugly, should we write chrominance table for luminance-only images? // TODO: Right now encoder writes both quantization tables for grayscale images - we shouldn't do that
// If not - this can code can be simplified int lumaQuality = Numerics.Clamp(this.luminanceQuality ?? jpegMetadata.LuminanceQuality, 1, 100);
Block8x8F luminanceQuantTable = Quantization.ScaleLuminanceTable(qlty); Block8x8F luminanceQuantTable = Quantization.ScaleLuminanceTable(lumaQuality);
Block8x8F chrominanceQuantTable = Quantization.ScaleChrominanceTable(qlty); Block8x8F chrominanceQuantTable = default;
if (componentCount > 1)
{
int chromaQuality = Numerics.Clamp(this.chrominanceQuality ?? jpegMetadata.ChrominanceQuality, 1, 100);
this.subsample ??= chromaQuality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420;
chrominanceQuantTable = Quantization.ScaleChrominanceTable(chromaQuality);
}
// Write the Start Of Image marker. // Write the Start Of Image marker.
this.WriteApplicationHeader(metadata); this.WriteApplicationHeader(metadata);

Loading…
Cancel
Save