Mark what is in focus, so a frame can be judged without zooming to 100%
FR-CULL-3's focus peaking. One compute dispatch measures local contrast in WGSL and writes an overlay texture; on desktop it reaches Slint through the same zero-copy wgpu import the canvas uses, so nothing per-pixel touches the CPU on the frame path. With peaking off the cost is zero and structurally so: focus_overlay opens with `let settings = self.peaking?;` before the frame is touched, and clearing drops both overlay textures, so no VRAM is held either. NFR-P14 is met by construction rather than by measurement -- one dispatch, no second render, no pipeline compile after session open, and a test asserting allocations stay at 2 over eight frames. The budget test asserts 50ms at 4K rather than a tight bound, deliberately: a tight bound fails on a loaded machine and gets deleted, which is worse than a loose one that still catches the regression that matters. TD-1 is amended rather than joined by a TD-6: on Android the overlay rides the readback that already exists there, roughly doubling that transfer while peaking is on, and TD-1's own "Done when" removes both because both are the same missing capability. Verified: cargo fmt clean; clippy --workspace --all-targets -D warnings green, which also compiles peaking.slint through dr-ui's build.rs; 11 focus GPU tests and 79 baseline dr-gpu tests pass; 511 dr-ui tests pass. Not verified: the cfg(target_os = "android") arm, which the host-target clippy never compiled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { LibraryGrid, LibraryCell, TimelineBar, PhotoRoll, KeywordRow, PersonChi
|
||||
import { Button, PanelHeading, Label, Value, Caption, Panel, EmptyState, ProgressBar, ActivityRow } from "widgets.slint";
|
||||
import { CollectionsPanel, CollectionRow, OfflinePrompt } from "collections.slint";
|
||||
import { HistogramPanel, HistogramView } from "histogram.slint";
|
||||
import { FocusMarks, FocusPanel } from "peaking.slint";
|
||||
import { SettingsPage } from "settings.slint";
|
||||
import { ImportPage } from "import.slint";
|
||||
import { StatusBar, InfoPanel } from "develop.slint";
|
||||
@@ -70,6 +71,22 @@ export component AppWindow inherits Window {
|
||||
/// of a draft frame is a histogram of an image nobody is reading.
|
||||
in property <HistogramView> histogram;
|
||||
|
||||
/// TRACES: FR-CULL-3
|
||||
/// Focus peaking: the marks, whether they describe *this* frame, and the
|
||||
/// three things the photographer chose. All of them are Rust's, because
|
||||
/// the marks come from a compute pass — see `peaking.slint` for why
|
||||
/// `focus-overlay-ready` is a separate question from `peaking-on`.
|
||||
in property <image> focus-overlay;
|
||||
in property <bool> focus-overlay-ready: false;
|
||||
in property <bool> peaking-on: false;
|
||||
in property <bool> peaking-available: true;
|
||||
in property <int> peaking-sensitivity: 1;
|
||||
in property <int> peaking-colour: 0;
|
||||
|
||||
callback peaking-toggled(bool);
|
||||
callback peaking-sensitivity-picked(int);
|
||||
callback peaking-colour-picked(int);
|
||||
|
||||
// --- zoom, pan and crop (FR-DEV-4) ---
|
||||
//
|
||||
// Zoom is a *viewing* state, not an edit: it changes the resolution the
|
||||
@@ -1617,6 +1634,18 @@ in property <bool> panel-visible: true;
|
||||
image-rendering: ImageRendering.pixelated;
|
||||
}
|
||||
|
||||
// TRACES: FR-CULL-3
|
||||
// The focus marks, over the same fitted rect. See
|
||||
// `peaking.slint` for why they are a layer over the canvas
|
||||
// rather than a tint in it.
|
||||
if root.focus-overlay-ready && root.total > 0: FocusMarks {
|
||||
x: parent.shown-x;
|
||||
y: parent.shown-y;
|
||||
width: parent.shown-w;
|
||||
height: parent.shown-h;
|
||||
marks: root.focus-overlay;
|
||||
}
|
||||
|
||||
// Where the photograph actually sits inside this box.
|
||||
//
|
||||
// `image-fit: contain` letterboxes, and Slint does not report
|
||||
@@ -2150,6 +2179,26 @@ in property <bool> panel-visible: true;
|
||||
background: Theme.rule;
|
||||
}
|
||||
|
||||
// TRACES: FR-CULL-3
|
||||
// Under the histogram, because the two are the same
|
||||
// kind of thing: instruments that report on the
|
||||
// photograph rather than change it. Kept in every
|
||||
// mode for the same reason the histogram is.
|
||||
FocusPanel {
|
||||
available: root.peaking-available;
|
||||
showing: root.peaking-on;
|
||||
sensitivity: root.peaking-sensitivity;
|
||||
colour: root.peaking-colour;
|
||||
toggled(v) => { root.peaking-toggled(v); }
|
||||
sensitivity-picked(i) => { root.peaking-sensitivity-picked(i); }
|
||||
colour-picked(i) => { root.peaking-colour-picked(i); }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 1px;
|
||||
background: Theme.rule;
|
||||
}
|
||||
|
||||
// Framing above the colour work, matching how the edit is
|
||||
// made rather than how it is applied: the frame is decided
|
||||
// by eye first and the pipeline runs it last (see
|
||||
|
||||
Reference in New Issue
Block a user