From 0f45698393062fe7efd501bcc88f962eb37f61c9 Mon Sep 17 00:00:00 2001 From: Maria Pospelova Date: Tue, 7 Apr 2026 09:23:32 +0000 Subject: [PATCH 1/3] Use pure integer arithmetic for CMYK to RGB conversion in TIFF --- src/codecs/tiff.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/codecs/tiff.rs b/src/codecs/tiff.rs index d945619cae..9c76f77f29 100644 --- a/src/codecs/tiff.rs +++ b/src/codecs/tiff.rs @@ -553,26 +553,30 @@ fn ycbcr_to_rgb8(ycbcr: &[[u8; 3]], lr: f32, lg: f32, lb: f32, out: &mut [[u8; 3 } fn cmyk_to_rgb(cmyk: &[u8; 4]) -> [u8; 3] { - let c = f32::from(cmyk[0]); - let m = f32::from(cmyk[1]); - let y = f32::from(cmyk[2]); - let kf = 1. - f32::from(cmyk[3]) / 255.; + let c = cmyk[0] as u32; + let m = cmyk[1] as u32; + let y = cmyk[2] as u32; + let k = cmyk[3] as u32; + + let k_inv = 255 - k; [ - ((255. - c) * kf) as u8, - ((255. - m) * kf) as u8, - ((255. - y) * kf) as u8, + (((255 - c) * k_inv) / 255) as u8, + (((255 - m) * k_inv) / 255) as u8, + (((255 - y) * k_inv) / 255) as u8, ] } fn cmyk_to_rgb16(cmyk: &[u16; 4]) -> [u16; 3] { - let c = f32::from(cmyk[0]); - let m = f32::from(cmyk[1]); - let y = f32::from(cmyk[2]); - let kf = 1. - f32::from(cmyk[3]) / 65535.; + let c = cmyk[0] as u64; + let m = cmyk[1] as u64; + let y = cmyk[2] as u64; + let k = cmyk[3] as u64; + + let k_inv = 65535 - k; [ - ((65535. - c) * kf) as u16, - ((65535. - m) * kf) as u16, - ((65535. - y) * kf) as u16, + (((65535 - c) * k_inv) / 65535) as u16, + (((65535 - m) * k_inv) / 65535) as u16, + (((65535 - y) * k_inv) / 65535) as u16, ] } From 9d2bcf38e3920a307034dd83d593bb77eae36466 Mon Sep 17 00:00:00 2001 From: Maria Pospelova Date: Tue, 7 Apr 2026 11:57:47 +0000 Subject: [PATCH 2/3] Add testcase with edge case where integer arithmetic performs a more accurate conversion --- tests/conversions.rs | 17 +++++++++++++++++ .../tiff/testsuite/cmyk_u8_edge_case.tif | Bin 0 -> 122 bytes 2 files changed, 17 insertions(+) create mode 100644 tests/images/tiff/testsuite/cmyk_u8_edge_case.tif diff --git a/tests/conversions.rs b/tests/conversions.rs index 56c3dd2943..af1164dd57 100644 --- a/tests/conversions.rs +++ b/tests/conversions.rs @@ -103,3 +103,20 @@ fn test_decode_8bit_ycbcr_lzw_invalid_coefficients() { let result = TiffDecoder::new(std::io::Cursor::new(data)); assert!(result.is_err()); } + +#[cfg(feature = "tiff")] +#[test] +fn test_decode_8bit_cmyk() -> Result<(), image::ImageError> { + let img_path = PathBuf::from("tests/images/tiff/testsuite/cmyk_u8_edge_case.tif"); + let data = fs::read(img_path).expect("Test image missing"); + + let tiff_decoder = TiffDecoder::new(std::io::Cursor::new(data))?; + + assert_eq!(tiff_decoder.color_type(), image::ColorType::Rgb8); + + let mut buffer = vec![0u8; tiff_decoder.total_bytes() as usize]; + tiff_decoder.read_image(&mut buffer)?; + assert_eq!(buffer, vec![190, 190, 190]); + + Ok(()) +} diff --git a/tests/images/tiff/testsuite/cmyk_u8_edge_case.tif b/tests/images/tiff/testsuite/cmyk_u8_edge_case.tif new file mode 100644 index 0000000000000000000000000000000000000000..86827d26dfea56c1bec9b5e5884955d729255f2c GIT binary patch literal 122 zcmebD)MDUZU;tr876wKj3yhK2OpMG7EI{!*AZ7!yLGr9XEC^Lo24ssu#X;)Cko1Z} O*&uNc;DACP(-8o_v;wXG literal 0 HcmV?d00001 From cd73e3f832fc3fb764eb101a65464e939f4ad147 Mon Sep 17 00:00:00 2001 From: Maria Pospelova Date: Tue, 7 Apr 2026 14:30:52 +0000 Subject: [PATCH 3/3] Add test case for truncation --- tests/conversions.rs | 17 +++++++++++++++++ .../tiff/testsuite/cmyk_u8_trunc_case.tif | Bin 0 -> 122 bytes 2 files changed, 17 insertions(+) create mode 100644 tests/images/tiff/testsuite/cmyk_u8_trunc_case.tif diff --git a/tests/conversions.rs b/tests/conversions.rs index af1164dd57..c687aaed96 100644 --- a/tests/conversions.rs +++ b/tests/conversions.rs @@ -120,3 +120,20 @@ fn test_decode_8bit_cmyk() -> Result<(), image::ImageError> { Ok(()) } + +#[cfg(feature = "tiff")] +#[test] +fn test_decode_8bit_cmyk_truncation() -> Result<(), image::ImageError> { + let img_path = PathBuf::from("tests/images/tiff/testsuite/cmyk_u8_trunc_case.tif"); + let data = fs::read(img_path).expect("Test image missing"); + + let tiff_decoder = TiffDecoder::new(std::io::Cursor::new(data))?; + + assert_eq!(tiff_decoder.color_type(), image::ColorType::Rgb8); + + let mut buffer = vec![0u8; tiff_decoder.total_bytes() as usize]; + tiff_decoder.read_image(&mut buffer)?; + assert_eq!(buffer, vec![126, 126, 126]); + + Ok(()) +} diff --git a/tests/images/tiff/testsuite/cmyk_u8_trunc_case.tif b/tests/images/tiff/testsuite/cmyk_u8_trunc_case.tif new file mode 100644 index 0000000000000000000000000000000000000000..4036e10110a83b82264d299b88a50e5f21bc99ae GIT binary patch literal 122 zcmebD)MDUZU;tr876wKj3yhK2OpMG7EI{!*AZ7!yLGr9XEC^Lo24ssu#X;)Cko1Z} P*&uNc;DAC#M#csJzx)F5 literal 0 HcmV?d00001