LR-LIB-CONVERT: avoid duplicated DNG camera make (#840)

This commit is contained in:
skybluu
2026-10-13 21:13:51 -05:00
parent 701f78ea2e
commit ce607d5a49
3 changed files with 61 additions and 5 deletions
+14 -5
View File
@@ -108,15 +108,24 @@ pub fn write_dng(raw: &RawImage, opts: &DngWriteOptions) -> Result<Vec<u8>> {
ifd.set(t::PHOTOMETRIC, Value::Short(vec![if raw.cfa.is_some() { photometric::CFA } else { photometric::LINEAR_RAW }]));
ifd.set(t::DNG_VERSION, Value::Byte(vec![1, 4, 0, 0]));
ifd.set(t::DNG_BACKWARD_VERSION, Value::Byte(vec![1, 3, 0, 0]));
let make = raw.metadata.make.clone().unwrap_or_default();
let model = raw.metadata.model.clone().unwrap_or_default();
let make = raw.metadata.make.as_deref().unwrap_or_default();
let model = raw.metadata.model.as_deref().unwrap_or_default();
if !make.is_empty() {
ifd.set(t::MAKE, Value::Ascii(make.clone()));
ifd.set(t::MAKE, Value::Ascii(make.to_owned()));
}
if !model.is_empty() {
ifd.set(t::MODEL, Value::Ascii(model.clone()));
ifd.set(t::MODEL, Value::Ascii(model.to_owned()));
}
let unique = format!("{make} {model}").trim().to_string();
let model_includes_make = !make.is_empty()
&& model.get(..make.len()).is_some_and(|prefix| prefix.eq_ignore_ascii_case(make))
&& model.get(make.len()..).is_some_and(|suffix| suffix.chars().next().is_none_or(|c| !c.is_alphanumeric() && c != '_'));
let unique = if model_includes_make || make.is_empty() {
model.to_owned()
} else if model.is_empty() {
make.to_owned()
} else {
format!("{make} {model}")
};
ifd.set(t::UNIQUE_CAMERA_MODEL, Value::Ascii(if unique.is_empty() { "LightCraft".into() } else { unique }));
ifd.set(t::ORIENTATION, Value::Short(vec![raw.orientation.to_exif()]));
ifd.set(t::SOFTWARE, Value::Ascii("LightCraft".into()));
+39
View File
@@ -90,6 +90,45 @@ fn assert_same(a: &RawImage, b: &RawImage) {
assert_eq!(b.metadata.rating, Some(3));
}
#[test]
fn writer_camera_identity_tags_roundtrip() {
let cases = [
(Some("Canon"), Some("Canon EOS R6"), "Canon EOS R6"),
(Some("CANON"), Some("cAnOn EOS R6"), "cAnOn EOS R6"),
(Some("Canon"), Some("Canon"), "Canon"),
(Some("Canon"), Some("Canon-EOS R6"), "Canon-EOS R6"),
(Some("Canon"), Some("Canon_EOS R6"), "Canon Canon_EOS R6"),
(Some("Canon"), Some("Canonic R6"), "Canon Canonic R6"),
(Some("Canon"), Some("Canon2 R6"), "Canon Canon2 R6"),
(Some("Canon"), Some("Canoné R6"), "Canon Canoné R6"),
(Some("Canon"), Some("EOS R6"), "Canon EOS R6"),
(Some("Eastman Kodak"), Some("eAsTmAn kOdAk DCS Pro"), "eAsTmAn kOdAk DCS Pro"),
(Some("Eastman Kodak"), Some("Eastman Kodakish DCS Pro"), "Eastman Kodak Eastman Kodakish DCS Pro"),
(Some("Eastman Kodak"), Some("Eastman DCS Pro"), "Eastman Kodak Eastman DCS Pro"),
(Some(""), Some("EOS R6"), "EOS R6"),
(Some("Canon"), Some(""), "Canon"),
(Some(""), Some(""), "LightCraft"),
(None, Some("EOS R6"), "EOS R6"),
(Some("Canon"), None, "Canon"),
(None, None, "LightCraft"),
];
for (make, model, unique) in cases {
let mut raw = synthetic(16, 16, Some(Cfa::bayer("RGGB").unwrap()), 1);
raw.metadata.make = make.map(str::to_owned);
raw.metadata.model = model.map(str::to_owned);
for order in [ByteOrder::Little, ByteOrder::Big] {
let bytes = write_dng(&raw, &DngWriteOptions { compression: DngCompression::Uncompressed, order, xmp: None }).unwrap();
let tiff = lightcraft_tiff::Tiff::parse(&bytes).unwrap();
let ifd = &tiff.ifds[0];
assert_eq!(ifd.value(t::UNIQUE_CAMERA_MODEL), Some(&Value::Ascii(unique.into())), "{make:?} / {model:?}, {order:?}");
for (tag, original) in [(t::MAKE, make), (t::MODEL, model)] {
let expected = original.filter(|s| !s.is_empty()).map(|s| Value::Ascii(s.into()));
assert_eq!(ifd.value(tag), expected.as_ref(), "tag {tag}, {make:?} / {model:?}, {order:?}");
}
}
}
}
#[test]
fn writer_roundtrip_all_layouts() {
for (w, h) in [(64, 48), (70, 45), (33, 17)] {
+8
View File
@@ -1,5 +1,13 @@
# What's new in LightCraft
## 2026-10-13
### DNG camera identity
- DNG export and Convert to DNG no longer duplicate the camera make when the model already
contains it: `Canon EOS R6` stays `Canon EOS R6`, rather than `Canon Canon EOS R6` (the camera
identity subissue in #840). The separate Make and Model tags keep their original values.
This does not change colour matrices, camera calibration or RAW noise rendering.
## 2026-10-11
### JPEG XL export