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