Browse Source

Add additional YCbCr subsample rates

pull/1734/head
Brian Popow 5 years ago
parent
commit
b506e92479
  1. 35
      src/ImageSharp/Formats/Jpeg/JpegColorType.cs
  2. 29
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  3. 5
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  4. 23
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  5. 3
      tests/ImageSharp.Tests/TestImages.cs
  6. 3
      tests/Images/Input/Jpg/baseline/jpeg410.jpg
  7. 3
      tests/Images/Input/Jpg/baseline/jpeg411.jpg
  8. 3
      tests/Images/Input/Jpg/baseline/jpeg422.jpg

35
src/ImageSharp/Formats/Jpeg/JpegColorType.cs

@ -22,14 +22,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
YCbCrRatio444 = 1,
/// <summary>
/// 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.
/// </summary>
YCbCrRatio422 = 2,
/// <summary>
/// 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.
/// </summary>
YCbCrRatio411 = 3,
/// <summary>
/// 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.
/// </summary>
YCbCrRatio410 = 4,
/// <summary>
/// Single channel, luminance.
/// </summary>
Luminance = 2,
Luminance = 5,
/// <summary>
/// The pixel data will be preserved as RGB without any sub sampling.
/// </summary>
Rgb = 3,
Rgb = 6,
/// <summary>
/// CMYK colorspace (cyan, magenta, yellow, and key black) intended for printing.
///
/// Note: Not supported by the encoder.
/// </summary>
Cmyk = 7,
}
}

29
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;
}

5
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<TPixel>(TestImageProvider<TPixel> provider, JpegColorType expectedColorType)
where TPixel : unmanaged, IPixel<TPixel>
{

23
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<TPixel>(TestImageProvider<TPixel> provider)
public void Encode_WithUnsupportedColorType_FromInputImage_DefaultsToYCbCr420<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// 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<TPixel>(TestImageProvider<TPixel> provider, JpegColorType colorType)
where TPixel : unmanaged, IPixel<TPixel>
[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<TPixel>(TestImageProvider<TPixel> provider, JpegColorType colorType, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, colorType, quality);
[Theory]
[WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 73, 71, PixelTypes.Rgba32)]

3
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";

3
tests/Images/Input/Jpg/baseline/jpeg410.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:318338c1a541227632a99cc47b04fc9f6d19c3e8300a76e71136edec2d17a5f5
size 9073

3
tests/Images/Input/Jpg/baseline/jpeg411.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:70315936e36bb1edf38fc950b7b321f20be5a2592db59bfd28d79dbc391a5aaf
size 4465

3
tests/Images/Input/Jpg/baseline/jpeg422.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be21207ecb96bcb0925706649678c522c68bf08ee26e6e8878456f4f7772dd31
size 3951
Loading…
Cancel
Save