mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
Never crash: Refuse raw data with two samples per pixel (#1511)
The demosaic read past each pixel. DNG decode and validation refuse it now.
This commit is contained in:
1 parent
b3d392b499
commit
3624a50423
4 files changed
+45
-2
No files matched your search
@@ -79,7 +79,20 @@ pub fn demosaic(n: &Normalized, method: Method) -> Rgb32f {
|
||||
}
|
||||
match (&n.cfa, n.cpp) {
|
||||
(_, 3) => Rgb32f { width: w, height: h, data: n.data.as_chunks::<3>().0.iter().map(|c| [c[0], c[1], c[2]]).collect() },
|
||||
(_, cpp) if cpp != 1 => Rgb32f { width: w, height: h, data: n.data.chunks_exact(cpp).map(|c| [c[0], c[1], c[2]]).collect() },
|
||||
// four colours develop from their first three; narrower data (refused by `RawImage::validate`, issue #1511)
|
||||
// repeats its last sample instead of reading past the pixel
|
||||
(_, cpp) if cpp != 1 => Rgb32f {
|
||||
width: w,
|
||||
height: h,
|
||||
data: n
|
||||
.data
|
||||
.chunks_exact(cpp.max(1))
|
||||
.map(|c| {
|
||||
let s = |i: usize| c.get(i).or(c.last()).copied().unwrap_or(0.0);
|
||||
[s(0), s(1), s(2)]
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
(None, _) => Rgb32f { width: w, height: h, data: n.data.iter().map(|&v| [v; 3]).collect() },
|
||||
(Some(cfa), _) => {
|
||||
let m = Mosaic { w, h, data: &n.data, cfa };
|
||||
|
||||
@@ -207,7 +207,8 @@ pub(crate) fn decode_as(bytes: &[u8], mode: Mode, format: RawFormat) -> Result<R
|
||||
}
|
||||
let info = raw.image()?;
|
||||
let (w, h, cpp) = (info.width as usize, info.height as usize, info.samples_per_pixel as usize);
|
||||
if !(1..=4).contains(&cpp) {
|
||||
// one sample (CFA or monochrome), RGB or four colours; two are neither and can't be developed (issue #1511)
|
||||
if !matches!(cpp, 1 | 3 | 4) {
|
||||
return Err(RawError::Unsupported(format!("{cpp} samples per pixel")));
|
||||
}
|
||||
let mut data = read_image_in(mode, bytes, &info, tiff.order, Packing::Msb)?;
|
||||
|
||||
@@ -715,6 +715,10 @@ impl RawImage {
|
||||
if self.width == 0 || self.height == 0 || !(1..=4).contains(&self.cpp) {
|
||||
return Err(RawError::Corrupt("bad dimensions".into()));
|
||||
}
|
||||
// two samples a pixel are neither one colour nor RGB: nothing downstream can develop them (issue #1511)
|
||||
if self.cpp == 2 {
|
||||
return Err(RawError::Unsupported("2 samples per pixel".into()));
|
||||
}
|
||||
if samples && self.data.len() != self.width * self.height * self.cpp {
|
||||
return Err(RawError::Corrupt("data length mismatch".into()));
|
||||
}
|
||||
|
||||
@@ -1047,3 +1047,28 @@ fn cfa_pattern_is_relative_to_the_active_area() {
|
||||
assert_eq!(back.normalized().unwrap().cfa.unwrap().name(), "GRBG");
|
||||
}
|
||||
}
|
||||
|
||||
/// Two samples a pixel are neither one colour nor RGB. A DNG with them decoded, and developing it read a third sample
|
||||
/// past each pixel, a panic in release builds too (issue #1511). Such data is refused with a reason, and the demosaic
|
||||
/// itself no longer reads past a pixel; one, three and four samples develop as before.
|
||||
#[test]
|
||||
fn two_samples_per_pixel_are_refused_not_a_panic() {
|
||||
let (w, h) = (8, 6);
|
||||
let file = |cpp: usize| {
|
||||
let mut raw = base_ifd(w, h, 16, cpp, false);
|
||||
raw.set_image(ImageData::Strips { rows_per_strip: h as u32, strips: vec![u16_bytes(&pattern(w, h, cpp, 16), ByteOrder::Little)] });
|
||||
dng(raw, ByteOrder::Little)
|
||||
};
|
||||
let err = decode(&file(2)).unwrap_err();
|
||||
assert!(matches!(err, lightcraft_raw::RawError::Unsupported(ref m) if m.contains("2 samples")), "{err}");
|
||||
for cpp in [1, 3, 4] {
|
||||
decode(&file(cpp)).unwrap().develop(Method::Bilinear).unwrap();
|
||||
}
|
||||
// a raw image built with two samples is refused when it develops
|
||||
let raw = synthetic(16, 12, None, 2);
|
||||
let err = raw.develop(Method::Ahd).unwrap_err();
|
||||
assert!(matches!(err, lightcraft_raw::RawError::Unsupported(_)), "{err}");
|
||||
// the demosaic of such data repeats the last sample instead of reading past the pixel
|
||||
let n = lightcraft_raw::Normalized { width: 2, height: 1, cpp: 2, data: vec![0.1, 0.2, 0.3, 0.4], cfa: None };
|
||||
assert_eq!(lightcraft_raw::demosaic(&n, Method::Ahd).data, vec![[0.1, 0.2, 0.2], [0.3, 0.4, 0.4]]);
|
||||
}
|
||||
Reference in new issue
Block a user