mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-14 22:42:46 +00:00
Preserve unmarked CMYK JPEG samples
This commit is contained in:
1 parent
d15bf1f35b
commit
c2a69bbdc6
2 files changed
+30
-4
No files matched your search
@@ -37,6 +37,8 @@ pub(crate) struct JpegMeta {
|
||||
pub dpi: Option<(f32, f32)>,
|
||||
/// The APP13 Photoshop image resource blocks (`8BIM` ...), segments joined, without the header.
|
||||
pub photoshop: Option<Vec<u8>>,
|
||||
/// Whether an Adobe APP14 marker identifies the four-component samples as inverted CMYK.
|
||||
pub adobe_marker: bool,
|
||||
pub adobe_transform: Option<u8>,
|
||||
/// (width, height, components) from the first SOFn.
|
||||
pub frame: Option<(u32, u32, u8)>,
|
||||
@@ -148,8 +150,11 @@ pub(crate) fn scan_metadata(b: &[u8]) -> JpegMeta {
|
||||
m.frame = Some((w, h, seg[5]));
|
||||
sof = Some((marker, seg));
|
||||
}
|
||||
0xEE if seg.len() >= 12 && seg.starts_with(b"Adobe") => {
|
||||
m.adobe_transform = Some(seg[11]);
|
||||
0xEE if seg.starts_with(b"Adobe") => {
|
||||
m.adobe_marker = true;
|
||||
if seg.len() >= 12 {
|
||||
m.adobe_transform = Some(seg[11]);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -398,8 +403,9 @@ pub(crate) fn decode(bytes: &[u8], limits: &Limits) -> Result<Image, CodecError>
|
||||
}
|
||||
px.truncate(expected);
|
||||
match input {
|
||||
// Adobe-style CMYK is stored inverted (255 = no ink).
|
||||
ColorSpace::CMYK => px.iter_mut().for_each(|v| *v = 255 - *v),
|
||||
// Adobe-style CMYK is stored inverted (255 = no ink). Four-component JPEGs without
|
||||
// APP14 are also encountered with their CMYK samples stored as ink values directly.
|
||||
ColorSpace::CMYK if meta.adobe_marker => px.iter_mut().for_each(|v| *v = 255 - *v),
|
||||
ColorSpace::YCCK => {
|
||||
for p in px.as_chunks_mut::<4>().0 {
|
||||
let (y, cb, cr) = (p[0] as f32, p[1] as f32 - 128.0, p[2] as f32 - 128.0);
|
||||
|
||||
@@ -115,6 +115,26 @@ fn jpeg_cmyk_roundtrip_with_adobe_marker() {
|
||||
assert!(psnr(&img, &back) > 35.0, "{}", psnr(&img, &back));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jpeg_cmyk_without_adobe_marker_keeps_samples_as_inks() {
|
||||
let img = Image::from_u8(16, 16, ChannelLayout::Cmyk, [10u8, 100, 200, 50].repeat(256)).unwrap();
|
||||
let marked = encode(&img, Format::Jpeg, &EncodeOptions { jpeg_quality: 95, ..Default::default() }).unwrap();
|
||||
let signature = b"\xFF\xEE\x00\x0EAdobe";
|
||||
let start = marked.windows(signature.len()).position(|part| part == signature).expect("Adobe APP14 marker");
|
||||
// APP14's two marker bytes and two-byte length precede its 12-byte payload.
|
||||
let mut unmarked = Vec::with_capacity(marked.len() - 16);
|
||||
unmarked.extend_from_slice(&marked[..start]);
|
||||
unmarked.extend_from_slice(&marked[start + 16..]);
|
||||
|
||||
let adobe_pixels = decode(&marked).unwrap().data().to_vec();
|
||||
let unmarked_pixels = decode(&unmarked).unwrap().data().to_vec();
|
||||
for (adobe, raw) in adobe_pixels.as_chunks::<4>().0.iter().zip(unmarked_pixels.as_chunks::<4>().0) {
|
||||
for (&ink, &stored) in adobe.iter().zip(raw) {
|
||||
assert_eq!(stored, 255 - ink, "unmarked CMYK samples are direct inks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jpeg_ycck_decodes_to_cmyk() {
|
||||
let img = synth(40, 32, ChannelLayout::Cmyk, SampleType::U8, 4, 0.02);
|
||||
|
||||
Reference in new issue
Block a user