@ -90,6 +90,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
private JFifMarker jFif ;
/// <summary>
/// Whether the image has a JFIF marker. This is needed to determine, if the colorspace is YCbCr.
/// </summary>
private bool hasJFif ;
/// <summary>
/// Contains information about the Adobe marker.
/// </summary>
@ -514,17 +519,50 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if ( componentCount = = 3 )
{
if ( ! this . adobe . Equals ( default ) & & this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformUnknown )
// We prioritize adobe marker over jfif marker, if somebody really encoded this image with redundant adobe marker,
// then it's most likely an adobe jfif image.
if ( ! this . adobe . Equals ( default ) )
{
return JpegColorSpace . RGB ;
if ( this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformYCbCr )
{
return JpegColorSpace . YCbCr ;
}
if ( this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformUnknown )
{
return JpegColorSpace . RGB ;
}
// Fallback to the id color deduction: If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr.
if ( this . Components [ 2 ] . Id = = 3 & & this . Components [ 1 ] . Id = = 2 & & this . Components [ 0 ] . Id = = 1 )
{
return JpegColorSpace . YCbCr ;
}
JpegThrowHelper . ThrowNotSupportedColorSpace ( ) ;
}
if ( this . hasJFif )
{
// JFIF implies YCbCr.
return JpegColorSpace . YCbCr ;
}
// Fallback to the id color deduction.
// If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr.
// See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
if ( this . Components [ 2 ] . Id = = 6 6 & & this . Components [ 1 ] . Id = = 7 1 & & this . Components [ 0 ] . Id = = 8 2 )
{
return JpegColorSpace . RGB ;
}
// 3-channel non-subsampled images are assumed to be RGB.
if ( this . Components [ 2 ] . VerticalSamplingFactor = = 1 & & this . Components [ 1 ] . VerticalSamplingFactor = = 1 & & this . Components [ 0 ] . VerticalSamplingFactor = = 1 & &
this . Components [ 2 ] . HorizontalSamplingFactor = = 1 & & this . Components [ 1 ] . HorizontalSamplingFactor = = 1 & & this . Components [ 0 ] . HorizontalSamplingFactor = = 1 )
{
return JpegColorSpace . RGB ;
}
// Some images are poorly encoded and contain incorrect colorspace transform metadata.
// We ignore that and always fall back to the default colorspace.
return JpegColorSpace . YCbCr ;
@ -532,9 +570,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if ( componentCount = = 4 )
{
return this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformYcck
? JpegColorSpace . Ycck
: JpegColorSpace . Cmyk ;
// jfif images doesn't not support 4 component images, so we only check adobe.
if ( ! this . adobe . Equals ( default ) )
{
if ( this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformYcck )
{
return JpegColorSpace . Ycck ;
}
if ( this . adobe . ColorTransform = = JpegConstants . Adobe . ColorTransformUnknown )
{
return JpegColorSpace . Cmyk ;
}
JpegThrowHelper . ThrowNotSupportedColorSpace ( ) ;
}
// Fallback to cmyk as neither of cmyk nor ycck have 'special' component ids.
return JpegColorSpace . Cmyk ;
}
JpegThrowHelper . ThrowNotSupportedComponentCount ( componentCount ) ;
@ -701,6 +754,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApplicationHeaderMarker ( BufferedReadStream stream , int remaining )
{
this . hasJFif = true ;
// We can only decode JFif identifiers.
// Some images contain multiple JFIF markers (Issue 1932) so we check to see
// if it's already been read.