Skip to content

Commit 201a9ca

Browse files
committed
Implemented colour transform selection logic based on libjpeg
1 parent a9f368a commit 201a9ca

File tree

2 files changed

+128
-74
lines changed

2 files changed

+128
-74
lines changed

src/decoder.rs

Lines changed: 126 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ pub struct ImageInfo {
7575
}
7676

7777
/// Describes the colour transform to apply before binary data is returned
78-
#[derive(Debug, Clone, Copy)]
78+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7979
#[non_exhaustive]
8080
pub enum ColorTransform {
8181
/// No transform should be applied and the data is returned as-is.
8282
None,
83-
/// Default colour transform should be applied. If there are 3 channels then YCbCr and if there are 4 then YCCK.
84-
Default,
83+
/// Unknown colour transformation
84+
Unknown,
85+
/// Grayscale transform should be applied (expects 1 channel)
86+
Grayscale,
8587
/// RGB transform should be applied.
8688
RGB,
8789
/// YCbCr transform should be applied.
@@ -90,14 +92,10 @@ pub enum ColorTransform {
9092
CMYK,
9193
/// YCCK transform should be applied.
9294
YCCK,
93-
/// Use the colour transform specified in the Adobe extended tag
94-
AdobeColorTransform(AdobeColorTransform),
95-
}
96-
97-
impl Default for ColorTransform {
98-
fn default() -> Self {
99-
ColorTransform::Default
100-
}
95+
/// big gamut Y/Cb/Cr, bg-sYCC
96+
JcsBgYcc,
97+
/// big gamut red/green/blue, bg-sRGB
98+
JcsBgRgb,
10199
}
102100

103101
/// JPEG decoder
@@ -110,8 +108,10 @@ pub struct Decoder<R> {
110108
quantization_tables: [Option<Arc<[u16; 64]>>; 4],
111109

112110
restart_interval: u16,
113-
fallback_color_transform: ColorTransform,
111+
112+
adobe_color_transform: Option<AdobeColorTransform>,
114113
color_transform: Option<ColorTransform>,
114+
115115
is_jfif: bool,
116116
is_mjpeg: bool,
117117

@@ -138,8 +138,7 @@ impl<R: Read> Decoder<R> {
138138
ac_huffman_tables: vec![None, None, None, None],
139139
quantization_tables: [None, None, None, None],
140140
restart_interval: 0,
141-
// Set the fallback transform as unknown. This can be overriden using `set_color_transform`
142-
fallback_color_transform: ColorTransform::default(),
141+
adobe_color_transform: None,
143142
color_transform: None,
144143
is_jfif: false,
145144
is_mjpeg: false,
@@ -151,11 +150,6 @@ impl<R: Read> Decoder<R> {
151150
}
152151
}
153152

154-
/// Colour transform to use if one is not set using `set_color_transform` or found in app segments.
155-
pub fn set_fallback_color_transform(&mut self, transform: ColorTransform) {
156-
self.fallback_color_transform = transform;
157-
}
158-
159153
/// Colour transform to use when decoding the image. App segments relating to colour transforms
160154
/// will be ignored.
161155
pub fn set_color_transform(&mut self, transform: ColorTransform) {
@@ -529,12 +523,7 @@ impl<R: Read> Decoder<R> {
529523
if let Some(data) = parse_app(&mut self.reader, marker)? {
530524
match data {
531525
AppData::Adobe(color_transform) => {
532-
// Set the colour transform if it has not already been set by the user
533-
// calling `set_color_transform`
534-
if self.color_transform.is_none() {
535-
self.color_transform =
536-
Some(ColorTransform::AdobeColorTransform(color_transform))
537-
}
526+
self.adobe_color_transform = Some(color_transform)
538527
}
539528
AppData::Jfif => {
540529
// From the JFIF spec:
@@ -687,21 +676,85 @@ impl<R: Read> Decoder<R> {
687676
compute_image_lossless(frame, planes_u16)
688677
} else {
689678
// Check whether a colour transform has been set - if not use the fallback
690-
let color_transform = match self.color_transform {
691-
Some(color_transform) => color_transform,
692-
None => self.fallback_color_transform,
693-
};
679+
// let color_transform = match self.color_transform {
680+
// Some(color_transform) => color_transform,
681+
// None => self.fallback_color_transform,
682+
// };
694683

695684
compute_image(
696685
&frame.components,
697686
planes,
698687
frame.output_size,
699688
self.is_jfif,
700-
color_transform,
689+
self.determine_color_transform(),
701690
)
702691
}
703692
}
704693

694+
fn determine_color_transform(&self) -> ColorTransform {
695+
if let Some(color_transform) = self.color_transform {
696+
return color_transform;
697+
}
698+
699+
let frame = self.frame.as_ref().unwrap();
700+
701+
if frame.components.len() == 1 {
702+
return ColorTransform::Grayscale;
703+
}
704+
705+
// Using logic for determining colour as described here: https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
706+
707+
if frame.components.len() == 3 {
708+
match (
709+
frame.components[0].identifier,
710+
frame.components[1].identifier,
711+
frame.components[2].identifier,
712+
) {
713+
(1, 2, 3) => {
714+
return ColorTransform::YCbCr;
715+
}
716+
(1, 34, 35) => {
717+
return ColorTransform::JcsBgYcc;
718+
}
719+
(82, 71, 66) => {
720+
return ColorTransform::RGB;
721+
}
722+
(114, 103, 98) => {
723+
return ColorTransform::JcsBgRgb;
724+
}
725+
_ => {}
726+
}
727+
728+
if self.is_jfif {
729+
return ColorTransform::YCbCr;
730+
}
731+
}
732+
733+
if let Some(colour_transform) = self.adobe_color_transform {
734+
match colour_transform {
735+
AdobeColorTransform::Unknown => {
736+
if frame.components.len() == 3 {
737+
return ColorTransform::RGB;
738+
} else if frame.components.len() == 4 {
739+
return ColorTransform::CMYK;
740+
}
741+
}
742+
AdobeColorTransform::YCbCr => {
743+
return ColorTransform::YCbCr;
744+
}
745+
AdobeColorTransform::YCCK => {
746+
return ColorTransform::YCCK;
747+
}
748+
}
749+
}
750+
751+
if frame.components.len() == 4 {
752+
ColorTransform::YCCK
753+
} else {
754+
ColorTransform::Unknown
755+
}
756+
}
757+
705758
fn read_marker(&mut self) -> Result<Marker> {
706759
loop {
707760
// This should be an error as the JPEG spec doesn't allow extraneous data between marker segments.
@@ -1278,50 +1331,49 @@ pub(crate) fn choose_color_convert_func(
12781331
color_transform: ColorTransform,
12791332
) -> Result<fn(&[Vec<u8>], &mut [u8])> {
12801333
match component_count {
1281-
3 => {
1282-
match color_transform {
1283-
ColorTransform::None => Ok(color_no_convert),
1284-
ColorTransform::Default => Ok(color_convert_line_ycbcr),
1285-
ColorTransform::RGB => Ok(color_convert_line_rgb),
1286-
ColorTransform::YCbCr => Ok(color_convert_line_ycbcr),
1287-
ColorTransform::CMYK => Err(Error::Format(
1288-
"Invalid number of channels (3) for CMYK data".to_string(),
1289-
)),
1290-
ColorTransform::YCCK => Err(Error::Format(
1291-
"Invalid number of channels (3) for YCCK data".to_string(),
1292-
)),
1293-
ColorTransform::AdobeColorTransform(adobe_transform) => {
1294-
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1295-
// Unknown means the data is RGB, so we don't need to perform any color conversion on it.
1296-
if adobe_transform == AdobeColorTransform::Unknown {
1297-
Ok(color_convert_line_rgb)
1298-
} else {
1299-
Ok(color_convert_line_ycbcr)
1300-
}
1301-
}
1302-
}
1303-
}
1304-
4 => {
1305-
match color_transform {
1306-
ColorTransform::None => Ok(color_no_convert),
1307-
ColorTransform::Default => Ok(color_convert_line_cmyk),
1308-
ColorTransform::RGB => Err(Error::Format(
1309-
"Invalid number of channels (4) for RGB data".to_string(),
1310-
)),
1311-
ColorTransform::YCbCr => Err(Error::Format(
1312-
"Invalid number of channels (4) for YCbCr data".to_string(),
1313-
)),
1314-
ColorTransform::CMYK => Ok(color_convert_line_cmyk),
1315-
ColorTransform::YCCK => Ok(color_convert_line_ycck),
1316-
ColorTransform::AdobeColorTransform(adobe_transform) => {
1317-
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1318-
match adobe_transform {
1319-
AdobeColorTransform::Unknown => Ok(color_convert_line_cmyk),
1320-
_ => Ok(color_convert_line_ycck),
1321-
}
1322-
}
1323-
}
1324-
}
1334+
3 => match color_transform {
1335+
ColorTransform::None => Ok(color_no_convert),
1336+
ColorTransform::RGB => Ok(color_convert_line_rgb),
1337+
ColorTransform::YCbCr => Ok(color_convert_line_ycbcr),
1338+
ColorTransform::CMYK => Err(Error::Format(
1339+
"Invalid number of channels (3) for CMYK data".to_string(),
1340+
)),
1341+
ColorTransform::YCCK => Err(Error::Format(
1342+
"Invalid number of channels (3) for YCCK data".to_string(),
1343+
)),
1344+
ColorTransform::JcsBgYcc => Err(Error::Unsupported(
1345+
UnsupportedFeature::ColorTransform(ColorTransform::JcsBgYcc),
1346+
)),
1347+
ColorTransform::JcsBgRgb => Err(Error::Unsupported(
1348+
UnsupportedFeature::ColorTransform(ColorTransform::JcsBgRgb),
1349+
)),
1350+
ColorTransform::Unknown => Err(Error::Format("Unknown colour transform".to_string())),
1351+
ColorTransform::Grayscale => Err(Error::Unsupported(
1352+
UnsupportedFeature::ColorTransform(ColorTransform::Grayscale),
1353+
)),
1354+
},
1355+
4 => match color_transform {
1356+
ColorTransform::None => Ok(color_no_convert),
1357+
ColorTransform::RGB => Err(Error::Format(
1358+
"Invalid number of channels (4) for RGB data".to_string(),
1359+
)),
1360+
ColorTransform::YCbCr => Err(Error::Format(
1361+
"Invalid number of channels (4) for YCbCr data".to_string(),
1362+
)),
1363+
ColorTransform::CMYK => Ok(color_convert_line_cmyk),
1364+
ColorTransform::YCCK => Ok(color_convert_line_ycck),
1365+
1366+
ColorTransform::JcsBgYcc => Err(Error::Unsupported(
1367+
UnsupportedFeature::ColorTransform(ColorTransform::JcsBgYcc),
1368+
)),
1369+
ColorTransform::JcsBgRgb => Err(Error::Unsupported(
1370+
UnsupportedFeature::ColorTransform(ColorTransform::JcsBgRgb),
1371+
)),
1372+
ColorTransform::Unknown => Err(Error::Format("Unknown colour transform".to_string())),
1373+
ColorTransform::Grayscale => Err(Error::Unsupported(
1374+
UnsupportedFeature::ColorTransform(ColorTransform::Grayscale),
1375+
)),
1376+
},
13251377
_ => panic!(),
13261378
}
13271379
}

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub enum UnsupportedFeature {
2727
SubsamplingRatio,
2828
/// A subsampling ratio not representable as an integer.
2929
NonIntegerSubsamplingRatio,
30+
/// Colour transform
31+
ColorTransform(ColorTransform),
3032
}
3133

3234
/// Errors that can occur while decoding a JPEG image.

0 commit comments

Comments
 (0)