mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-26 19:17:02 -06:00
LR-LIB-CONVERT: avoid duplicated DNG camera make (#840)
This commit is contained in:
@@ -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()));
|
||||
|
||||
@@ -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)] {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user