@@ -74,6 +74,30 @@ pub struct ImageInfo {
74
74
pub coding_process : CodingProcess ,
75
75
}
76
76
77
+ /// Describes the colour transform to apply before binary data is returned
78
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
79
+ #[ non_exhaustive]
80
+ pub enum ColorTransform {
81
+ /// No transform should be applied and the data is returned as-is.
82
+ None ,
83
+ /// Unknown colour transformation
84
+ Unknown ,
85
+ /// Grayscale transform should be applied (expects 1 channel)
86
+ Grayscale ,
87
+ /// RGB transform should be applied.
88
+ RGB ,
89
+ /// YCbCr transform should be applied.
90
+ YCbCr ,
91
+ /// CMYK transform should be applied.
92
+ CMYK ,
93
+ /// YCCK transform should be applied.
94
+ YCCK ,
95
+ /// big gamut Y/Cb/Cr, bg-sYCC
96
+ JcsBgYcc ,
97
+ /// big gamut red/green/blue, bg-sRGB
98
+ JcsBgRgb ,
99
+ }
100
+
77
101
/// JPEG decoder
78
102
pub struct Decoder < R > {
79
103
reader : R ,
@@ -84,7 +108,10 @@ pub struct Decoder<R> {
84
108
quantization_tables : [ Option < Arc < [ u16 ; 64 ] > > ; 4 ] ,
85
109
86
110
restart_interval : u16 ,
87
- color_transform : Option < AdobeColorTransform > ,
111
+
112
+ adobe_color_transform : Option < AdobeColorTransform > ,
113
+ color_transform : Option < ColorTransform > ,
114
+
88
115
is_jfif : bool ,
89
116
is_mjpeg : bool ,
90
117
@@ -111,6 +138,7 @@ impl<R: Read> Decoder<R> {
111
138
ac_huffman_tables : vec ! [ None , None , None , None ] ,
112
139
quantization_tables : [ None , None , None , None ] ,
113
140
restart_interval : 0 ,
141
+ adobe_color_transform : None ,
114
142
color_transform : None ,
115
143
is_jfif : false ,
116
144
is_mjpeg : false ,
@@ -122,6 +150,12 @@ impl<R: Read> Decoder<R> {
122
150
}
123
151
}
124
152
153
+ /// Colour transform to use when decoding the image. App segments relating to colour transforms
154
+ /// will be ignored.
155
+ pub fn set_color_transform ( & mut self , transform : ColorTransform ) {
156
+ self . color_transform = Some ( transform) ;
157
+ }
158
+
125
159
/// Set maximum buffer size allowed for decoded images
126
160
pub fn set_max_decoding_buffer_size ( & mut self , max : usize ) {
127
161
self . decoding_buffer_size_limit = max;
@@ -211,18 +245,15 @@ impl<R: Read> Decoder<R> {
211
245
} else {
212
246
PreferWorkerKind :: Immediate
213
247
}
214
- } ,
248
+ }
215
249
}
216
250
}
217
251
218
252
/// Tries to read metadata from the image without decoding it.
219
253
///
220
254
/// If successful, the metadata can be obtained using the `info` method.
221
255
pub fn read_info ( & mut self ) -> Result < ( ) > {
222
- WorkerScope :: with ( |worker| {
223
- self . decode_internal ( true , worker)
224
- } )
225
- . map ( |_| ( ) )
256
+ WorkerScope :: with ( |worker| self . decode_internal ( true , worker) ) . map ( |_| ( ) )
226
257
}
227
258
228
259
/// Configure the decoder to scale the image during decoding.
@@ -250,9 +281,7 @@ impl<R: Read> Decoder<R> {
250
281
251
282
/// Decodes the image and returns the decoded pixels if successful.
252
283
pub fn decode ( & mut self ) -> Result < Vec < u8 > > {
253
- WorkerScope :: with ( |worker| {
254
- self . decode_internal ( false , worker)
255
- } )
284
+ WorkerScope :: with ( |worker| self . decode_internal ( false , worker) )
256
285
}
257
286
258
287
fn decode_internal (
@@ -415,11 +444,13 @@ impl<R: Read> Decoder<R> {
415
444
}
416
445
}
417
446
418
- let preference = Self :: select_worker ( & frame, PreferWorkerKind :: Multithreaded ) ;
447
+ let preference =
448
+ Self :: select_worker ( & frame, PreferWorkerKind :: Multithreaded ) ;
419
449
420
- let ( marker, data) = worker_scope. get_or_init_worker (
421
- preference,
422
- |worker| self . decode_scan ( & frame, & scan, worker, & finished) ) ?;
450
+ let ( marker, data) = worker_scope
451
+ . get_or_init_worker ( preference, |worker| {
452
+ self . decode_scan ( & frame, & scan, worker, & finished)
453
+ } ) ?;
423
454
424
455
if let Some ( data) = data {
425
456
for ( i, plane) in data
@@ -492,7 +523,7 @@ impl<R: Read> Decoder<R> {
492
523
if let Some ( data) = parse_app ( & mut self . reader , marker) ? {
493
524
match data {
494
525
AppData :: Adobe ( color_transform) => {
495
- self . color_transform = Some ( color_transform)
526
+ self . adobe_color_transform = Some ( color_transform)
496
527
}
497
528
AppData :: Jfif => {
498
529
// From the JFIF spec:
@@ -566,10 +597,9 @@ impl<R: Read> Decoder<R> {
566
597
let frame = self . frame . as_ref ( ) . unwrap ( ) ;
567
598
let preference = Self :: select_worker ( & frame, PreferWorkerKind :: Multithreaded ) ;
568
599
569
- worker_scope. get_or_init_worker (
570
- preference,
571
- |worker| self . decode_planes ( worker, planes, planes_u16)
572
- )
600
+ worker_scope. get_or_init_worker ( preference, |worker| {
601
+ self . decode_planes ( worker, planes, planes_u16)
602
+ } )
573
603
}
574
604
575
605
fn decode_planes (
@@ -649,12 +679,79 @@ impl<R: Read> Decoder<R> {
649
679
& frame. components ,
650
680
planes,
651
681
frame. output_size ,
652
- self . is_jfif ,
653
- self . color_transform ,
682
+ self . determine_color_transform ( ) ,
654
683
)
655
684
}
656
685
}
657
686
687
+ fn determine_color_transform ( & self ) -> ColorTransform {
688
+ if let Some ( color_transform) = self . color_transform {
689
+ return color_transform;
690
+ }
691
+
692
+ let frame = self . frame . as_ref ( ) . unwrap ( ) ;
693
+
694
+ if frame. components . len ( ) == 1 {
695
+ return ColorTransform :: Grayscale ;
696
+ }
697
+
698
+ // Using logic for determining colour as described here: https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
699
+
700
+ if frame. components . len ( ) == 3 {
701
+ match (
702
+ frame. components [ 0 ] . identifier ,
703
+ frame. components [ 1 ] . identifier ,
704
+ frame. components [ 2 ] . identifier ,
705
+ ) {
706
+ ( 1 , 2 , 3 ) => {
707
+ return ColorTransform :: YCbCr ;
708
+ }
709
+ ( 1 , 34 , 35 ) => {
710
+ return ColorTransform :: JcsBgYcc ;
711
+ }
712
+ ( 82 , 71 , 66 ) => {
713
+ return ColorTransform :: RGB ;
714
+ }
715
+ ( 114 , 103 , 98 ) => {
716
+ return ColorTransform :: JcsBgRgb ;
717
+ }
718
+ _ => { }
719
+ }
720
+
721
+ if self . is_jfif {
722
+ return ColorTransform :: YCbCr ;
723
+ }
724
+ }
725
+
726
+ if let Some ( colour_transform) = self . adobe_color_transform {
727
+ match colour_transform {
728
+ AdobeColorTransform :: Unknown => {
729
+ if frame. components . len ( ) == 3 {
730
+ return ColorTransform :: RGB ;
731
+ } else if frame. components . len ( ) == 4 {
732
+ return ColorTransform :: CMYK ;
733
+ }
734
+ }
735
+ AdobeColorTransform :: YCbCr => {
736
+ return ColorTransform :: YCbCr ;
737
+ }
738
+ AdobeColorTransform :: YCCK => {
739
+ return ColorTransform :: YCCK ;
740
+ }
741
+ }
742
+ } else if frame. components . len ( ) == 4 {
743
+ return ColorTransform :: CMYK ;
744
+ }
745
+
746
+ if frame. components . len ( ) == 4 {
747
+ ColorTransform :: YCCK
748
+ } else if frame. components . len ( ) == 3 {
749
+ ColorTransform :: YCbCr
750
+ } else {
751
+ ColorTransform :: Unknown
752
+ }
753
+ }
754
+
658
755
fn read_marker ( & mut self ) -> Result < Marker > {
659
756
loop {
660
757
// This should be an error as the JPEG spec doesn't allow extraneous data between marker segments.
@@ -1190,8 +1287,7 @@ fn compute_image(
1190
1287
components : & [ Component ] ,
1191
1288
mut data : Vec < Vec < u8 > > ,
1192
1289
output_size : Dimensions ,
1193
- is_jfif : bool ,
1194
- color_transform : Option < AdobeColorTransform > ,
1290
+ color_transform : ColorTransform ,
1195
1291
) -> Result < Vec < u8 > > {
1196
1292
if data. is_empty ( ) || data. iter ( ) . any ( Vec :: is_empty) {
1197
1293
return Err ( Error :: Format ( "not all components have data" . to_owned ( ) ) ) ;
@@ -1221,36 +1317,58 @@ fn compute_image(
1221
1317
decoded. resize ( size, 0 ) ;
1222
1318
Ok ( decoded)
1223
1319
} else {
1224
- compute_image_parallel ( components, data, output_size, is_jfif , color_transform)
1320
+ compute_image_parallel ( components, data, output_size, color_transform)
1225
1321
}
1226
1322
}
1227
1323
1228
1324
pub ( crate ) fn choose_color_convert_func (
1229
1325
component_count : usize ,
1230
- _is_jfif : bool ,
1231
- color_transform : Option < AdobeColorTransform > ,
1326
+ color_transform : ColorTransform ,
1232
1327
) -> Result < fn ( & [ Vec < u8 > ] , & mut [ u8 ] ) > {
1233
1328
match component_count {
1234
- 3 => {
1235
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1236
- // Unknown means the data is RGB, so we don't need to perform any color conversion on it.
1237
- if color_transform == Some ( AdobeColorTransform :: Unknown ) {
1238
- Ok ( color_convert_line_rgb)
1239
- } else {
1240
- Ok ( color_convert_line_ycbcr)
1241
- }
1242
- }
1243
- 4 => {
1244
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1245
- match color_transform {
1246
- Some ( AdobeColorTransform :: Unknown ) => Ok ( color_convert_line_cmyk) ,
1247
- Some ( _) => Ok ( color_convert_line_ycck) ,
1248
- None => {
1249
- // Assume CMYK because no APP14 marker was found
1250
- Ok ( color_convert_line_cmyk)
1251
- }
1252
- }
1253
- }
1329
+ 3 => match color_transform {
1330
+ ColorTransform :: None => Ok ( color_no_convert) ,
1331
+ ColorTransform :: Grayscale => Err ( Error :: Format (
1332
+ "Invalid number of channels (3) for Grayscale data" . to_string ( ) ,
1333
+ ) ) ,
1334
+ ColorTransform :: RGB => Ok ( color_convert_line_rgb) ,
1335
+ ColorTransform :: YCbCr => Ok ( color_convert_line_ycbcr) ,
1336
+ ColorTransform :: CMYK => Err ( Error :: Format (
1337
+ "Invalid number of channels (3) for CMYK data" . to_string ( ) ,
1338
+ ) ) ,
1339
+ ColorTransform :: YCCK => Err ( Error :: Format (
1340
+ "Invalid number of channels (3) for YCCK data" . to_string ( ) ,
1341
+ ) ) ,
1342
+ ColorTransform :: JcsBgYcc => Err ( Error :: Unsupported (
1343
+ UnsupportedFeature :: ColorTransform ( ColorTransform :: JcsBgYcc ) ,
1344
+ ) ) ,
1345
+ ColorTransform :: JcsBgRgb => Err ( Error :: Unsupported (
1346
+ UnsupportedFeature :: ColorTransform ( ColorTransform :: JcsBgRgb ) ,
1347
+ ) ) ,
1348
+ ColorTransform :: Unknown => Err ( Error :: Format ( "Unknown colour transform" . to_string ( ) ) ) ,
1349
+ } ,
1350
+ 4 => match color_transform {
1351
+ ColorTransform :: None => Ok ( color_no_convert) ,
1352
+ ColorTransform :: Grayscale => Err ( Error :: Format (
1353
+ "Invalid number of channels (4) for Grayscale data" . to_string ( ) ,
1354
+ ) ) ,
1355
+ ColorTransform :: RGB => Err ( Error :: Format (
1356
+ "Invalid number of channels (4) for RGB data" . to_string ( ) ,
1357
+ ) ) ,
1358
+ ColorTransform :: YCbCr => Err ( Error :: Format (
1359
+ "Invalid number of channels (4) for YCbCr data" . to_string ( ) ,
1360
+ ) ) ,
1361
+ ColorTransform :: CMYK => Ok ( color_convert_line_cmyk) ,
1362
+ ColorTransform :: YCCK => Ok ( color_convert_line_ycck) ,
1363
+
1364
+ ColorTransform :: JcsBgYcc => Err ( Error :: Unsupported (
1365
+ UnsupportedFeature :: ColorTransform ( ColorTransform :: JcsBgYcc ) ,
1366
+ ) ) ,
1367
+ ColorTransform :: JcsBgRgb => Err ( Error :: Unsupported (
1368
+ UnsupportedFeature :: ColorTransform ( ColorTransform :: JcsBgRgb ) ,
1369
+ ) ) ,
1370
+ ColorTransform :: Unknown => Err ( Error :: Format ( "Unknown colour transform" . to_string ( ) ) ) ,
1371
+ } ,
1254
1372
_ => panic ! ( ) ,
1255
1373
}
1256
1374
}
@@ -1340,6 +1458,16 @@ fn color_convert_line_cmyk(data: &[Vec<u8>], output: &mut [u8]) {
1340
1458
}
1341
1459
}
1342
1460
1461
+ fn color_no_convert ( data : & [ Vec < u8 > ] , output : & mut [ u8 ] ) {
1462
+ let mut output_iter = output. iter_mut ( ) ;
1463
+
1464
+ for pixel in data {
1465
+ for d in pixel {
1466
+ * ( output_iter. next ( ) . unwrap ( ) ) = * d;
1467
+ }
1468
+ }
1469
+ }
1470
+
1343
1471
const FIXED_POINT_OFFSET : i32 = 20 ;
1344
1472
const HALF : i32 = ( 1 << FIXED_POINT_OFFSET ) / 2 ;
1345
1473
0 commit comments