# Conflicts:
#	docs/whats-new.md
This commit is contained in:
Brandon Thomas committed 2026-10-14 01:39:02 -04:00
commit 4fe0466942
3 files changed
+58 -5

No files matched your search

+14 -5
View File
@@ -112,15 +112,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(SOFTWARE.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)] {
+5
View File
@@ -85,6 +85,11 @@
- A demo photo whose stored size was damaged (a width of 4294967295) is rendered at its own size instead of
one that overflows.
### 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.
### Slider drags on large raws
- Dragging a slider no longer re-renders the edited photo's filmstrip thumbnail on every frame; it follows once you
let go. On a 64 MP raw the first of those renders decoded the whole file again, which held up the loupe's