@@ -89,6 +89,11 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
89
89
/// </summary>
90
90
private JFifMarker jFif ;
91
91
92
+ /// <summary>
93
+ /// Whether the image has a JFIF marker. This is needed to determine, if the colorspace is YCbCr.
94
+ /// </summary>
95
+ private bool hasJFif ;
96
+
92
97
/// <summary>
93
98
/// Contains information about the Adobe marker.
94
99
/// </summary>
@@ -488,27 +493,75 @@ private JpegColorSpace DeduceJpegColorSpace(byte componentCount)
488
493
489
494
if ( componentCount == 3 )
490
495
{
491
- if ( ! this . adobe . Equals ( default ) && this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformUnknown )
496
+ // We prioritize adobe marker over jfif marker, if somebody really encoded this image with redundant adobe marker,
497
+ // then it's most likely an adobe jfif image.
498
+ if ( ! this . adobe . Equals ( default ) )
492
499
{
493
- return JpegColorSpace . RGB ;
500
+ if ( this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformYCbCr )
501
+ {
502
+ return JpegColorSpace . YCbCr ;
503
+ }
504
+
505
+ if ( this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformUnknown )
506
+ {
507
+ return JpegColorSpace . RGB ;
508
+ }
509
+
510
+ // 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.
511
+ if ( this . Components [ 2 ] . Id == 3 && this . Components [ 1 ] . Id == 2 && this . Components [ 0 ] . Id == 1 )
512
+ {
513
+ return JpegColorSpace . YCbCr ;
514
+ }
515
+
516
+ JpegThrowHelper . ThrowNotSupportedColorSpace ( ) ;
517
+ }
518
+
519
+ if ( this . hasJFif )
520
+ {
521
+ // JFIF implies YCbCr.
522
+ return JpegColorSpace . YCbCr ;
494
523
}
495
524
525
+ // Fallback to the id color deduction.
496
526
// If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr.
527
+ // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
497
528
if ( this . Components [ 2 ] . Id == 66 && this . Components [ 1 ] . Id == 71 && this . Components [ 0 ] . Id == 82 )
498
529
{
499
530
return JpegColorSpace . RGB ;
500
531
}
501
532
533
+ // 3-channel non-subsampled images are assumed to be RGB.
534
+ if ( this . Components [ 2 ] . VerticalSamplingFactor == 1 && this . Components [ 1 ] . VerticalSamplingFactor == 1 && this . Components [ 0 ] . VerticalSamplingFactor == 1 &&
535
+ this . Components [ 2 ] . HorizontalSamplingFactor == 1 && this . Components [ 1 ] . HorizontalSamplingFactor == 1 && this . Components [ 0 ] . HorizontalSamplingFactor == 1 )
536
+ {
537
+ return JpegColorSpace . RGB ;
538
+ }
539
+
502
540
// Some images are poorly encoded and contain incorrect colorspace transform metadata.
503
541
// We ignore that and always fall back to the default colorspace.
504
542
return JpegColorSpace . YCbCr ;
505
543
}
506
544
507
545
if ( componentCount == 4 )
508
546
{
509
- return this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformYcck
510
- ? JpegColorSpace . Ycck
511
- : JpegColorSpace . Cmyk ;
547
+ // jfif images doesn't not support 4 component images, so we only check adobe.
548
+ if ( ! this . adobe . Equals ( default ) )
549
+ {
550
+ if ( this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformYcck )
551
+ {
552
+ return JpegColorSpace . Ycck ;
553
+ }
554
+
555
+ if ( this . adobe . ColorTransform == JpegConstants . Adobe . ColorTransformUnknown )
556
+ {
557
+ return JpegColorSpace . Cmyk ;
558
+ }
559
+
560
+ JpegThrowHelper . ThrowNotSupportedColorSpace ( ) ;
561
+ }
562
+
563
+ // Fallback to cmyk as neither of cmyk nor ycck have 'special' component ids.
564
+ return JpegColorSpace . Cmyk ;
512
565
}
513
566
514
567
JpegThrowHelper . ThrowNotSupportedComponentCount ( componentCount ) ;
@@ -675,6 +728,8 @@ private void ExtendProfile(ref byte[] profile, byte[] extension)
675
728
/// <param name="remaining">The remaining bytes in the segment block.</param>
676
729
private void ProcessApplicationHeaderMarker ( BufferedReadStream stream , int remaining )
677
730
{
731
+ this . hasJFif = true ;
732
+
678
733
// We can only decode JFif identifiers.
679
734
// Some images contain multiple JFIF markers (Issue 1932) so we check to see
680
735
// if it's already been read.
0 commit comments