mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
commit
4fe0466942
3 files changed
+58
-5
No files matched your search
@@ -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()));
|
||||
|
||||
@@ -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)] {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user