Commit Graph
1561 Commits
Author SHA1 Message Date
Michael Anderson 27d1e9b04d Never crash: Cap enlarged exports at 512 MP and check memory first
A huge enlargement failed to allocate and took the app down.
2026-10-13 10:45:39 -05:00
Michael Anderson 337f02b4dc Never crash: Fix export size panic for very long strips
Apply the 1 px and 65535 px bounds in turn instead of clamping between bounds that can cross.
2026-10-13 10:45:34 -05:00
Michael Anderson cef6f2d4b8 Never crash: Fix demosaic panic on a NaN black level
Read non-finite DNG black levels as 0 and map non-finite normalized samples to black.
2026-10-13 10:45:18 -05:00
Michael Anderson d5cba8b3dc xtask parity: Fail when docs/gaps.md lists a gap more than once
Also remove the duplicate rows for gaps 10, 22 and 23.
2026-10-13 10:45:11 -05:00
Michael Anderson 65dbba9ecd LRC-LIB-CATALOG-IMPORT: Open catalogs whose tables are all still in the WAL
Accept schema format 0 in the main header and check page 1 again after the WAL overlay.
2026-10-13 10:45:06 -05:00
PiMinusMax d64a1ecc93 LR-PROF-CAMERACOLOR: bundled Sony ILCA-99M2 camera profile
Fitted with `lightcraft-cli calibrate` to 144 usable ILCA-99M2 ARWs (of
150, seven shooting days, Standard creative style, DRO Auto) and their
in-camera JPEGs; aggregate colour statistics only.

On 30 withheld photos (profile fitted to the other 120) mean dE76 against
the camera JPEG goes 5.11 -> 4.87; 25 of 30 improve, the worst
regression is 0.24. All 150 photos stay accepted. The public raw.pixls.us
ILCA-99M2 file, not used for fitting, stays accepted (5.57 -> 5.73).

A corpus test checks that this public sample's fit keeps exactly the
built-in profile's matrix and table; the ILCE-7CR test now shares its
body. LOOK_VERSION 5 -> 6, as the profile changes the fit of this model.
2026-10-13 17:33:23 +02:00
Jason Baldwin bc9d8a21b5 Merge slideshow-templates into slideshow-export (origin/main: batches 32–33) 2026-10-13 17:03:26 +02:00
Jason Baldwin d747c9e0f6 Merge slideshow-titles into slideshow-templates (origin/main: batches 32–33) 2026-10-13 17:01:24 +02:00
Jason Baldwin f30e4af838 Merge slideshow-overlays into slideshow-titles (origin/main: batches 32–33) 2026-10-13 16:59:34 +02:00
Jason Baldwin ba9262a4a0 Merge slideshow-composer into slideshow-overlays (origin/main: batches 32–33) 2026-10-13 16:57:38 +02:00
Jason Baldwin a021ad2c56 Merge slideshow-playback into slideshow-composer (origin/main: batches 32–33) 2026-10-13 16:55:11 +02:00
Nathaniel LedfordandClaude Opus 5.5 2783e5de9b PERF-IMPORT: a scan, an import and a sidecar write no longer make a table of the whole library first
Setting up Synchronize Folder, an import review or an import made a table
of every photo in the library on the session's thread before the first
file was looked at, and naming one photo's sidecar worked out who owns
every shared sidecar name in the library. In a library of 3.5 million
photos (`scan_setup_bench`, release, a folder of 500): 5.8 s before a
Synchronize scan, 2.6 s before an import review, 1.7 s before an import,
2.4 s for each sidecar written.

- A scan keeps the library as it is (photos are shared, not copied) and
  looks up the files it finds (`library_files`) and the contents it probes
  (`library_hashes`) on its own thread.
- An import looks the library up with its first files
  (`ImportJob::look_up_library`), from the library and the options as they
  were when it was set up.
- The owners of shared sidecar names are worked out for the photos asked
  about (`StemOwners::among`): all five callers name only those photos.
- Synchronize Folder names its photos' sidecars in the scan, after it has
  seen that the folder is there.

