From 699bbd4b85e509d1da4a0eb249b313c6cd2cee28 Mon Sep 17 00:00:00 2001 From: Korg1ch <57859048+Korg1ch@users.noreply.github.com> Date: Mon, 19 Oct 2026 20:54:34 +0300 Subject: [PATCH] =?UTF-8?q?fix(engine):=20a=20developed=20raw=20opens=20at?= =?UTF-8?q?=20Preferences=20=E2=80=BA=20Raw=20Defaults=20=E2=80=BA=20Resol?= =?UTF-8?q?ution=20(#7789)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/engine/src/file_cmds.rs | 11 +++++++++++ crates/engine/src/jobs.rs | 1 + crates/engine/src/prefs.rs | 1 - crates/ui-egui/src/camera_raw_ui.rs | 6 +++++- crates/ui-egui/src/lib.rs | 7 +++++-- docs/scorecard.md | 6 +++--- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/file_cmds.rs b/crates/engine/src/file_cmds.rs index 5770d054b1..15b0a2d6a1 100644 --- a/crates/engine/src/file_cmds.rs +++ b/crates/engine/src/file_cmds.rs @@ -558,6 +558,16 @@ pub(crate) fn tag_exr_colorspace(s: &Session, name: &str, doc: &mut Document) { } } +/// Edit › Preferences › Raw Defaults › Resolution: the ppi a raw developed on open gets, as +/// Camera Raw's workflow options set it (the raw itself states none). `warnings` are the +/// import's notes, which say whether the raw was developed or only its preview opened. +pub fn raw_defaults(s: &Session, warnings: &[String], doc: &mut Document) { + let ppi = s.prefs().raw_defaults.resolution; + if ppi.is_finite() && warnings.iter().any(|w| w.contains(photocraft_io::raw::DEVELOPED_NOTE)) { + doc.resolution_dpi = ppi.clamp(1.0, 10_000.0) as f32; + } +} + pub fn open_bytes_as(s: &mut Session, name: &str, bytes: &[u8], as_ext: Option<&str>, path: Option) -> Result { crate::allocation::checkpoint("importing document")?; let decode_name = match as_ext { @@ -567,6 +577,7 @@ pub fn open_bytes_as(s: &mut Session, name: &str, bytes: &[u8], as_ext: Option<& let r = photocraft_io::import(&decode_name, bytes).map_err(|e| EngineError::Other(format!("{decode_name}: {e}")))?; let mut doc = r.document; tag_exr_colorspace(s, &decode_name, &mut doc); + raw_defaults(s, &r.warnings, &mut doc); let path = match template_name(s, name) { Some(untitled) => { doc.name = untitled; diff --git a/crates/engine/src/jobs.rs b/crates/engine/src/jobs.rs index 0ee0573170..5db1d2dc41 100644 --- a/crates/engine/src/jobs.rs +++ b/crates/engine/src/jobs.rs @@ -537,6 +537,7 @@ impl Session { move |s, mut r: photocraft_io::ImportResult| { // File Handling's ACEScg option, before the colour prompt looks at the profile. crate::file_cmds::tag_exr_colorspace(s, &name_a, &mut r.document); + crate::file_cmds::raw_defaults(s, &r.warnings, &mut r.document); // Ask When Opening: held back until the prompt is answered (`take_held_open`). A // developed raw has no embedded profile to ask about. let raw = r.warnings.iter().any(|w| w.contains(photocraft_io::raw::DEVELOPED_NOTE)); diff --git a/crates/engine/src/prefs.rs b/crates/engine/src/prefs.rs index 419468426a..a09ab4050f 100644 --- a/crates/engine/src/prefs.rs +++ b/crates/engine/src/prefs.rs @@ -1059,7 +1059,6 @@ pub const HIDDEN_UNTIL_IMPLEMENTED: &[&str] = &[ "enhancedControls.scrubbySliderAcceleration", "rawDefaults.colorSpace", "rawDefaults.bitDepth", - "rawDefaults.resolution", "rawDefaults.sharpenFor", "rawDefaults.openAsSmartObject", "rawDefaults.applyAutoTone", diff --git a/crates/ui-egui/src/camera_raw_ui.rs b/crates/ui-egui/src/camera_raw_ui.rs index 090c0a0b3c..cfd0d5c8df 100644 --- a/crates/ui-egui/src/camera_raw_ui.rs +++ b/crates/ui-egui/src/camera_raw_ui.rs @@ -469,12 +469,16 @@ fn commit_open_raw(app: &mut PhotocraftApp, params: &CameraRaw, raw: &RawOpen) - fn replace_raw_document( s: &mut photocraft_engine::Session, doc_id: photocraft_doc::DocId, - r: photocraft_io::ImportResult, + mut r: photocraft_io::ImportResult, wb: bool, ) -> photocraft_engine::Result { let index = s.documents().iter().position(|st| st.doc.id == doc_id).ok_or_else(|| photocraft_engine::EngineError::Other("the raw document was closed".into()))?; let path = s.documents().get(index).and_then(|st| st.path.clone()); + // The resolution it opened with (Preferences › Raw Defaults), which re-developing keeps. + if let Some(st) = s.documents().get(index) { + r.document.resolution_dpi = st.doc.resolution_dpi; + } s.close(index); let (opened, _) = s.open_document(r.document, path); let index = match s.execute("document.move", json!({"document": opened, "to": index})) { diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 8784800ddd..4f022a30b8 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -1455,7 +1455,9 @@ impl PhotocraftApp { } let import = self.services.import.as_ref().ok_or("no importer configured")?; let max_svg_group_depth = self.session.prefs().file_handling.rasterize_svg_groups_deeper_than as usize; - let (doc, warnings) = import(name, bytes, max_svg_group_depth)?; + let (mut doc, warnings) = import(name, bytes, max_svg_group_depth)?; + // Preferences › Raw Defaults › Resolution, as the engine's open applies it. + photocraft_engine::file_cmds::raw_defaults(&self.session, &warnings, &mut doc); // Edit › Color Settings policies apply on open; Ask When Opening holds the file back // until its prompt is answered, and later files wait behind it (`open_prompt`). // Missing fonts are read from the document itself: it may not be open yet (#4559). @@ -1505,7 +1507,8 @@ impl PhotocraftApp { fn import_automation_document(&mut self, name: &str, bytes: &[u8]) -> Result, String> { let import = self.services.import.as_ref().ok_or("no importer configured")?; let max_svg_group_depth = self.session.prefs().file_handling.rasterize_svg_groups_deeper_than as usize; - let (doc, warnings) = import(name, bytes, max_svg_group_depth)?; + let (mut doc, warnings) = import(name, bytes, max_svg_group_depth)?; + photocraft_engine::file_cmds::raw_defaults(&self.session, &warnings, &mut doc); // The caller records the path it read from. self.session.add_document(doc, None); if let Some(st) = self.session.active_mut() { diff --git a/docs/scorecard.md b/docs/scorecard.md index 5a59aad2a4..c8e786bd7a 100644 --- a/docs/scorecard.md +++ b/docs/scorecard.md @@ -9,7 +9,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js | [Performance](#performance) | 3 | 22 | 26 | 60 scenarios: 3 within budget, 22 over budget or failing, 9 tracked only, 22 not run, 4 not measurable yet (baseline 2026-10-05) | | [File compatibility](#file-compatibility) | 9 | 12 | 6 | io corpus: oracle ≥ 148/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 249/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 188/256, round trip ≥ 258/256 | | [Tools](#tools) | 36 | 8 | 0 | | -| [Workspace and UI](#workspace-and-ui) | 13 | 7 | 3 | settings that do nothing: 24 of 171 (target 0) | +| [Workspace and UI](#workspace-and-ui) | 13 | 7 | 3 | settings that do nothing: 23 of 171 (target 0) | | [Type](#type) | 5 | 3 | 1 | | | [Automation](#automation) | 5 | 2 | 4 | 631 of 631 menu items live (100.0%) | | [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 30 of 30 crates; panic_hunt present | @@ -181,9 +181,9 @@ Corpus floors (`crates/io/tests/corpus.rs`): the enforced minimums; the corpus t ## Workspace and UI -**Settings that do nothing: 24 of 171** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't. +**Settings that do nothing: 23 of 171** (target 0, #204). Counted by the prefs audit (`xtask/src/scorecard.rs`): a `Preferences` field counts as read when code outside the prefs model (`prefs.rs` except its `impl Session` blocks; tests excluded; the schema-driven dialog never names a field) accesses `section.field`, or `.section` and `.field` in the same file, or calls a section method that reads `self.field`. It is a heuristic lower bound: a generic name can look read when it isn't. -Unread: `general.color_picker`, `general.zoom_resizes_windows`, `workspace.auto_show_hidden_panels`, `workspace.enable_floating_document_window_docking`, `performance.cache_levels`, `performance.gpu_adapter`, `performance.legacy_compositing`, `scratch_disks.disks`, `units_and_rulers.column_width`, `units_and_rulers.gutter`, `plug_ins.show_extension_panels`, `plug_ins.allow_scripts_to_connect`, `plug_ins.generator_enabled`, `type_.text_engine`, `type_.recent_fonts`, `enhanced_controls.scrubby_slider_acceleration`, `raw_defaults.color_space`, `raw_defaults.bit_depth`, `raw_defaults.resolution`, `raw_defaults.sharpen_for`, `raw_defaults.open_as_smart_object`, `raw_defaults.apply_auto_tone`, `integrations.allow_agent_control`, `integrations.control_port`. +Unread: `general.color_picker`, `general.zoom_resizes_windows`, `workspace.auto_show_hidden_panels`, `workspace.enable_floating_document_window_docking`, `performance.cache_levels`, `performance.gpu_adapter`, `performance.legacy_compositing`, `scratch_disks.disks`, `units_and_rulers.column_width`, `units_and_rulers.gutter`, `plug_ins.show_extension_panels`, `plug_ins.allow_scripts_to_connect`, `plug_ins.generator_enabled`, `type_.text_engine`, `type_.recent_fonts`, `enhanced_controls.scrubby_slider_acceleration`, `raw_defaults.color_space`, `raw_defaults.bit_depth`, `raw_defaults.sharpen_for`, `raw_defaults.open_as_smart_object`, `raw_defaults.apply_auto_tone`, `integrations.allow_agent_control`, `integrations.control_port`. | Id | Target | Status | Issue | Note | |---|---|---|---|---|