mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-30 21:18:11 -06:00
LR-EDIT-OPTICS-PROFILE: Sony embedded corrections on by default for ARW
An ILCE-7RM6 ARW with Sony's distortion table now imports with Enable Lens Corrections on: Photo::camera_defaults already turns the correction on for any file whose probe yields embedded lens data, and the ARW decoder emits it since the previous commit. No new default rule is needed, so RW2 and DNG keep theirs (on when the file carries a correction) and stored settings are never rewritten. Tests pin it: a synthetic ARW imports with the correction on (another body's tables are not read: off), RW2 / DNG defaults are unchanged, and an ARW catalogued without lens data or switched off by the user keeps its settings across a library reopen. The Optics panel's hint drops "(DNG lens opcodes only)" in every locale. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
b677fbac0a
commit
d982a073c9
@@ -2296,4 +2296,112 @@ mod tests {
|
||||
assert_eq!(s.quick_view_job(id, 1600, true).unwrap().run().quick, Some(QuickSource::Small));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// A Sony ARW of `model`: a 64 × 48 uncompressed 14-bit CFA strip in a SubIFD (as `crates/raw/tests/vendor_robust.rs`
|
||||
/// lays it out) whose IFD carries the full-frame 16–35 mm distortion table (tag 0x7037).
|
||||
fn sony_arw(model: &str) -> Vec<u8> {
|
||||
use lightcraft_tiff::tags::{self as t, photometric};
|
||||
use lightcraft_tiff::{ByteOrder, IfdBuilder, ImageData, TiffWriter, Value};
|
||||
// 3:2, the aspect Sony's lens opcodes are limited to
|
||||
let (w, h) = (72u32, 48u32);
|
||||
let mut raw = IfdBuilder::new();
|
||||
raw.set(t::NEW_SUBFILE_TYPE, Value::Long(vec![0]));
|
||||
raw.set(t::IMAGE_WIDTH, Value::Long(vec![w]));
|
||||
raw.set(t::IMAGE_LENGTH, Value::Long(vec![h]));
|
||||
raw.set(t::BITS_PER_SAMPLE, Value::Short(vec![14]));
|
||||
raw.set(t::COMPRESSION, Value::Short(vec![1]));
|
||||
raw.set(t::PHOTOMETRIC, Value::Short(vec![photometric::CFA]));
|
||||
raw.set(t::CFA_REPEAT_PATTERN_DIM, Value::Short(vec![2, 2]));
|
||||
raw.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
|
||||
raw.set(0x7037, Value::SShort(vec![16, 0, 2, 7, 16, 28, 43, 61, 83, 109, 139, 175, 217, 267, 325, 395, 479]));
|
||||
raw.set_image(ImageData::Strips { rows_per_strip: h, strips: vec![2000u16.to_le_bytes().repeat((w * h) as usize)] });
|
||||
let mut ifd0 = IfdBuilder::new();
|
||||
ifd0.set(t::MAKE, Value::Ascii("SONY".into()));
|
||||
ifd0.set(t::MODEL, Value::Ascii(model.into()));
|
||||
ifd0.add_sub_ifd(raw);
|
||||
TiffWriter::new(ByteOrder::Little, false).write(&[ifd0]).unwrap()
|
||||
}
|
||||
|
||||
/// Sony's embedded distortion correction is on by default: an ARW of a verified body imports with "Enable Lens
|
||||
/// Corrections" on and still counts as unedited; another body's tables aren't read, so its correction is off.
|
||||
#[test]
|
||||
fn arw_with_lens_data_imports_with_profile_corrections_on() {
|
||||
let dir = std::env::temp_dir().join(format!("lc-arw-lens-default-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let mut s = crate::Session::new().with_fs();
|
||||
for (model, on) in [("ILCE-7RM6", true), ("ILCE-7M4", false)] {
|
||||
let path = dir.join(format!("{model}.arw"));
|
||||
std::fs::write(&path, sony_arw(model)).unwrap();
|
||||
let r = s.execute("library.import", &serde_json::json!({"paths": [path.to_string_lossy()]})).unwrap();
|
||||
let p = s.catalog.photo(lightcraft_catalog::PhotoId(r["imported"][0].as_u64().unwrap())).unwrap();
|
||||
assert_eq!((p.format.as_str(), p.embedded_lens.is_some(), p.develop.optics.lens_profile), ("ARW", on, on), "{model}");
|
||||
assert!(!p.is_edited(), "{model}: as imported");
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// RW2 and DNG keep their default: the correction is on when the file carries one (RW2 distortion, a DNG's
|
||||
/// opcodes), off when it doesn't.
|
||||
#[test]
|
||||
fn rw2_and_dng_defaults_unchanged() {
|
||||
use lightcraft_catalog::{Photo, PhotoId, Source};
|
||||
let s = crate::Session::new();
|
||||
let lens = lightcraft_develop::EmbeddedLens { warp: Some(Default::default()), ..Default::default() };
|
||||
for format in ["RW2", "DNG"] {
|
||||
for embedded in [None, Some(lens)] {
|
||||
let name = format!("x.{format}");
|
||||
let mut p = Photo::new(PhotoId(1), Source::File { path: format!("/{name}") }, &name, format, 64, 48, "");
|
||||
p.kind = MediaKind::Raw;
|
||||
p.embedded_lens = embedded;
|
||||
crate::import::apply_import_defaults(&s, &mut p);
|
||||
assert_eq!(p.develop.optics.lens_profile, embedded.is_some(), "{format} {embedded:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stored settings stay as they are when the library opens again: an ARW catalogued before its lens data was read
|
||||
/// (none, correction off) keeps the correction off and counts as unedited, and one whose user switched the
|
||||
/// correction off keeps it off.
|
||||
#[test]
|
||||
fn existing_photo_settings_are_not_rewritten() {
|
||||
use lightcraft_catalog::{Op, PhotoId};
|
||||
let dir = std::env::temp_dir().join(format!("lc-arw-lens-existing-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
let (lib, files) = (dir.join("lib"), dir.join("files"));
|
||||
std::fs::create_dir_all(&files).unwrap();
|
||||
let mut s = crate::Session::new().with_fs();
|
||||
s.open_library(&lib, false).unwrap();
|
||||
let import = |s: &mut crate::Session, name: &str| {
|
||||
let path = files.join(name);
|
||||
// trailing bytes of its own: not a duplicate of the other file
|
||||
std::fs::write(&path, [sony_arw("ILCE-7RM6"), name.as_bytes().to_vec()].concat()).unwrap();
|
||||
let r = s.execute("library.import", &serde_json::json!({"paths": [path.to_string_lossy()]})).unwrap();
|
||||
PhotoId(r["imported"][0].as_u64().unwrap())
|
||||
};
|
||||
let old = import(&mut s, "old.arw");
|
||||
// as a release that didn't read the Sony tables catalogued it
|
||||
let mut d = (*s.catalog.photo(old).unwrap().develop).clone();
|
||||
d.optics.lens_profile = false;
|
||||
let ops = vec![
|
||||
Op::SetEmbeddedLens { id: old, lens: None },
|
||||
Op::SetDevelop { id: old, settings: std::sync::Arc::new(d), label: "x".into(), edited: None },
|
||||
];
|
||||
s.commit("x", Op::Batch { ops }).unwrap();
|
||||
let off = import(&mut s, "off.arw");
|
||||
s.execute("develop.merge", &serde_json::json!({"settings": {"optics": {"lens_profile": false}}})).unwrap();
|
||||
let before: Vec<_> = [old, off].iter().map(|id| s.catalog.photo(*id).unwrap().clone()).collect();
|
||||
assert!(before.iter().all(|p| !p.develop.optics.lens_profile));
|
||||
s.persist().unwrap();
|
||||
drop(s);
|
||||
let mut s = crate::Session::new().with_fs();
|
||||
s.open_library(&lib, false).unwrap();
|
||||
for b in &before {
|
||||
let p = s.catalog.photo(b.id).unwrap();
|
||||
assert_eq!((&p.develop, &p.embedded_lens), (&b.develop, &b.embedded_lens), "{}", p.file_name);
|
||||
}
|
||||
assert!(!s.catalog.photo(old).unwrap().is_edited(), "still as imported");
|
||||
drop(s);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user