diff --git a/crates/raw/src/dng.rs b/crates/raw/src/dng.rs index 27e30002f..ba7dac532 100644 --- a/crates/raw/src/dng.rs +++ b/crates/raw/src/dng.rs @@ -290,7 +290,9 @@ pub(crate) fn decode_as(bytes: &[u8], mode: Mode, format: RawFormat) -> Result Result> { if let Some(cfa) = &raw.cfa { ifd.set(t::CFA_REPEAT_PATTERN_DIM, Value::Short(vec![cfa.height as u16, cfa.width as u16])); - ifd.set(t::CFA_PATTERN_EP, Value::Byte(cfa.pattern.clone())); + // the tag is anchored at the ActiveArea's top-left, `raw.cfa` at the sensor origin + let a = raw.active_area; + ifd.set(t::CFA_PATTERN_EP, Value::Byte(cfa.shifted(a.x, a.y).pattern)); ifd.set(t::CFA_PLANE_COLOR, Value::Byte(vec![0, 1, 2])); ifd.set(t::CFA_LAYOUT, Value::Short(vec![1])); } diff --git a/crates/raw/tests/dng.rs b/crates/raw/tests/dng.rs index 44527cf9c..9feccdcbd 100644 --- a/crates/raw/tests/dng.rs +++ b/crates/raw/tests/dng.rs @@ -989,3 +989,24 @@ fn baseline_sharpness_is_read_when_plausible() { assert_eq!(decode(&bytes).unwrap().color.baseline_sharpness, None); assert_eq!(decode(&profile_dng(ByteOrder::Big, |_| {})).unwrap().color.baseline_sharpness, None); } + +/// The DNG CFAPattern is anchored at the top-left of the ActiveArea: a DNG whose area starts on an odd row or +/// column still demosaics with the pattern the tag states (#1086), and the writer states it back the same way. +#[test] +fn cfa_pattern_is_relative_to_the_active_area() { + let (w, h) = (20, 12); + for (top, left, sensor_name) in [(0u32, 0u32, "GRBG"), (1, 0, "BGGR"), (0, 1, "RGGB"), (1, 1, "GBRG"), (3, 5, "GBRG"), (2, 4, "GRBG")] { + let mut raw = base_ifd(w, h, 16, 1, true); + raw.set_image(ImageData::Strips { rows_per_strip: h as u32, strips: vec![u16_bytes(&pattern(w, h, 1, 16), ByteOrder::Little)] }); + raw.set(t::WHITE_LEVEL, Value::Short(vec![65535])); + raw.set(t::ACTIVE_AREA, Value::Long(vec![top, left, h as u32 - 1, w as u32 - 1])); + let img = decode(&dng(raw, ByteOrder::Little)).unwrap(); + assert_eq!(img.cfa.as_ref().unwrap().name(), sensor_name, "sensor anchor for area at ({left}, {top})"); + assert_eq!(img.normalized().unwrap().cfa.unwrap().name(), "GRBG", "tag pattern for area at ({left}, {top})"); + let back = + decode(&write_dng(&img, &DngWriteOptions { compression: DngCompression::Uncompressed, order: ByteOrder::Little, xmp: None }).unwrap()) + .unwrap(); + assert_eq!(back.cfa.as_ref().unwrap().name(), sensor_name); + assert_eq!(back.normalized().unwrap().cfa.unwrap().name(), "GRBG"); + } +}