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
{
/// <summary>
/// Gets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// Gets the quality, that will be used to encode the luminance 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? 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>
/// 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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Threading;
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).
/// Defaults to <value>75</value>.
/// </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>
/// 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>
internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals
{
/// <summary>
/// Default JPEG encoding quality for both luminance and chominance tables.
/// </summary>
private const int DefaultQualityValue = 75;
/// <summary>
/// The number of quantization tables.
/// </summary>
@ -41,7 +46,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// The quality, that will be used to encode the image.
/// </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>
/// Gets or sets the subsampling method to use.
@ -59,7 +69,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="options">The options</param>
public JpegEncoderCore(IJpegEncoderOptions options)
{
this.quality = options.Quality;
this.luminanceQuality = options.LuminanceQuality;
this.chrominanceQuality = options.ChrominanceQuality;
this.subsample = options.Subsample;
this.colorType = options.ColorType;
}
@ -86,19 +97,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream = stream;
ImageMetadata metadata = image.Metadata;
JpegMetadata jpegMetadata = metadata.GetJpegMetadata();
// Compute number of components based on color type in options.
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.
// TODO: This looks ugly, should we write chrominance table for luminance-only images?
// If not - this can code can be simplified
Block8x8F luminanceQuantTable = Quantization.ScaleLuminanceTable(qlty);
Block8x8F chrominanceQuantTable = Quantization.ScaleChrominanceTable(qlty);
// TODO: Right now encoder writes both quantization tables for grayscale images - we shouldn't do that
int lumaQuality = Numerics.Clamp(this.luminanceQuality ?? jpegMetadata.LuminanceQuality, 1, 100);
Block8x8F luminanceQuantTable = Quantization.ScaleLuminanceTable(lumaQuality);
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.
this.WriteApplicationHeader(metadata);

Loading…
Cancel
Save