diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 4c81c58dd3..593937b929 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -102,18 +102,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Compute number of components based on color type in options. int componentCount = (this.colorType == JpegColorType.Luminance) ? 1 : 3; - // Initialize the quantization tables. // 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); - } + // Initialize the quantization tables. + this.InitQuantizationTables(componentCount, jpegMetadata, out Block8x8F luminanceQuantTable, out Block8x8F chrominanceQuantTable); // Write the Start Of Image marker. this.WriteApplicationHeader(metadata); @@ -651,5 +642,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.buffer[3] = (byte)(length & 0xff); this.outputStream.Write(this.buffer, 0, 4); } + + /// + /// Initializes quntization tables. + /// + /// Color components count. + /// Jpeg metadata instance. + /// Output luminance quantization table. + /// Output chrominance quantization table. + private void InitQuantizationTables(int componentCount, JpegMetadata metadata, out Block8x8F luminanceQuantTable, out Block8x8F chrominanceQuantTable) + { + // We take quality values in a hierarchical order: + // 1. Check if encoder has set quality + // 2. Check if metadata has special table for encoding + // 3. Check if metadata has set quality + // 4. Take default quality value - 75 + int lumaQuality = Numerics.Clamp( + this.luminanceQuality ?? metadata.LuminanceQuality ?? DefaultQualityValue, + min: 1, + max: 100); + + luminanceQuantTable = Quantization.ScaleLuminanceTable(lumaQuality); + chrominanceQuantTable = default; + if (componentCount > 1) + { + int chromaQuality = Numerics.Clamp( + this.chrominanceQuality ?? metadata.ChrominanceQuality ?? DefaultQualityValue, + min: 1, + max: 100); + + this.subsample ??= chromaQuality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; + + chrominanceQuantTable = Quantization.ScaleChrominanceTable(chromaQuality); + } + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 0579e7b5e8..8b3332ef8b 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -24,7 +24,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The metadata to create an instance from. private JpegMetadata(JpegMetadata other) { - this.Quality = other.Quality; this.ColorType = other.ColorType; this.LumaQuantizationTable = other.LumaQuantizationTable; @@ -56,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// This value might not be accurate if it was calculated during jpeg decoding /// with non-complient ITU quantization tables. /// - public int LuminanceQuality { get; set; } + public int? LuminanceQuality { get; set; } /// /// Gets or sets the jpeg chrominance quality. @@ -65,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// This value might not be accurate if it was calculated during jpeg decoding /// with non-complient ITU quantization tables. /// - public int ChrominanceQuality { get; set; } + public int? ChrominanceQuality { get; set; } /// /// Gets a value indicating whether jpeg luminance data was encoded using ITU complient quantization table. @@ -82,7 +81,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public int Quality { - get => (int)Math.Round((this.LuminanceQuality + this.ChrominanceQuality) / 2f); + [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;