tests/accessibility.rs walks the AccessKit tree headless in every view,
Develop tool and 13 dialogs and fails on unnamed controls, sliders without
value/range and untitled dialogs. Fixes what it found: text fields are
named by caption or hint, develop sliders expose min/max/step/value text
and take Increment/Decrement, Tab-focused sliders take the arrow keys,
Auto Stack's slider is named. Also as_chunks in spots.rs for the newer
clippy lint. Docs: ui-parity audit section, checklist, gaps, ROADMAP.
Sources: own design.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The horizontal box pass gave each thread 16+ consecutive pixels to slide
a running sum over, so a workgroup's threads read pixels 16 apart. On a
Radeon 780M (DX12) that pass took 9-15x as long as the vertical one, and
it sits under every GPU Gaussian and guided filter (NR, texture, dehaze,
clarity/base). Each thread now sums one pixel's 2r+1 window: adjacent
threads read adjacent pixels. Same 64x4 workgroups, parameters and
bindings; box_v unchanged.
Measured on the Radeon 780M (6000x4000): guided filter at sigma 1.75
828 -> 146 ms, colour-NR Gaussian 495 -> 104 ms. Full-size heavy-edit
export of the CC0 Sony ARW: GPU 3.6-4.0 s -> 1.9-2.0 s (CPU 1.9-2.1 s);
GPU-vs-CPU output distance unchanged.
Adds gaussian_matches_cpu (GPU blur vs lightcraft_raster::blur::gaussian,
1-3 channels, partial workgroups, windows wider than the image) and
per-pass rows to bench_kernels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AVIF HDR export takes hdrTransfer = pq | hlg (export dialog: HDR curve;
MCP export schema lists it). HLG writes CICP transfer 18 and no clli box.
The window logs whether it shows HDR; today always SDR (eframe configures
an 8-bit surface), with the platform / surface format / headroom / opt-out
rules unit-tested. Also satisfies clippy 1.99's chunks_exact_to_as_chunks
in spots.rs.
Part of #868.
Sources: ITU-R BT.2100 (HLG OETF, inverse OOTF with system gamma 1.2),
ITU-R BT.2408 (203 cd/m2 reference white at 75 % HLG); own design for
the display decision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
DNG export gains a "Lossy" compression: the raw data is demosaiced to linear
camera RGB (OpcodeList3 and the default crop applied), scaled to the resize long
edge when one is set, and stored as 8-bit baseline JPEG tiles at the export
quality. The codes follow a 1/2.2 power curve that a LinearizationTable maps
back to linear 16-bit values; colour matrices, white balance and the profile
look are kept. Available in the export dialog, `app.export`
(`dngCompression: "lossy"`) and the MCP export tool.
Also fixes a new clippy lint (chunks_exact_to_as_chunks) in pipeline/spots.rs
that fails `cargo xtask ci` on Rust 1.99.
Sources: Adobe DNG Specification 1.4+ (Compression 34892, LinearizationTable);
own design for the code curve.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
app.export takes destinationMode (specificFolder | sameAsOriginal); the
Export dialog shows it as "Export to". In sameAsOriginal mode each photo
is written into its original's folder plus the optional subfolder, with
the usual conflict policy and original guard per folder. A photo with no
folder on disk or whose folder is missing gets its own error. Saved in
presets and Export with Previous like the other options.
Fixes#956
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up. "A GPU device exists" does not mean the GPU draws the
view: soft proofs and the HDR range render on the CPU, and a LUT profile,
refined masks or a photo past a GPU limit fall back per render. Those drags
had become full-size CPU renders.
region::DragScale now picks the draft size when a drag starts and keeps it
until the drag ends (no resolution jump mid-gesture): full size where the
GPU draws the view (not soft-proofed, no HDR range) and no earlier drag of
the same control on this photo went over the 16 ms slider budget at full
size; 0.6 otherwise. Full-size drafts are timed as they land (the first,
rebuilding stage caches, is left out; the median decides).
ViewSizes carries one `draft: Option<f32>` instead of a scale and a flag
that could disagree. Tests: DragScale unit tests (budget, lock, hostile
times) and headless drags for a remembered slow drag and a soft-proofed
drag, each checked to fail without its guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slider drag rendered the loupe's whole frame at 0.6 of its size, so the
fit view went soft while dragging. On the GPU a canvas-sized frame takes a
few ms, so drags now render at full size there; the CPU keeps the 0.6 draft
(region::drag_scale, CPU_DRAFT_SCALE). The no-window path's canvas cap now
follows an explicit `drafting` flag instead of `scale < 1.0`, so a GPU drag
at 1:1 still never renders every pixel of the photo each frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photos that tie on the sort key (equal ratings, one import batch, a
burst within a second) now sort by file name before id, in
Catalog::query, Recently Added and auto-stack. Split out of #641.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All 38 files decode from sensor data and develop at full size (9520x6336 and
6336x9520) without error; mean CIE76 dE to the camera JPEG is 6.3-13.9 at 400 px.
The files are not committed and the evidence entry carries no path. Leica is now
14 verified / 47 unverified, the Leica bucket on a verified body 5% -> 21%; the
weighted shares (85% decode, 42% on a verified body) do not move.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On merge: the HDR histogram is applied in develop_reporting (#712 split develop), is skipped for
draft renders (a second render per drag tick), and HdrBins::bin clamps a hand-built split so it
can't underflow (test hdr_bins_with_any_split_stay_in_range).
photo.editExternal takes the selection (or ids, at most 50): each photo gets its own -Edit copy
stacked on its original, the copies are selected and returned in copies (path / id / original
still name the first). Photo ▸ Edit In opens each copy in the editor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
On merge: Lightroom's default profiles (Adobe Standard, Adobe Color), named by nearly every sidecar,
give no camera-profile warning; other profiles still do (test default_lightroom_profiles_are_not_reported).
The import help string keeps main's onDeleted wording and gains warnings.
On merge: the quick preview path orients like the whole-file path (preview_orientation with the
raw's header prefix, so a portrait raw whose embedded JPEG carries no orientation is not sideways),
and its doc links point at preview_window.