From b506e924793a8d14cc7a9694c6bb33c151fd2157 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 15 Aug 2021 17:21:54 +0200 Subject: [PATCH] Add additional YCbCr subsample rates --- src/ImageSharp/Formats/Jpeg/JpegColorType.cs | 35 +++++++++++++++++-- .../Formats/Jpeg/JpegDecoderCore.cs | 29 +++++++++++++++ .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 5 +++ .../Formats/Jpg/JpegEncoderTests.cs | 23 +++++++----- tests/ImageSharp.Tests/TestImages.cs | 3 ++ tests/Images/Input/Jpg/baseline/jpeg410.jpg | 3 ++ tests/Images/Input/Jpg/baseline/jpeg411.jpg | 3 ++ tests/Images/Input/Jpg/baseline/jpeg422.jpg | 3 ++ 8 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/jpeg410.jpg create mode 100644 tests/Images/Input/Jpg/baseline/jpeg411.jpg create mode 100644 tests/Images/Input/Jpg/baseline/jpeg422.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegColorType.cs b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs index 11d5f65a4c..d6a9542c35 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegColorType.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs @@ -22,14 +22,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// YCbCrRatio444 = 1, + /// + /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. + /// The two chroma components are sampled at half the horizontal sample rate of luma while vertically it has full resolution. + /// + /// Note: Not supported by the encoder. + /// + YCbCrRatio422 = 2, + + /// + /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. + /// In 4:1:1 chroma subsampling, the horizontal color resolution is quartered. + /// + /// Note: Not supported by the encoder. + /// + YCbCrRatio411 = 3, + + /// + /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. + /// This ratio uses half of the vertical and one-fourth the horizontal color resolutions. + /// + /// Note: Not supported by the encoder. + /// + YCbCrRatio410 = 4, + /// /// Single channel, luminance. /// - Luminance = 2, + Luminance = 5, /// /// The pixel data will be preserved as RGB without any sub sampling. /// - Rgb = 3, + Rgb = 6, + + /// + /// CMYK colorspace (cyan, magenta, yellow, and key black) intended for printing. + /// + /// Note: Not supported by the encoder. + /// + Cmyk = 7, } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 343362f6c8..83aacb48fb 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -457,8 +457,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { case JpegColorSpace.Grayscale: return JpegColorType.Luminance; + case JpegColorSpace.RGB: return JpegColorType.Rgb; + case JpegColorSpace.YCbCr: if (this.Frame.Components[0].HorizontalSamplingFactor == 1 && this.Frame.Components[0].VerticalSamplingFactor == 1 && this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && @@ -466,11 +468,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { return JpegColorType.YCbCrRatio444; } + else if (this.Frame.Components[0].HorizontalSamplingFactor == 2 && this.Frame.Components[0].VerticalSamplingFactor == 2 && + this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && + this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) + { + return JpegColorType.YCbCrRatio420; + } + else if (this.Frame.Components[0].HorizontalSamplingFactor == 1 && this.Frame.Components[0].VerticalSamplingFactor == 1 && + this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 2 && + this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 2) + { + return JpegColorType.YCbCrRatio422; + } + else if (this.Frame.Components[0].HorizontalSamplingFactor == 4 && this.Frame.Components[0].VerticalSamplingFactor == 1 && + this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && + this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) + { + return JpegColorType.YCbCrRatio411; + } + else if (this.Frame.Components[0].HorizontalSamplingFactor == 4 && this.Frame.Components[0].VerticalSamplingFactor == 2 && + this.Frame.Components[1].HorizontalSamplingFactor == 1 && this.Frame.Components[1].VerticalSamplingFactor == 1 && + this.Frame.Components[2].HorizontalSamplingFactor == 1 && this.Frame.Components[2].VerticalSamplingFactor == 1) + { + return JpegColorType.YCbCrRatio410; + } else { return JpegColorType.YCbCrRatio420; } + case JpegColorSpace.Cmyk: + return JpegColorType.Cmyk; + default: return JpegColorType.YCbCrRatio420; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index b56748d138..e5f8989c5c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -143,6 +143,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, JpegColorType.YCbCrRatio420)] [InlineData(TestImages.Jpeg.Baseline.Jpeg444, JpegColorType.YCbCrRatio444)] [InlineData(TestImages.Jpeg.Baseline.JpegRgb, JpegColorType.Rgb)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorType.Cmyk)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg410, JpegColorType.YCbCrRatio410)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg422, JpegColorType.YCbCrRatio422)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg411, JpegColorType.YCbCrRatio411)] public void Identify_DetectsCorrectColorType(string imagePath, JpegColorType expectedColorType) { var testFile = TestFile.Create(imagePath); @@ -159,6 +163,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgba32, JpegColorType.YCbCrRatio420)] [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.YCbCrRatio444)] [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgba32, JpegColorType.Rgb)] + [WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgba32, JpegColorType.Cmyk)] public void Decode_DetectsCorrectColorType(TestImageProvider provider, JpegColorType expectedColorType) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 3cdfb5fad3..a30a5611f9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [WithFile(TestImages.Jpeg.Baseline.Jpeg410, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Baseline.Jpeg411, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Baseline.Jpeg422, PixelTypes.Rgba32)] - public void Encode_WithUnsupportedColorType_FromInput_DefaultsToYCbCr420(TestImageProvider provider) + public void Encode_WithUnsupportedColorType_FromInputImage_DefaultsToYCbCr420(TestImageProvider provider) where TPixel : unmanaged, IPixel { // arrange @@ -100,7 +100,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var memoryStream = new MemoryStream(); // act - input.Save(memoryStream, JpegEncoder); + input.Save(memoryStream, new JpegEncoder() + { + Quality = 75 + }); // assert memoryStream.Position = 0; @@ -110,12 +113,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.Cmyk)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.YCbCrRatio410)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.YCbCrRatio411)] - [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.YCbCrRatio422)] - public void Encode_WithUnsupportedColorType_DefaultsToYCbCr420(TestImageProvider provider, JpegColorType colorType) - where TPixel : unmanaged, IPixel + [InlineData(JpegColorType.Cmyk)] + [InlineData(JpegColorType.YCbCrRatio410)] + [InlineData(JpegColorType.YCbCrRatio411)] + [InlineData(JpegColorType.YCbCrRatio422)] + public void Encode_WithUnsupportedColorType_DefaultsToYCbCr420(JpegColorType colorType) { // arrange var jpegEncoder = new JpegEncoder() { ColorType = colorType }; @@ -153,6 +155,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)] + public void EncodeBaseline_CalliphoraPartial(TestImageProvider provider, JpegColorType colorType, int quality) + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality); + [Theory] [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 73, 71, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 572c5abce1..feff08b94f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -191,6 +191,9 @@ namespace SixLabors.ImageSharp.Tests public const string Jpeg444 = "Jpg/baseline/jpeg444.jpg"; public const string Jpeg420Small = "Jpg/baseline/jpeg420small.jpg"; public const string JpegRgb = "Jpg/baseline/jpeg-rgb.jpg"; + public const string Jpeg410 = "Jpg/baseline/jpeg410.jpg"; + public const string Jpeg411 = "Jpg/baseline/jpeg411.jpg"; + public const string Jpeg422 = "Jpg/baseline/jpeg422.jpg"; public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public const string Ratio1x1 = "Jpg/baseline/ratio-1x1.jpg"; diff --git a/tests/Images/Input/Jpg/baseline/jpeg410.jpg b/tests/Images/Input/Jpg/baseline/jpeg410.jpg new file mode 100644 index 0000000000..3bc41af8d8 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/jpeg410.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:318338c1a541227632a99cc47b04fc9f6d19c3e8300a76e71136edec2d17a5f5 +size 9073 diff --git a/tests/Images/Input/Jpg/baseline/jpeg411.jpg b/tests/Images/Input/Jpg/baseline/jpeg411.jpg new file mode 100644 index 0000000000..43a2ba49d8 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/jpeg411.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70315936e36bb1edf38fc950b7b321f20be5a2592db59bfd28d79dbc391a5aaf +size 4465 diff --git a/tests/Images/Input/Jpg/baseline/jpeg422.jpg b/tests/Images/Input/Jpg/baseline/jpeg422.jpg new file mode 100644 index 0000000000..4782b53b33 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/jpeg422.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be21207ecb96bcb0925706649678c522c68bf08ee26e6e8878456f4f7772dd31 +size 3951