Now 0.9 s, 0.05 s, 0.05 s and under 1 s. What is found, and how sidecars
are named, is unchanged: the tests hold the owners against the table of
the whole library as it was made before, and the key made without
allocating against the key, for every string of up to six of the
characters that decide it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:52:56 -04:00
Jason Baldwin 09ea6a3f77 Merge origin/main into slideshow-playback (painted toast from batch 33, kept at the top during a slideshow; both Slot variants; tracker rows) 2026-10-13 16:52:50 +02:00
MarekSpodBiedry d77d1a4650 MCP: do not resend a request whose reply was lost, report an unknown outcome (#1526) 2026-10-13 16:37:38 +02:00
MarekSpodBiedry 935fabeb18 Preview disk cache: ignore symlinked shard directories (#1504) 2026-10-13 16:36:39 +02:00
LosminekandClaude Opus 5.5 95cde76917 Merge upstream/main into cs-ui
Conflict in panels/dialogs.rs: keep upstream's PQ/HLG HDR curve choice
and route both labels through i18n::tr. Czech for the new strings:
8 catalog entries (import, missing files, GPU notice, mask overlays),
3 format strings (other editor, identical copies) and the HLG label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:12:20 +02:00
Jason Baldwin fdad5a4ad4 LR-VIEW-FOCUSPOINT: read focus points for photos already in the library, header-only in the background (#1480)
Photos imported before focus points were read got one only through Reload from Disk, which
hashes whole files on the UI thread. Now:

- lightcraft_meta::focus::read_header reads only what the decoder looks at: IFD0 (Make,
  Model), the Exif IFD, the maker note's IFD and the focus tags' values (a JPEG: its segments
  up to the Exif one), walked round by round and decoded with the whole-file code from a
  buffer of the file's length holding just those ranges. Bounded rounds, bytes (2 MB) and
  size (1 GB). One 32 KB read per α1 / A7S III ARW; on 109 real files (83 with a point)
  it gives the whole-file read's point for every one.
- A background job (crates/engine/src/focus_scan.rs) reads photos with no point from a
  supported body (by the stored camera; nothing else is opened), ARW or JPEG, one at a
  time in path order, with a "Reading focus points" activity row (Cancel::Flag). The UI's
  per-frame focus.pump gives it the face scan's pace (paused while dragging or typing) and
  starts it once per library opening. Commits every 200 photos or 3 s as system ops (no
  undo step, no sidecar write), only meta.focus, only on photos that still have none at the
  same path. "Looked, none" goes to focus-scanned.bin beside the library, headed by the
  decoder's tag so a decoder knowing more bodies starts afresh; unreadable files are retried
  at the next opening.
- photo.readFocusPoints {ids?, wait?} (Photo > Read Focus Points) runs it on demand.

Measured cold on a 5400 rpm USB drive: 200 α1 / A7S III ARWs in 1.9 s (~100 photos/s,
32 KB each), so 58,796 raws take about 10 minutes instead of re-reading 2.25 TB.
2026-10-13 16:06:06 +02:00
squareSphere29andCursor 6e20f82360 LR-LIB-STACK: deleting a stacked photo drops it from the stack so i/n badges update (fixes #1493)
Soft-delete and remove-folder now unstack in the same undo step as Recently Deleted, so leftover members show 1/2 not 3/3. Restore-from-trash does not restack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 21:02:42 +08:00
eliets2 c38bf96ef9 Docs: Select Only Active Photo in the shortcut list and the parity checklist
Add the Cmd+Shift+D row to the library shortcut table with a short note on
what it does and where it can be rebound, add a checklist row for it with
status partial (covered by headless key tests; the native macOS menu path
is not verified), and update the checklist's Last updated line, revision
history and the summary counts that this one row changes.
2026-10-13 15:42:56 +03:00
Fabio LussiandClaude Opus 5.5 e665aff1b3 docs: re-verify against 0.7.1
- All pages checked against the v0.7.1 tag and its release assets;
  verified: bumped to 0.7.1.
- Install: 32-bit Windows (x86) installer and portable build; pre-0.6
  upgrades keep their install folder.
- Import: Recently Deleted photos are restored by default; progress shows
  in the activity stack.
- Edit: HDR button next to B&W.
- Export: JPEG XL Effort and Progressive, lossy JPEG XL colour space
  note, Add to Apple Photos on macOS.
- Lightroom import: activity stack progress and stopping with its X.
- Raw troubleshooting: Adobe RGB fix now released, monochrome picture
  style tone, EOS R5 Mark II C-RAW decodes, Leica DNG and dark scenes
  still open; drop "import again" (duplicates are skipped).
- docs/user/CLAUDE.md is a regular file instead of a symlink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:34:54 +02:00
dev-null-undefinedandClaude 14e0d12813 LR-IMP-PREVIEW-READ: stand-ins and raw thumbnails decode the smallest embedded JPEG that covers them
`embedded_preview_srgb` (the loupe stand-in and the grid thumbnail of an unedited raw) decoded the
largest embedded JPEG whatever the size asked for: a 512 px thumbnail of a Sony A7R VI ARW decoded
its 9984 x 6656 JPEG (5.6 MB) although the file holds a 1616 x 1080 one (0.3 MB).

`lightcraft_raw::embedded_preview_covering(bytes, min_edge)` returns the smallest of a TIFF-based
raw's JPEGs whose long edge is at least `min_edge` and that is a copy of the largest one: aspect
ratio within 1 % (not a letterboxed thumbnail of another shape) and the same number of components
(not the grey semantic masks of an iPhone ProRAW DNG); else the largest, as `embedded_preview`.
The colour-space handling stays in `decode_raw_preview` (a `covering` argument); an unsupported
raw's editing source and the camera-look fit keep the largest JPEG, so a source never depends on
its size level.

On 120 raws of every make in the corpus at 256 / 512 / 1600 px: wherever the largest JPEG is still
chosen the output is identical byte for byte; where a smaller one is (Sony, Nikon, Panasonic,
Adobe DNG) orientation matches, the size is equal or 1-2 px off, mean difference <= 4.7 / 255.

- Rebased onto #1020: its plan route (`preview_window` -> `planned_preview`) now takes the box size and picks
  with `embedded_preview_covering` too, so a TIFF raw read through the sparse plan gets the whole file's JPEG.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 14:33:52 +02:00
nevicia2010-hub f86459db58 LR-MASK: fix range handle drags and color sampling settings 2026-10-13 14:29:16 +02:00
eliets2 939d422ff6 Docs: grid navigation keys, Cmd+O and Shift+H
Describe the new keys in the library shortcuts page, update the rows they
touch in the parity checklist and the UI parity page, and stop listing F6
and F7 as missing in the small-shortcuts gap now that they are bound.
2026-10-13 15:16:51 +03:00
Matthew James SivakandClaude Opus 5.5 eeb2ca2f6a LR-EDIT-SECTION-SOLO: register section.calibration, refuse unknown sections
Calibration's header runs section.calibration, now a registered command (no menu
item); section.<id> refuses an id the Edit panel hasn't got instead of closing
the real sections for a phantom one. "Solo Mode" sits with the other So… keys
in the catalogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:13:48 +02:00
Matthew James SivakandClaude Opus 5.5 12015179ea LR-EDIT-SECTION-SOLO: section.<id> {solo?} and header clicks run the command
Option-click goes through section.<id> {solo: true}, so agents can open just one
section; view.soloSections and section.* refuse flags that are not true / false
(bool_param); toggle_section reuses solo_section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:13:48 +02:00
Matthew James SivakandClaude Opus 5.5 d8f62a29b2 LR-EDIT-SECTION-SOLO: Solo Mode for Edit sections
Window > Edit Sections > Solo Mode (view.soloSections {on?}) and a right-click on
any section header turn it on: opening one section closes the others. Option-click
on a header opens just that section once. Kept with the UI state (singlePanel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:13:48 +02:00
HossamvsandCursor 8d6070e224 LR-LIB-PEOPLE: rename a person across the library (#1444)
People cards and the person page had no way to rename every face of one person as one undo step.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 15:00:24 +03:00
dev-null-undefinedandClaude 8c64c467f5 LR-EDIT-OPTICS-PROFILE: Sony lateral CA planes off by default (the demosaic already hides most of it)
Verified on 54 ILCE-7RM6 photos, the CA planes made the render worse: our
demosaic already hides about 70 % of the lens CA (the render shows 0.29x
the raw-CFA CA) while the post-demosaic warp moves R and B by 0.84x the
table, so the corner R/G and B/G CA went from rms 0.032 / max 0.125 px
(off) to 0.052 / 0.219 px (on) at 1440 px; only the 16-35 mm improved.

warp_opcode takes a lateral_ca flag; lens_opcodes passes false, so a Sony
ARW gets the one-plane distortion warp. The CA conversion stays live and
tested (FF and APS-C examples, both lossless corpus files' tables). The
follow-up is to warp the CFA colour planes before demosaic. The doc,
tracker (docs/parity-checklist.md), docs/gaps.md, docs/raw-parity.md and
whats-new say distortion applied, CA parsed and off
by default, vignetting not applied; the one-plane default costs 24 -> 45 ms
in the sample stage at 1920 px (three planes: 112 ms).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:19:44 +02:00
Jason Baldwin ac84cd31ee LR-VIEW-FOCUSPOINT: show the camera's focus point (Sony) and zoom to it (#1480)
Read the autofocus point from Sony maker notes (FocusLocation 0x2027, FocusFrameSize
0x2037, FocusMode 0x201b, AFAreaModeSetting 0x201c, AFTracking 0x2021 and the area mode
used from the enciphered 0x9402 block), for the ILCE-1 and ILCE-7SM3, whose layout was
checked against ExifTool's output on 333 real ARWs. Manual focus, centre-only Multi
reports and unchecked bodies give no point. The box is stored on the upright photo as
Meta.focus (omitted when unknown; no catalog format change), filled at import and by
Reload from Disk.

View > Show Focus Point (Alt+F) draws it in Detail, Compare and Survey through rotation
and crop; View > Zoom to Focus Point (on by default) makes Toggle Zoom centre on it; the
Info panel shows "AF-C · Wide · Animal Eye Tracking". photo.inspect returns meta.focus.
2026-10-13 13:14:24 +02:00
dev-null-undefinedandClaude 33c362a7e4 LR-EDIT-OPTICS-PROFILE: Sony lens corrections verified on the ILCE-7RM6; docs and tracker
docs/sony-ilce7rm6-lens-corrections.md records the three Sony tables, the
measured conventions with their residuals, the conversion to
WarpRectilinear and its fit gate, the model allowlist and how to extend
it, the measurement method, the verification and the limits.

Verified locally on 54 ILCE-7RM6 photos with four lenses. The corrected
render lines up with the camera JPEG within 0.679 px at 1440 px at the
corners and edge midpoints of the 33 measurable photos shot with
in-camera correction on (up to 17.3 px uncorrected). The 13 controls show
the model's own shift. Every fit passes the 0.25 px gate (margin 0.075).
Lateral CA stays within 0.3 px, but applied after demosaic it raises the
render's corner CA (rms 0.032 -> 0.052 px); the doc and tracker say so.

Its "Two conventions" section sets these conventions next to the
ILCE-7RM4A's (vendor::arw_lens, docs/sony-lens-corrections.md, #498):
on the 34 measurable ILCE-7RM6 photos that formula gives 1.429 / 0.319 px
(max / rms) against 0.596 / 0.143, failing only on the 24-70 GM II and
fitting the 100 mm and 200-600 better; no single unit fits every lens
under either, and what would settle it (Sony On/Off exports of a wide
zoom, more lenses). docs/sony-lens-corrections.md gains a pointer to it.

Tracker rows LR-EDIT-OPTICS-PROFILE (one note for both bodies) and
LR-IMP-CAMERA-COVERAGE in docs/parity-checklist.md, gap 8 in
docs/gaps.md, the lens-profile section of docs/raw-parity.md (lens score
unchanged), whats-new (lossless ARWs now; Compressed / Compressed HQ once
the ARW 6.0 decoder, #625, lands) and the colour-fit follow-up note are
updated.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:09:40 +02:00
dev-null-undefinedandClaude e7cd00ef56 M5.6: a zoomed view with Dehaze keeps its airlight proxy (each pan and slider step resampled the whole original)
A windowed render dehazes with the whole frame's airlight, estimated on a 384 px proxy of the frame
(`local::frame_airlight`). Every window asked for that proxy again, so every pan of a 1:1 view and every
slider step while zoomed sampled the whole original down to it on the CPU: ~200 ms of CPU on a 64 MP raw.

The proxy (taken before white balance) is now kept for the last 4 (source fingerprint, frame, size); white
balance, the dark channel and the percentile still run on it every time. Same pixels: the GPU window of
`render_bench` is byte-identical.

`optics::fingerprint` indexed the first pixel of an empty image; it now hashes only the dimensions then.

render_bench ONLY=window GPU_ONLY=1 on a 64 MP Sony ILCE-7RM6 raw (RTX 4070 Ti SUPER, 4-CPU quota), CPU time:
pan 208-212 -> 9-11 ms, exposure change 200-204 -> 4 ms, NR change 211-217 -> 13 ms.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:07:34 +02:00
Atmik Shetty 21861c2f14 LR-EDIT-DETAIL-NR: noise reduction works in the original's pixels, so previews match the full-size render scaled down (fixes #960)
Grid thumbnails looked much more denoised than the loupe. `nr_params` sized
the radii from the output's scale relative to the source buffer it was given,
not the original: a 256-px cell drawn from the 512-px thumbnail level used
half the full-size radius on pixels that each average ~23 sensor pixels, and
below 0.4x (0.35x for colour) the radius stopped shrinking at all. The
guided filter's eps (the variance treated as noise) also stayed at its
full-size value although downscaling divides the noise variance by 1/scale²,
so texture in small previews was flattened as noise.

- `SourceInfo::native_long` (filled by the engine from the catalog's photo
  size, kept when the decoder supplies its own facts, carried to web
  workers) and `Plan::detail_scale` = output pixels per original pixel
  (from `px_per_long`, so a crop no longer changes it).
- `nr_params(s, scale)`: radii x scale with no floor, eps x scale². Below
  1 px the smallest blur the box Gaussian makes is blended in by the ratio
  of variances (`blur::box_sigma`); a fade under 1% skips the filter.
  At scale 1 the parameters are unchanged: 1:1 views and full-size exports
  render as before. The GPU takes the same parameters.
- RENDER_CACHE_VERSION 63: cached thumbnails are redrawn.

Measured on a 1536x1024 noisy synthetic scene (tests_nr_scale.rs), error
of the preview against the full-size render scaled down, as a fraction of
leaving NR off, luminance NR 50 at preview scales 0.5 / 0.33 / 0.25 /
0.167 / 0.1 / 0.05: before 0.53 / 0.70 / 1.04 / 2.26 / 3.87 / 6.07, after
0.28 / 0.37 / 0.66 / 0.90 / 0.99 / 1.00. A 256-px render from the 512-px
level now matches one from the original (mean 0.07 LSB vs 0.71 before).
2026-10-13 16:14:19 +05:30
Atmik Shetty 25e832b404 KEYC-COMPARE: = and - make the grid thumbnails larger and smaller
View ▸ Increase / Decrease Grid Thumbnail Size (view.thumbSizeUp / view.thumbSizeDown)
step the bottom-bar slider by 30 pt within its 90–480 range in Photo Grid, Square
Grid and People, as Lightroom Classic's Library Grid keys do; + also enlarges.
Detail, Compare, Survey and Reference ignore the keys, text fields keep them, and
⌘= / ⌘− still zoom the photo. The slider's range is now shared constants.
2026-10-13 16:11:47 +05:30
Atmik Shetty db69e288c8 KEYC-RATING: backtick cycles the flag; Cmd+Up / Cmd+Down raise / lower it
photo.cycleFlag steps each selected photo's own flag unflagged -> pick ->
reject -> unflagged; photo.increaseFlag / photo.decreaseFlag move it up or
down reject < unflagged < pick without wrapping. One batch undo step, Compare
and Survey act on the active photo, Auto Advance follows, text fields keep
the key. Shown in Photo > Set Flag and Help > Keyboard Shortcuts.
2026-10-13 16:11:45 +05:30
Markus FriedelandClaude Opus 5.5 8907f5f56f LR-VIEW-PHOTOGRID: a mouse-wheel notch scrolls a third of the grid or filmstrip (#1481)
One notch scrolls the photo grid by a third of its visible height and the
filmstrip by a third of its width, as in Lightroom Classic, instead of the fixed
100 points a side panel scrolls. It jumps at once instead of gliding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:38:21 +02:00
Claude 951d52788a LR-MERGE-HDR: pixels next to clipped ones don't tint HDR merges
Measured against the darkest frame on real ARW brackets, each frame is linear to 1 % up to the clip far from
clipped pixels, but within 2 px of them the +2 EV frame's green reads 3–5 % low: demosaicing interpolates it from
clipped photosites. That frame carries the most weight (k = 4) and those pixels were unclipped themselves, so they
kept it, and bright windows took a magenta tinge.

A frame's weight now goes by its brightest channel within 1 px (3 × 3; wider margins changed nothing more). On two
real interior brackets (ILCE-7M3, 0 / −2 / +2 EV), bright pixels whose green against red and blue is over 10 % below
the darkest frame's, in camera RGB: dining room 0.32 % → 0.08 %, kitchen 2.86 % → 0.28 %. Rendered at −3 EV with
main's colour tags (#1123), window pixels with G < 0.85·min(R, B) go 50 → 3 and 16 → 10 (the darkest frame alone:
2 and 205); most of the visible magenta those brackets showed before came from the missing colour tags. Test: a
bracket whose +2 EV frame has 5 % less green next to its clipped pixels merges without the deficit (fails before:
−1.96 %).

Docs: docs/merge.md, the LR-MERGE-HDR row, docs/whats-new.md (also for the frame-order fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gRtdWF1E9358FfzhzYqCS
2026-10-13 09:51:11 +00:00
eliets2 0cd7513a8d LR-MERGE-HDR: an HDR merge of raws keeps the as-shot white balance of its inputs (#1144)
The HDR merge has the same cause as the panorama: its result took the
reference frame's colour tags, and a raw whose white balance is only in the
vendor's multipliers (CR2, NEF, ARW...) has no AsShotNeutral in them.

merge_hdr now takes the median multipliers of all its inputs (the rule of
consensus_multipliers: the mean of the middle two for an even count; missing,
non-finite and non-positive values do not vote) and sets the result's
AsShotNeutral to their reciprocal, as stitch does. With no multipliers (DNG
sources) the reference frame's tags are kept, as before. HdrResult carries the
multipliers so that the brackets of an HDR panorama vote as frames of their own
when the merged brackets are stitched (the engine passes them on).

In the app and the CLI the neutral from starting_colour_tags (#965) is already
there for frames balanced alike, so those renders do not change.

Tests: an HDR merge of raw-style frames gets the median AsShotNeutral (odd and
even counts, missing and unusable multipliers ignored) and the written DNG says
it; without multipliers the reference frame's colour is unchanged.
2026-10-13 12:23:36 +03:00
eliets2 25314aaacf LR-MERGE-PANO: a panorama of raws keeps the as-shot white balance of its frames (#1144)
A raw whose white balance is only in the vendor's multipliers (CR2, NEF, ARW...)
has no AsShotNeutral of its own. Loading a frame dropped the multipliers and
stitch copied the first frame's colour tags, so a panorama of frames that reach
the stitcher without a neutral wrote a DNG with neither AsShotNeutral nor
AsShotWhiteXY, and a viewer had no white to start from (a strong colour cast
until White Balance > Auto).

Frames now carry the multipliers (Frame::wb_multipliers, filled from the decoded
raw; load_frame_with is split so a decoded raw can be tested without a file).
stitch takes the median R/G and B/G of the multipliers of the frames that are in
the panorama (the mean of the middle two for an even count; frames without
multipliers, or with non-finite or non-positive ones, do not vote) and sets the
result's AsShotNeutral to their reciprocal, green = 1. The source's
AsShotWhiteXY is dropped with it: a reader takes it before the neutral and the
DNG spec makes the two exclusive. With no multipliers anywhere (DNG sources) the
first frame's tags are kept, as before.

In the app and the CLI a raw without colour matrices already gets a neutral from
starting_colour_tags (#965). For frames balanced alike the new neutral renders
the same (checked in the engine crate); it differs when the camera balanced the
frames differently: the panorama follows the median instead of the first frame.

Tests: the frame carries the multipliers; the consensus for odd and even counts,
with missing and unusable values; stitching raw-style frames gives the median
AsShotNeutral, ignores frames outside the panorama and leaves the colour as it
was without multipliers; the written DNG, read back with the raw reader, has the
reciprocal AsShotNeutral and the white balance the source's multipliers give.
2026-10-13 12:23:36 +03:00
Claude 5cb57ef4ea LR-PROF-CAMERACOLOR: Canon EOS R7 / R6 Mark II calibrate measurements
Held-out and library-wide calibrate runs on real CR3s (calibrate's CR3 /
CR2 input is #1073): profiles change little (R7 2.52 -> 2.57, R6 Mark II
6.12 -> 6.09 mean dE), so none is bundled; notes the CRX adaptive QP
172/175 files that stay preview-only. The catalog test pins that an EOS
R6 Mark II (Exif "Canon EOS R6m2") edits in Kelvin through its measured
matrices and an EOS R7 stays relative.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzWyUn77ZWLe3q3qMiQbVt
2026-10-13 09:10:47 +00:00
cookingwithsimonandClaude Opus 5.5 d9cea1404a LR-IMP-PROFILES: presets apply the imported profile they name (#1001)
A Lightroom preset's crs:Look (name, group, UUID, amount; the Look table
in .lrtemplate files) is kept as profile.look and resolved against the
user's imported profiles by name, the group breaking ties. Lightroom's six
basic profiles map to ours; its own creative/camera profiles and profiles
not imported yet are reported per preset. References are resolved again
after profile.import / profile.deleteImported, so profiles and presets can
be imported in either order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FxP2xEy623xzWGqCWXVwtb
2026-10-13 09:08:53 +00:00
draco892 eec4746a56 build: automatically include craft-fonts in desktop packages
Detect ../craft-fonts or use CRAFT_FONTS_DIR on macOS, Linux and Windows.
Download the pinned release revision when the checkout is missing
and require Chinese and Japanese fonts during the build.

Document font setup and preserve skip-build behavior.
2026-10-13 11:01:40 +02:00
PiMinusMaxandClaude Opus 5.5 b36b0d14b7 KEY-SLIDER-NUDGE: scrolling sideways over a slider adjusts it
A trackpad swipe, a tilting wheel or Shift+wheel over a Develop slider nudges it by the
wheel/arrow-key steps, without selecting it first (the right panel only scrolls vertically).
Only a mostly sideways movement counts, so scrolling the panel leaves the sliders alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:10:24 +02:00
dev-null-undefinedandClaude bc1f138ee3 Perf: ARW6 decode another 20-34 % faster, same samples
- The two halves of each stream are independent bit streams: they now decode in parallel
  (`decode_stream`) instead of one after the other inside each of a tile's 13 streams.
- The vertical inverse 5/3 runs its rows in parallel. The horizontal pass lifts each row in place
  (`lift_row`, scratch space reused per thread), rows in parallel, instead of allocating two vectors
  per row and copying the result back; a test checks it against the vertical pass, row by row.

Best of 3 on 4 CPUs, previous and new decoder alternating: ILCE-7RM6 FF HQ 0.60 → 0.48 s
(process CPU 1.14 → 0.87 s), FF Compressed 0.62 → 0.48 s, APS-C HQ 0.34 → 0.23 s, APS-C
Compressed 0.31 → 0.24 s (docs/arw6-compression.md → Timings). The decoded mosaic is identical
(hash of every sample) on the 8 ARW6 files of the RawDB ILCE-7RM6 set and 3 private A7R VI photos.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 10:00:33 +02:00
dev-null-undefinedandClaude Opus 5.5 79ab54e167 Perf: ARW6 decode 20-33 % faster, same samples
- The vertical inverse 5/3 lifts whole rows at once (`inverse_lift` with samples `w` values wide)
  instead of gathering each column through a strided iterator and scattering it back: the same
  integer arithmetic, sequential memory. The horizontal pass is the same function with w = 1.
- `0^n 1` codes are counted with one `leading_zeros` over the next MAX_DPT + 1 bits instead of bit by
  bit (same bits consumed, same errors); each line reserves its values up front.
- Colour reconstruction reads plane rows as slices and runs over rows in parallel (it was one
  thread per tile: 4 threads on a full-frame file whatever the machine).

Best of 5 on 4 CPUs, previous and new decoder alternating: ILCE-7RM6 FF HQ 0.56 → 0.46 s,
FF Compressed 0.54 → 0.38 s, APS-C HQ 0.34 → 0.23 s, APS-C Compressed 0.35 → 0.24 s
(docs/arw6-compression.md → Timings). The decoded mosaic is identical (hash of every sample) on
the 8 ARW6 corpus files and 2 private full-frame photos (ISO 100, ISO 12800); the pinned corpus
sums still match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:00:33 +02:00
eliets2 be592cf2b1 LR-IMP-FORMATS: CI-enforce the unsupported CRX variants, listed in docs/cr3.md
docs/cr3.md gains a variant-status table: every supported and rejected CRX
coding variant, the function that gates it and the exact Unsupported text.

crates/raw/tests/cr3_variants.rs enforces the rejections (19 tests). Each
rejected variant is rebuilt as a small synthetic CR3 (container, CMP1
descriptor, CRX stream) and the test asserts the exact string, and that
probe_info (the import path) and decode return the same error. Control
fixtures decode through the same container, including both edges of the
accepted uniform quantization range (4..=44) and adaptive QP range
(131..=168), so a rejection can only come from the documented gate. The
table itself is prose and is not checked against the tests.

The doc said only QP values 131-167 were accepted; the decoder also accepts
168 (quantization value 44, step 102), so that sentence now says so.

Tests and docs only; no decoder change.
2026-10-13 10:56:29 +03:00
Atmik Shetty cb8b797cd4 KEY-SLIDER-RESET: a slider's name and thumb say a double-click resets it (fixes #1436)
A double-click on a slider's name or thumb already reset it, but nothing said so: only the value had a
tooltip. Resting the pointer on the name or on the thumb now shows "Exposure — double-click to reset
it", the wording (and translations) the curve channels already use. The rest of the track, which is
clicked and dragged, gets no tooltip, and the value keeps "Click to type a value".

The first click of a double-click on the thumb moved the value to where the pointer landed, a few
points off its centre: one history step, then the reset another. A click on the thumb now leaves the
value alone, so the reset is one undo step, as from the name. A disabled slider's name no longer
resets it.

No per-slider reset button: Lightroom has none, and the double-click it documents is now shown where
it works. Tests: tests_slider_reset.rs (name and thumb reset Exposure and Contrast in one undo step,
the hints, a click on the thumb keeps the value, typing a value still works) and
widgets/tests_slider_reset.rs (a disabled slider's name).
2026-10-13 13:16:08 +05:30
Atmik Shetty 64ccc20853 KEY-HIDEPINS: Tool Overlay modes (Auto / Always / Selected / Never) for spot and mask pins, Shift+H cycles them (fixes #224)
The Remove tool's spot pins (Remove / Content-Aware / Heal / Clone) and the
mask pins now follow a Tool Overlay mode, saved with the UI state:

- Auto (default): shown while the pointer is over the photo (or a pin is
  dragged), hidden while a new spot or brush stroke is painted
- Always: all of them
- Selected: only the selected spot's or mask's
- Never: none; the tool still works and a click paints a new spot

A hidden spot can't be grabbed. Mask pins also need Show Pins
(view.maskPins), which keeps its meaning and reply.

Command view.toolOverlay {mode?: auto|always|selected|never} (no mode steps
to the next), Shift+H (H stays Remove), View > Tool Overlay with the modes
and the cycle item, and a Tool Overlay dropdown in the Remove and Masking
panels. Headless tests cover each mode with the pointer over and away from
the photo, grabbing, the key, the command, the menu, the dropdown, mask pins
with Show Pins, and the ui.json round trip.
2026-10-13 13:10:57 +05:30
wstockandClaude Fable 5.1 e935ad75e2 LR-VIEW-COMPARE: ⌘/Ctrl- and Shift-clicks on the filmstrip select in Compare
In Compare a filmstrip click goes to `compare.set` (#747), so the grid's
modifier clicks never reached `library.select` and multi-select from the
filmstrip was impossible there (#187 follow-up). A ⌘/Ctrl-click now toggles
the photo and a Shift-click extends from the active photo, as in the grid;
the two photos shown stay selected and the active photo is unchanged, so a
rating key still acts on a photo that is on screen. A plain click still
replaces the active side.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NH3itd85jPuM6wNjgcGfEH
2026-10-13 07:22:42 +00:00
Marco Arnulfo c10ca4e958 LR-IMP-PREVIEW-READ: a raw whose embedded preview can't be read still gets a thumbnail (fixes #1448)
The grid queues no render behind an unedited raw's quick thumbnail, since the embedded JPEG is
meant to be the thumbnail. But whether that JPEG can be read is only known when the quick job
runs, and `quick_thumb_job` had no fallback: when the preview loader returned nothing the job
failed with "no quick preview", the failure was remembered for that key, and the cell stayed
empty until an edit changed it. The quick job now carries the real thumbnail job as its last
resort, as `quick_view_job` already does for the loupe.
2026-10-13 09:01:48 +02:00
smposaraandClaude Opus 5.5 26df1c5cdf LR-IMP-CAMERA-COVERAGE: verify Canon EOS RP CR3 pixel for pixel
Four CC0 EOS RP samples from raw.pixls.us (lossless RAW and C-RAW, full
frame and 1.6x crop mode) now have exact full-sensor regressions against
the DNGLab 0.8.0 black-box oracle (Windows binary, checked first against
the documented M50 RAW hash). The decoder already matched; no decoder
change was needed. Crop mode is a single 4032x2656 tile, full frame two
3192-wide tiles.

docs/cr3.md also records that the white level is Canon's specular white,
which at the -1/3-stop ISOs (e.g. 11062 at ISO 5000) is well below the
sensor's saturation point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:59:30 +02:00