Skip to content

Commit 22ad950

Browse files
committed
Added explicit colour transform and ability to set manually
1 parent 21a7a3b commit 22ad950

File tree

3 files changed

+90
-38
lines changed

3 files changed

+90
-38
lines changed

src/decoder.rs

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ pub struct ImageInfo {
7474
pub coding_process: CodingProcess,
7575
}
7676

77+
/// Describes the colour transform to apply before binary data is returned
78+
#[derive(Debug, Clone, Copy)]
79+
pub enum ColorTransform {
80+
/// No transform should be applied and the data is returned as-is.
81+
None,
82+
/// Default colour transform should be applied. If there are 3 channels then YCbCr and if there are 4 then YCCK.
83+
Default,
84+
/// RGB transform should be applied.
85+
RGB,
86+
/// YCbCr transform should be applied.
87+
YCbCr,
88+
/// CMYK transform should be applied.
89+
CMYK,
90+
/// YCCK transform should be applied.
91+
YCCK,
92+
/// Use the colour transform specified in the Adobe extended tag
93+
AdobeColorTransform(AdobeColorTransform),
94+
}
95+
96+
impl Default for ColorTransform {
97+
fn default() -> Self {
98+
ColorTransform::Default
99+
}
100+
}
101+
77102
/// JPEG decoder
78103
pub struct Decoder<R> {
79104
reader: R,
@@ -84,7 +109,7 @@ pub struct Decoder<R> {
84109
quantization_tables: [Option<Arc<[u16; 64]>>; 4],
85110

86111
restart_interval: u16,
87-
color_transform: Option<AdobeColorTransform>,
112+
color_transform: ColorTransform,
88113
is_jfif: bool,
89114
is_mjpeg: bool,
90115

@@ -111,7 +136,8 @@ impl<R: Read> Decoder<R> {
111136
ac_huffman_tables: vec![None, None, None, None],
112137
quantization_tables: [None, None, None, None],
113138
restart_interval: 0,
114-
color_transform: None,
139+
// Set the default transform as unknown. This can be overriden using `set_color_transform`
140+
color_transform: ColorTransform::default(),
115141
is_jfif: false,
116142
is_mjpeg: false,
117143
icc_markers: Vec::new(),
@@ -122,6 +148,11 @@ impl<R: Read> Decoder<R> {
122148
}
123149
}
124150

151+
/// Sets the colour transform to be applied before returning the data.
152+
pub fn set_color_transform(&mut self, transform: ColorTransform) {
153+
self.color_transform = transform;
154+
}
155+
125156
/// Set maximum buffer size allowed for decoded images
126157
pub fn set_max_decoding_buffer_size(&mut self, max: usize) {
127158
self.decoding_buffer_size_limit = max;
@@ -489,7 +520,8 @@ impl<R: Read> Decoder<R> {
489520
if let Some(data) = parse_app(&mut self.reader, marker)? {
490521
match data {
491522
AppData::Adobe(color_transform) => {
492-
self.color_transform = Some(color_transform)
523+
self.color_transform =
524+
ColorTransform::AdobeColorTransform(color_transform)
493525
}
494526
AppData::Jfif => {
495527
// From the JFIF spec:
@@ -1187,7 +1219,7 @@ fn compute_image(
11871219
mut data: Vec<Vec<u8>>,
11881220
output_size: Dimensions,
11891221
is_jfif: bool,
1190-
color_transform: Option<AdobeColorTransform>,
1222+
color_transform: ColorTransform,
11911223
) -> Result<Vec<u8>> {
11921224
if data.is_empty() || data.iter().any(Vec::is_empty) {
11931225
return Err(Error::Format("not all components have data".to_owned()));
@@ -1224,28 +1256,50 @@ fn compute_image(
12241256
pub(crate) fn choose_color_convert_func(
12251257
component_count: usize,
12261258
_is_jfif: bool,
1227-
color_transform: Option<AdobeColorTransform>,
1259+
color_transform: ColorTransform,
12281260
) -> Result<fn(&[Vec<u8>], &mut [u8])> {
12291261
match component_count {
12301262
3 => {
1231-
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1232-
// Unknown means the data is RGB, so we don't need to perform any color conversion on it.
1233-
if color_transform == Some(AdobeColorTransform::Unknown) {
1234-
Ok(color_convert_line_rgb)
1235-
} else if color_transform.is_none() {
1236-
Ok(color_no_convert)
1237-
} else {
1238-
Ok(color_convert_line_ycbcr)
1263+
match color_transform {
1264+
ColorTransform::None => Ok(color_no_convert),
1265+
ColorTransform::Default => Ok(color_convert_line_ycbcr),
1266+
ColorTransform::RGB => Ok(color_convert_line_rgb),
1267+
ColorTransform::YCbCr => Ok(color_convert_line_ycbcr),
1268+
ColorTransform::CMYK => Err(Error::Format(
1269+
"Invalid number of channels (3) for CMYK data".to_string(),
1270+
)),
1271+
ColorTransform::YCCK => Err(Error::Format(
1272+
"Invalid number of channels (3) for YCCK data".to_string(),
1273+
)),
1274+
ColorTransform::AdobeColorTransform(adobe_transform) => {
1275+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1276+
// Unknown means the data is RGB, so we don't need to perform any color conversion on it.
1277+
if adobe_transform == AdobeColorTransform::Unknown {
1278+
Ok(color_convert_line_rgb)
1279+
} else {
1280+
Ok(color_convert_line_ycbcr)
1281+
}
1282+
}
12391283
}
12401284
}
12411285
4 => {
1242-
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
12431286
match color_transform {
1244-
Some(AdobeColorTransform::Unknown) => Ok(color_convert_line_cmyk),
1245-
Some(_) => Ok(color_convert_line_ycck),
1246-
None => {
1247-
// Assume CMYK because no APP14 marker was found
1248-
Ok(color_no_convert)
1287+
ColorTransform::None => Ok(color_no_convert),
1288+
ColorTransform::Default => Ok(color_convert_line_cmyk),
1289+
ColorTransform::RGB => Err(Error::Format(
1290+
"Invalid number of channels (4) for RGB data".to_string(),
1291+
)),
1292+
ColorTransform::YCbCr => Err(Error::Format(
1293+
"Invalid number of channels (4) for YCbCr data".to_string(),
1294+
)),
1295+
ColorTransform::CMYK => Ok(color_convert_line_cmyk),
1296+
ColorTransform::YCCK => Ok(color_convert_line_ycck),
1297+
ColorTransform::AdobeColorTransform(adobe_transform) => {
1298+
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
1299+
match adobe_transform {
1300+
AdobeColorTransform::Unknown => Ok(color_convert_line_cmyk),
1301+
_ => Ok(color_convert_line_ycck),
1302+
}
12491303
}
12501304
}
12511305
}

src/worker/mod.rs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ mod multithreaded;
66
))]
77
mod rayon;
88

9-
use crate::decoder::choose_color_convert_func;
9+
use crate::decoder::{choose_color_convert_func, ColorTransform};
1010
use crate::error::Result;
11-
use crate::parser::{AdobeColorTransform, Component, Dimensions};
11+
use crate::parser::{Component, Dimensions};
1212
use crate::upsampler::Upsampler;
1313

14-
use core::cell::RefCell;
1514
use alloc::sync::Arc;
1615
use alloc::vec::Vec;
16+
use core::cell::RefCell;
1717

1818
pub struct RowData {
1919
pub index: usize,
@@ -66,21 +66,19 @@ impl WorkerScope {
6666
pub fn get_or_init_worker<T>(
6767
&self,
6868
prefer: PreferWorkerKind,
69-
f: impl FnOnce(&mut dyn Worker) -> T
69+
f: impl FnOnce(&mut dyn Worker) -> T,
7070
) -> T {
7171
let mut inner = self.inner.borrow_mut();
72-
let inner = inner.get_or_insert_with(move || {
73-
match prefer {
74-
#[cfg(all(
75-
not(any(target_arch = "asmjs", target_arch = "wasm32")),
76-
feature = "rayon"
77-
))]
78-
PreferWorkerKind::Multithreaded => WorkerScopeInner::Rayon(Default::default()),
79-
#[allow(unreachable_patterns)]
80-
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
81-
PreferWorkerKind::Multithreaded => WorkerScopeInner::Multithreaded(Default::default()),
82-
_ => WorkerScopeInner::Immediate(Default::default()),
83-
}
72+
let inner = inner.get_or_insert_with(move || match prefer {
73+
#[cfg(all(
74+
not(any(target_arch = "asmjs", target_arch = "wasm32")),
75+
feature = "rayon"
76+
))]
77+
PreferWorkerKind::Multithreaded => WorkerScopeInner::Rayon(Default::default()),
78+
#[allow(unreachable_patterns)]
79+
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
80+
PreferWorkerKind::Multithreaded => WorkerScopeInner::Multithreaded(Default::default()),
81+
_ => WorkerScopeInner::Immediate(Default::default()),
8482
});
8583

8684
f(match &mut *inner {
@@ -101,7 +99,7 @@ pub fn compute_image_parallel(
10199
data: Vec<Vec<u8>>,
102100
output_size: Dimensions,
103101
is_jfif: bool,
104-
color_transform: Option<AdobeColorTransform>,
102+
color_transform: ColorTransform,
105103
) -> Result<Vec<u8>> {
106104
#[cfg(all(
107105
not(any(target_arch = "asmjs", target_arch = "wasm32")),

src/worker/rayon.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::convert::TryInto;
33
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
44
use rayon::slice::ParallelSliceMut;
55

6-
use crate::decoder::choose_color_convert_func;
6+
use crate::decoder::{choose_color_convert_func, ColorTransform};
77
use crate::error::Result;
88
use crate::idct::dequantize_and_idct_block;
99
use crate::parser::{AdobeColorTransform, Component};
@@ -197,7 +197,7 @@ pub fn compute_image_parallel(
197197
data: Vec<Vec<u8>>,
198198
output_size: Dimensions,
199199
is_jfif: bool,
200-
color_transform: Option<AdobeColorTransform>,
200+
color_transform: ColorTransform,
201201
) -> Result<Vec<u8>> {
202202
let color_convert_func = choose_color_convert_func(components.len(), is_jfif, color_transform)?;
203203
let upsampler = Upsampler::new(components, output_size.width, output_size.height)?;

0 commit comments

Comments
 (0)