From 096ef3d3aa1189c1abcebf03ee7deff30a138874 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Tue, 30 Mar 2021 18:13:05 +0200 Subject: [PATCH] Refactor out JpegSubsample.Grayscale --- .../Formats/Jpeg/JpegEncoderCore.cs | 81 ++++++++++--------- src/ImageSharp/Formats/Jpeg/JpegSubsample.cs | 5 -- .../Formats/Jpg/JpegEncoderTests.cs | 30 ++++--- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index bca03b109..f5dc1c79f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -189,9 +189,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100); this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; - // Force SubSample into Grayscale for single component ColorType. - this.subsample = (componentCount == 1) ? JpegSubsample.Grayscale : this.subsample; - // Convert from a quality rating to a scaling factor. int scale; if (qlty < 50) @@ -921,32 +918,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg 0x01 }; - switch (this.subsample) + if (this.colorType == JpegColorType.Luminance) { - case JpegSubsample.Grayscale: - subsamples = stackalloc byte[] - { - 0x11, - 0x00, - 0x00 - }; - break; - case JpegSubsample.Ratio444: - subsamples = stackalloc byte[] - { - 0x11, - 0x11, - 0x11 - }; - break; - case JpegSubsample.Ratio420: - subsamples = stackalloc byte[] - { - 0x22, - 0x11, - 0x11 - }; - break; + subsamples = stackalloc byte[] + { + 0x11, + 0x00, + 0x00 + }; + } + else + { + switch (this.subsample) + { + case JpegSubsample.Ratio444: + subsamples = stackalloc byte[] + { + 0x11, + 0x11, + 0x11 + }; + break; + case JpegSubsample.Ratio420: + subsamples = stackalloc byte[] + { + 0x22, + 0x11, + 0x11 + }; + break; + } } // Length (high byte, low byte), 8 + components * 3. @@ -1025,17 +1026,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(this.buffer, 0, sosSize + 2); ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer); - switch (this.subsample) + if (this.colorType == JpegColorType.Luminance) { - case JpegSubsample.Grayscale: - this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); - break; - case JpegSubsample.Ratio444: - this.Encode444(image, cancellationToken, ref emitBufferBase); - break; - case JpegSubsample.Ratio420: - this.Encode420(image, cancellationToken, ref emitBufferBase); - break; + this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); + } + else + { + switch (this.subsample) + { + case JpegSubsample.Ratio444: + this.Encode444(image, cancellationToken, ref emitBufferBase); + break; + case JpegSubsample.Ratio420: + this.Encode420(image, cancellationToken, ref emitBufferBase); + break; + } } // Pad the last byte with 1's. diff --git a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs index 3989bef7a..16488f6d2 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs @@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public enum JpegSubsample { - /// - /// Only luminance - No chrome channels. - /// - Grayscale, - /// /// High Quality - Each of the three Y'CbCr components have the same sample rate, /// thus there is no chroma subsampling. diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 0d277a132..9a1d423a6 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -40,6 +40,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { JpegSubsample.Ratio444, 100 }, }; + public static readonly TheoryData Grayscale_Quality = + new TheoryData + { + { 40 }, + { 60 }, + { 100 } + }; + public static readonly TheoryData RatioFiles = new TheoryData { @@ -85,14 +93,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality); [Theory] - [WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.Rgba32)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L16)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La16)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La32)] - public void EncodeBaseline_Grayscale(TestImageProvider provider, JpegSubsample subsample, int quality) - where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance); + [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgba32, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L8, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L16, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)] + public void EncodeBaseline_Grayscale(TestImageProvider provider, int quality) + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, null, quality, JpegColorType.Luminance); [Theory] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] @@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg /// /// Anton's SUPER-SCIENTIFIC tolerance threshold calculation /// - private static ImageComparer GetComparer(int quality, JpegSubsample subsample) + private static ImageComparer GetComparer(int quality, JpegSubsample? subsample) { float tolerance = 0.015f; // ~1.5% @@ -140,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private static void TestJpegEncoderCore( TestImageProvider provider, - JpegSubsample subsample, + JpegSubsample? subsample, int quality = 100, JpegColorType colorType = JpegColorType.YCbCr, ImageComparer comparer = null) @@ -311,7 +319,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs) { using var image = new Image(5000, 5000); - using MemoryStream stream = new MemoryStream(); + using var stream = new MemoryStream(); var cts = new CancellationTokenSource(); if (cancellationDelayMs == 0) {