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, ] } diff --git a/tests/conversions.rs b/tests/conversions.rs index 56c3dd2943..c687aaed96 100644 --- a/tests/conversions.rs +++ b/tests/conversions.rs @@ -103,3 +103,37 @@ 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(()) +} + +#[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_edge_case.tif b/tests/images/tiff/testsuite/cmyk_u8_edge_case.tif new file mode 100644 index 0000000000..86827d26df Binary files /dev/null and b/tests/images/tiff/testsuite/cmyk_u8_edge_case.tif differ 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 0000000000..4036e10110 Binary files /dev/null and b/tests/images/tiff/testsuite/cmyk_u8_trunc_case.tif differ