mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-13 09:20:07 -06:00
baf079f659c4df026b31ffbbf5a8a5bf380b332c
5072
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
baf079f659 |
LR-BEHAV-RENDER-FIDELITY: process V5 rolls raw tone curves off into white in stops, so raising Exposure keeps highlights (#1632) (#3783)
* LR-BEHAV-RENDER-FIDELITY: V3 camera tone curve rolls off in stops past its last knot (#1632) A raw's camera tone curve is fitted only to camera JPEG tones below 0.85, so its last knot sits 1 to 3 stops below the sensor's white. Past it, CameraTone::apply closes on white exponentially in linear light: the sensor's white renders at 8-bit 246-254 at Exposure 0 and 254-255 from +1, and raising Exposure turns the top stop the sensor recorded white. Process V3 extends the curve with CameraTone::apply_rolloff: same value and slope at the knot, then gap / (1 + c (2^t - 1)) at t stops past it, the gap halving every stop in the end. On 11 CC0 corpus files with a fitted look, unclipped pixels rendering white drop from up to 22.5% to 0 at +1 EV and from up to 35% to 0 at +2 EV; Exposure 0 renders change by at most 1 code on average. V1 and V2, the fit itself, JPEGs and raws without a fitted look are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: V3 generic raw curve rolls off in stops too; DRO-off, DNG and GPU checks (#1632) Raws without a camera curve take ToneMap::hdr, which reached white about 1/4 stop past the sensor's white and clipped there (o.min(1.0)). Process V3 now uses ToneMap::rolloff_hdr: from half a stop below the white point (tone::GENERIC_KNEE_STOPS) the extended Reinhard curve hands over to the same stop-based shoulder as the camera curve (tone::rolloff_past, shared with CameraTone::apply_rolloff), with the same value and slope at the join. The sensor's white renders at about 235 / 248 / 252 at Exposure 0 / +1 / +2 where V1 and V2 render 239 / 255 / 255. On 6 CC0 DNGs on the generic curve, unclipped pixels rendering white drop from up to 8.4% to 0 at +1 EV and from up to 17.2% to 0 at +2 EV. The DRO-off gate (look_fit::at_most) also checks the V3 shoulder; the rate bound already implies it, so no fit changes and LOOK_VERSION stays. Tests: the generic shoulder (join, slope, monotone, below white, no kink), a regression test that fails with the clipping curve, a DNG ProfileToneCurve under V3, the DRO-off property tests on the V3 shoulder, and a per-process camera-curve GPU check. Docs: process-versions, parity checklist (LR-BEHAV-RENDER-FIDELITY, LRC-DEV-PROCESS), gap 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: what's new: process 3 keeps raw highlights when Exposure is raised (#1632) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: a flat last camera-curve segment joins the V3 shoulder with its own slope (#1632) The V3 camera shoulder took the last segment's slope with a floor of 0.1, so a flat or shallow segment (valid for a fitted, DNG or saved curve) joined with a kink: slope 0 on the curve, about 0.1 just past it. The shoulder now keeps the segment's own slope at the knot. Below 0.1 a quadratic term in 2^t - 1 joins the denominator: it adds no slope at the knot and makes up the 0.1 pace further up, so a flat curve still closes on white. Segments at 0.1 or steeper are byte-identical, and the generic curve passes no floor. Comments now say the shoulder approaches white in real numbers and can round to it in f32. Found in an independent review of the branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: V4's GPU test pins V4; the corpus DRO photos keep their knots under the V5 bound (#1632) The GPU light-operator test assumed the default process is V4; it now sets V4, since V5 is the default. The corpus DRO test checks that dro_off's V5 bound leaves the second-last knot where the rate bound puts it on the Sony DRO samples, so their V1 to V4 renders don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: the DRO-off curve is main's again, so V1 to V4 keep it (#1632) CodeRabbit found that the V5 bound in `dro_off` could move the second-last knot of a DRO photo whose fitted last segment is flat, and V1 to V4 read that curve too. `dro_off` and `at_most` are back to main's, so the camera-look fit is unchanged and LOOK_VERSION stays. The DRO tests check the V5 shoulder where the lowered segment's slope over its squared gap is at most the fitted one's (then it stays below), and on the Sony DRO corpus samples. Also: `rolloff_hdr`'s doc names the HDR lift, and the release note names DNGs whose curve already reaches white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5981ecadbb |
Issue #2165: a Lightroom collection that can't nest goes under the nearest set instead of failing the import (#3145)
* Issue #2165: place a Lightroom collection that can't nest where it is under the nearest set Lightroom Classic keeps a saved print, book, slideshow or web gallery inside a plain collection, but a LightCraft album nests only in a folder. The importer passed that collection as the album's parent, AddAlbum rejected it with "parent must be an existing folder", and the atomic batch rolled back every album and every catalog-only photo (#2165: photos but no collections; #2532: nothing at all). An album whose Lightroom parent isn't a collection set (plain, smart, a saved creation, system-only or missing) now joins the nearest set above it, or the top level, and the import report names it under warnings. Before each AddAlbum the parent is checked against the catalog's rule (an existing folder, or one added earlier in the batch), so no album can fail the batch. Builds on #2168 by @endthestart. Fixes #2165, fixes #2532. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Issue #2165: a Lightroom collection never silently loses its album; warnings name the set as the library does - Two collections never share an album. An album id the counter hands out that is already taken (the counter saturates, so a damaged one repeats) stops the import with an error naming the collection, as AddAlbum's "album exists" did before; it no longer drops the second collection and its photos while the report counts it. A collection listed twice imports once when both rows are the same and stops the import when they differ. An index that names one existing album for two collections gives the second an album of its own. - A relocation warning names the destination set as this library calls it (a set renamed after an earlier import), and a set whose earlier album isn't a folder is described as such, not as "not a collection set". - The staged-album set is no longer called `added`, which #3142 uses for its photo count in this function. - Tests: relocated re-import, a user-moved album on re-import, a renamed destination set, a set album that isn't a folder, sets nested under a plain collection, an exhausted album counter, duplicate collection rows and an index naming one album twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(lightroom): name the Lightroom collection id in relocation warnings (#2165) A collection imported somewhere other than where Lightroom keeps it now ends its warning with "(Lightroom collection N)", so two collections that share a name can be told apart, as #2165 asked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(lightroom): a collection listed twice costs only the duplicate (#2165) A different collection under the id of an earlier one stopped the whole import. Like any bad Lightroom record it now costs only itself (#1588): the first row of each id is kept, in catalog order, and a different one is left out with a warning naming both; the photos listed under that id go to the first. A row repeated as it is still drops quietly. The warning reads as the one #3152 gives, so the import behaves the same whichever of the two lands first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(lightroom): the nearest-set search ends on a parent loop or a deep chain (#2165) `collection_parent` follows a collection's parents to the nearest set with a seen-set and a 64-level cap. `validate` refuses loops before it runs, so no import test reached that guard: a unit test now gives it A in B in A, a collection in itself, a child of that loop (with and without a set in it) and a 100-deep chain of plain collections. The duplicate-collection test also checks that an exact repeat gives no warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1ff2211cd4 |
catalog: resync finds the retry record from the end, not every marker (#2124) (#2179)
* catalog: resync finds the retry record from the end, not every marker (#2124) A damaged log line made only of the record-start marker repeated N times cost O(N^2): resync scanned every occurrence of the marker and ran a from_utf8 + decode_record for each, so a 2 MB line (~300k markers) hung library open effectively forever. Only the retry record can follow a torn fragment (it re-encodes the same ops with the same seqs), so resync now takes the LAST occurrence of the marker and requires a fragment to precede it; the seq <= last + 1 gate and the CRC check are unchanged. Adds a regression test: a 300k-marker line followed by a valid retried record opens in well under a second and still recovers the record (torn_fragments == 1). * catalog: drop unneeded mut bindings in the resync regression test * catalog: count the start markers resync decodes instead of timing the cost test The cost test asserted a one-second wall-clock limit, which a slow worker can miss without any regression. The test now counts the start markers that resync decodes on the hostile line, through a counter that exists only in test builds, and asserts that at most the last one is decoded. The production path is unchanged. * Repair review branch integration * Address review for pull request 2179 * Format the files this pull request changes * Repair review branch integration * Address review for pull request 2179 * Format the files this pull request changes * catalog: pin recovery of a rules retry with nested seq values on the final line A failed append can leave its fragment glued to a CRC-valid SetAlbumRules retry whose rules hold several nested {"seq":1} values, and the damaged line can be the log's last. Picking the last raw {"seq": occurrence as the record start rejects the whole record and truncates the final line, losing the valid operation (issue #2124). The regression opens such a log and asserts the operation is replayed and the log file is preserved. * catalog: bound the resync scan and require the record header on candidates Every header-shaped marker in a damaged line still became a resync candidate, and each attempt decodes the rest of the line, so a crafted final line of many {"seq":N,"crc":M,"op": markers made Journal::open spend quadratic work on one load (CWE-407). Two changes: - a candidate must now match the full record header shape, digits after {"seq": and after ,"crc": and a trailing ,"op":, so nested markers (a rule value such as {"seq":1}) and junk never reach decode_record; - a per-line budget of 64 MiB of candidate suffixes, checked with checked_add, stops the scan once it is spent; the line then reads as damage, which open handles safely. The new regression stuffs a final line with 5000 header-shaped markers and asserts a bounded number of candidate decodes via the existing counter (not timing), that open still succeeds with the line dropped as a torn tail, and that a CRC-valid retry after the junk is still recovered. --------- Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com> |
||
|
|
d5959bc65f |
Report the lens database contents and missing XML files in the log (#3809)
The reporter could not tell whether the lens database had been found, nor why a lens name did not match, because the load path is silent on success; matching itself is already case-insensitive (tokens() lowercases both sides in crates/lensdb/src/lib.rs:219), so the letter-perfection complaint does not reproduce in this tree, but the missing diagnostics do. database() now logs the database directory, the number of XML files read, the lens and camera counts, warns when the directory holds no XML files or yields no profiles, and notes the lens name when no profile matches it; a small pure helper formats that summary so it can be asserted in a unit test. Test: lens_database_summary_reports_files_and_profiles failed before the change and passes after. Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com> |
||
|
|
b1dc4d5093 |
Test Create Preset name box restores on escape, keeps text on return (#3773)
The Create Preset name box has no automation id and no Esc restore today, so neither behavior exists or can be verified; the test pins the missing field:presetName registration and the Escape/Return behavior using only existing control-channel requests. Opens the Create Preset dialog via dialog.createPreset, asserts field:presetName is registered in the widget registry, types into the field with ui.text, checks Return keeps the text and Escape dismisses the dialog without saving. Test: create_preset_name_box_restores_on_escape_and_keeps_its_text_on_return failed before the change and passes after. Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com> |
||
|
|
dc0dc59a90 |
Keep the Vulkan driver unloaded on Windows unless it is asked for (#3667)
Windows already defaults to DX12 alone everywhere else (default_compute_backends, default_window_backends), but the window's fallback chain still probes Vulkan, and a persisted renderer name can promote it ahead of DX12. That is exactly the first-launch/second-launch asymmetry reported: the Vulkan driver is only touched on the later start, where it crashes the process before any window appears. Treat Vulkan as opt-in on Windows in `window_renderer_order`: drop it from the platform chain and ignore a remembered or failed Vulkan renderer unless the environment explicitly forces it (`LIGHTCRAFT_GPU_BACKEND=vulkan` / `WGPU_BACKEND=vulkan`). The existing `window_renderer_fallback_order` expectations for Windows have to be updated with it. Test: windows_never_uses_vulkan_unless_it_is_asked_for failed before the change and passes after. Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com> |
||
|
|
5cd7eb107e |
CI: Windows leg for pull requests that change Windows code, or labelled full-ci (#3793)
* CI: Windows leg for pull requests that change Windows code, or labelled full-ci * ci-windows: also run on manifest and Cargo.lock changes * deps: craft-update with flate2's miniz backend (fixes larger-than-raw ZIP TIFFs on Windows) craft-update's zip dependency used the `deflate` feature, which pulls in flate2's zlib-rs backend. Cargo unifies flate2's features across the app, so on Windows the whole of Lightcraft used zlib-rs, whose level 1 (the TIFF exporter's DeflateLevel::Fast) is a static-Huffman mode that can make a 16-bit ZIP TIFF larger than the uncompressed one. storytold/craft-libs#608 switches craft-update to `deflate-flate2` (miniz_oxide), the backend Linux and macOS already use. Only craft-update's rev moves (c5a1cbf -> a231e51); the other craft-libs crates stay where they are. * ci-windows: cover lightcraft-cli and menus/files.rs (cfg(windows) code) |
||
|
|
4b5dc8b90a |
Merge pull request #3591 from helenkwok/page-host-after-2315
feat(web): page host mode, so a page can host LightCraft in its own document (builds on #2315) |
||
|
|
7ed37fd515 |
Merge pull request #2315 from francoisgn/feat/issue-2270-web-host-bridge
#2270: a host bridge for pages framing the web build (?host=parent) |
||
|
|
071cef2c9c |
deps: craft-update with flate2's miniz backend (fixes larger-than-raw ZIP TIFFs on Windows) (#3800)
craft-update's zip dependency used the `deflate` feature, which pulls in flate2's zlib-rs backend. Cargo unifies flate2's features across the app, so on Windows the whole of Lightcraft used zlib-rs, whose level 1 (the TIFF exporter's DeflateLevel::Fast) is a static-Huffman mode that can make a 16-bit ZIP TIFF larger than the uncompressed one. storytold/craft-libs#608 switches craft-update to `deflate-flate2` (miniz_oxide), the backend Linux and macOS already use. Only craft-update's rev moves (c5a1cbf -> a231e51); the other craft-libs crates stay where they are. |
||
|
|
326b48286e |
Merge the host bridge's cross-origin check into page mode
Bring #2315's current head onto the page-mode branch, including the check that a cross-origin parent gets no bridge. ?host=parent wins when a page also sets data-host="page". A page-mode event is typed by its name, listeners are Fn, and dirty state is recorded before the synchronous send. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d73cf00ca8 |
Merge main into the host bridge
Keep the current changelog and record the embed under Unreleased. A cross-origin parent gets no bridge, so exports keep downloading; an opaque origin still gets none. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
eab130ecee |
Docs: drop the Last updated / Change header line that every PR conflicted on (#3790)
* Docs: drop the Last updated / Change header line that every PR conflicted on * xtask parity: the header's Changes field holds only the revision-history link; name the kept fields * xtask parity: reject a field after the header's Changes link |
||
|
|
d58b4d6723 |
Merge pull request #3794 from FredApps/ci/gate-timeout
CI: stop a hung gate at 80 min with a process list |
||
|
|
ffb79a6c03 |
CI: keep the gate timeout at 80 min (30 cuts off green runs)
Reverts
|
||
|
|
0b5c6ff46a | CI: stop a hung gate at 30 min | ||
|
|
5b4eb69242 |
xtask ci: --no-fail-fast test runs; flaky-test policy (#3791)
* xtask ci: --no-fail-fast test runs; flaky-test policy * Flaky-test policy: doctest quarantine and nextest's SLOW lines |
||
|
|
22399df614 | Merge remote-tracking branch 'upstream/main' into ci/gate-timeout | ||
|
|
4f5b3a3fcc |
CI: gate timeout back to 80 min (a green run takes about 40)
Reverts
|
||
|
|
db60ab4008 | CI: stop a hung gate at 30 min | ||
|
|
272c255f29 |
Merge pull request #2857 from insideTheFlux/fix/2674-stored-frame-regions
LR-LIB-PEOPLE: face boxes drawn on a photo as stored land on the upright photo (#2674) |
||
|
|
b79f9971a3 |
GPU tests allow a level of 8-bit dither (#3789)
The CPU tests got this in #3779. These only run with a real GPU, so hosted CI never showed them failing. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1d82c72c6d | CI: stop a hung gate at 80 min with a process list (gate-timeout-minutes) | ||
|
|
df79328e58 |
Merge origin/main into fix/2674-stored-frame-regions
Keep face-box (#2674) and main (monitor profile / denoise) entries in the parity checklist change line. |
||
|
|
bf96a5fa79 |
Merge pull request #2439 from insideTheFlux/feat/windows-monitor-profile
LR-PREF-DISPLAY: on Windows, previews follow the monitor's colour profile |
||
|
|
aa53064de6 |
Merge pull request #3650 from insideTheFlux/fix/3381-title-bar
LR-BEHAV-TITLEBAR: the Windows title bar takes LightCraft's theme at launch (#3381) |
||
|
|
1a39b60344 |
Merge origin/main into fix/2674-stored-frame-regions
Keep both the denoise and face-box (#2674) entries in the parity checklist change line. |
||
|
|
2bc668e42a | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
3438a00d31 | style: rustfmt for MEF orientation regression test | ||
|
|
a220d3eb68 |
LR-EDIT-DETAIL-DENOISE: built-in BM3D denoising with GPU acceleration (#2982)
* LR-EDIT-DETAIL-DENOISE: AI Denoise works out of the box with a built-in denoiser Turning AI Denoise on no longer needs an ONNX model and a denoise-model.json. Without a model of the user's own, the engine runs a built-in pure-Rust denoiser (noise measured per tile, non-local means on the Bayer planes, edge-directed demosaic) through the same tiling, cache, queue, preview and export path as a model. Installing a model stays optional; Stop using returns to the built-in one, and the id `builtin` is reserved. The set-up prompt and its resume-after-install flow (model_setup.rs, PhotoState::NoModel) are removed. Tests updated for the new behaviour, with new tests for the built-in path; docs, trackers and locale catalogs updated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: built-in denoiser cleans the colour planes together and adapts to the noise The built-in denoiser now measures the photo's noise once from the whole mosaic (the model prepares the photo before its tiles run) instead of per tile, stabilises the variance (generalised Anscombe), rotates the four Bayer planes with a Hadamard transform and runs a non-local-means filter whose patch distance spans all planes, so colour blotches are cleaned with the brightness. It cleans gently at low noise to keep fine detail, and harder, with a second pass that matches on the first pass's cleaner picture, as the noise grows. Cached pictures made by the first version are made again (builtin version 2). Adds an opt-in benchmark (tests/denoise_builtin_quality.rs, public CC0 raw plus noise of known level, scored against the original) and tests for the whole-photo noise estimate, strong noise, bad noise values and the strength ramp. On the Sony A7 III sample the result is 3.6, 7.0, 9.1 and 9.7 dB above the noisy picture at light, medium, heavy and extreme noise, against 2.9, 6.0, 6.5 and 5.8 dB for the first version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * contributors: add esli1283 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: docs say how to run the built-in denoiser's quality benchmark Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: built-in denoiser cleans colour further, keeps more texture and leaves no isolated specks Colour-difference planes get a wider pass guided by the cleaned brightness plane, the first-pass threshold is lower (more texture kept), and cells with no matching patch lean on a looser average in heavy noise. Skips negligible weights for speed; bumps builtin::VERSION to 3. Benchmark adds a texture figure; on the public A7 III raw the gain over the noisy picture is +3.7 / +7.2 / +9.7 / +10.4 dB (was +3.6 / +7.0 / +9.1 / +9.7). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Windows: tests build paths one component at a time and doc_path has no unused variable On Windows `cargo xtask ci` failed before it reached any of the work under test: - doc_path.rs bound the gvfs path outside the cfg(not(windows)) block that uses it, an unused variable under -D warnings. - tests_import and the MCP headless discovery test joined "trip/day2" onto a temp folder, giving mixed separators, and compared them with the normalised paths the folder walk returns. They now join each component. Test and lint changes only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: built-in denoiser filters with block-matching collaborative filtering, on the GPU The built-in denoiser's filter is now block matching with collaborative filtering (BM3D / CBM3D, written from the paper) instead of non-local means. On the public Sony A7 III benchmark with added noise it scores 0.25 to 0.6 dB higher at every noise level and keeps more texture at medium to extreme noise (light noise keeps slightly less: 0.58 against 0.62), as fast as before on the processor. docs/denoise.md records the comparison with non-local means and weighted nuclear norm minimisation (tried, not kept: about +0.03 texture for ten times the time) and what the tuning showed. - denoise-core: bm3d.rs (matching on the brightness plane, 2-D DCT plus Haar across the group, hard threshold then Wiener, hard threshold rising with the noise). Builtin::run_with takes the filter stage, so every other step is shared between devices. Tuning.bm3d selects it; non-local means stays as the fallback. builtin::VERSION is 4, so cached pictures are made again. - gpu: bm3d.rs and wgsl/bm3d_*.wgsl run the grouping, the filtering and the putting back on the graphics card, in the same order as the reference (the results agree to rounding: at most 0.1 noise deviation at a few tie blocks, 4e-6 on average, identical every run). Noise measurement, stabilising and the demosaic stay on the processor. FXC (DX12) needs the working arrays at module scope and no dynamically indexed vector: documented in docs/gpu-pipeline.md. - engine: the card is set up once per process for the first photo that may use it, checked against the processor on a check tile (in the filter's own domain: the demosaic's direction choice can flip a pixel on a tiny difference), timed, and used where it is faster. A tile it gets wrong runs on the processor and three stop it. Settings' Run on choice, device line and "Try the graphics card again" now work for the built-in denoiser. Its set-up marker lives in the models folder. - The benchmark takes LC_GPU=1, LC_NLM=1 and LC_B_* for sweeps. On an RTX 4080 SUPER a 256-cell tile takes 22 ms on the card against 408 ms on one processor thread, and the 1536 x 1024 benchmark crop 0.13 to 0.24 s against 1.0 to 2.1 s on four threads, with identical scores. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: the built-in denoiser measures the noise per colour plane and sets its strength from it The first noise estimator read a picture's texture as noise: it never reported less than about 0.003 at mid-grey whatever the real noise, so the denoiser always believed a photo was about as noisy as ISO 12800. The threshold curve was tuned on that over-estimate, and its levels (0.024 to 0.14) are far noisier than real Canon bodies produce (about 0.0025 at ISO 400 up to 0.038 above ISO 25600, measured on a private library; only aggregates are recorded in the docs). - estimate_noise: each colour plane is read on its own (they sit at different levels in a raw), a second-difference operator cancels flat and sloping signal, 12 x 12 blocks, the quietest 5 % at each brightness divided by the bias of that quantile of pure noise (0.67, measured), a constrained line fit. The first version stays as estimate_noise_v1 to compare with. On 30 public pictures with known added noise the 10th to 90th percentile of estimated / true deviation at mid-grey is 0.97 to 1.29 at the ISO 6400 level against 0.94 to 1.96. - bm3d::Params::lambda is a curve of the noise (1.2 at 0.0028 up to 3.5 at 0.14, straight against its logarithm) instead of two end values and a strength, and plan / filter take the noise level; the GPU path shares plan, so CPU and GPU agree as before. Fitted on clean low-ISO photos of a Canon EOS R and R6 Mark III with noise of their measured levels added: the lowest threshold within about a quarter of a decibel of the best at each level. - Result (PSNR, EOS R / R6 Mark III, 10 + 5 photos): ISO 25600 level 38.36 / 38.86 from 36.18 / 36.21, ISO 51200 level 33.83 / 34.45 from 28.42 / 28.78, ISO 6400 39.93 / 40.69 from 39.30 / 39.74, quiet photos no longer over-smoothed; the noise left in the result falls from 1.3 to 3.1 down to 0.73 to 0.99. - Tests: threshold curve, flat versus textured pictures, no-flat-area pictures, clipped / NaN mosaics; an opt-in accuracy harness on the public corpus (denoise_noise_estimate) with a calibration of the quantile bias; the benchmark takes LC_REAL, LC_BIN, LC_FULL, LC_EST and LC_B_LOFF. - builtin::VERSION is 5, so cached pictures are made again. docs/denoise.md has "How the noise is measured" and "Fitted on real cameras". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: GPU workspace pool cannot miss a wake-up; benchmark path and parity date fixed Review findings (CodeRabbit) on the built-in denoiser pull request: - bm3d::Bm3dGpu::forget and nn::NetRunner::forget changed the workspace count without the free-list lock. A take() that had found no free workspace and read made == WORKSPACES, but had not gone to sleep yet, could miss the wake-up and wait for good on a slot that was already free. forget now changes the count under the free lock, in the same order as take(), then notifies. - A test for each runner holds the free lock and checks that forget waits for it; both fail on the old code and pass now. - docs/denoise.md: the benchmark command resolves the raw file from the repository root, since the test runs from the crate's folder. - docs/target-app-parity.md: Last updated matches its newest revision-history entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: doc comments for the functions this branch adds CodeRabbit's pre-merge docstring check counted 49.72 % of the functions this pull request touches as documented (threshold 80 %). Every function the branch adds, the built-in denoiser's block-matching and non-local-means filters, its noise estimator, the GPU runner and their tests, now says what it does or what the test proves. Older functions the branch only touched are left as they were. Comments only: no code line changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: make the GPU set-up marker's folder; fix the benchmark's default path The crash guard was lost when the models folder did not exist yet. The opt-in quality benchmark resolved its default raw file against crates/engine. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: enforce the cache limit without a models folder The built-in denoiser makes products when there is no models folder, but eviction returned early in that case, so the cache could outgrow its limit. It now applies the default limit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * LR-EDIT-DETAIL-DENOISE: the cache-limit test needs no 20 GiB file off Unix The no-models-folder test now checks the limit itself on every platform and keeps the oversized sparse file for Unix only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * S0002: review fixes for the built-in denoiser Tell the runner a photo's noise only when it could be measured, run prepare() after the work gate with a cancel check, reword the Settings hint for builds without a models folder, drop untranslated placeholder locale entries (they fall back to English) and restore the cs.json spacing; rustfmt. Co-Authored-By: Claude Code <noreply@anthropic.com> * S0002: fix review findings: command descriptions, stale gpu test, self-test noise The denoise.models.select and denoise.toggle descriptions now describe the built-in fallback. Drop our workspace-forget test, which read fields that upstream's Workspaces pool (#2631, same fix) replaced and so did not compile; upstream's own tests cover it. The built-in self-test is told a noise instead of measuring a 16-cell tile too small to measure. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: esli1283 <esli.aaron@outlook.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: FredApps <FredApps@users.noreply.github.com> |
||
|
|
bde8fd9904 | Merge storytold/main into fix/3381-title-bar | ||
|
|
bb0f0d5e11 | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
eb441859ca | Merge storytold/main into fix/2674-stored-frame-regions | ||
|
|
419373a84a |
LR-LIB-PEOPLE: re-read uses a raw's decoder orientation (#2674, review)
Review of #2857 (P2): import takes a raw's orientation from the decoder (CIFF for CRW, the raw IFD for MEF); re-read and sync used Exif then XMP. file_orientation now prefers lightcraft_raw::probe_info when the file is a raw, so Read Metadata from File and Synchronize Folder agree with import. |
||
|
|
d729c80a5a |
Detail: Remove Hot Pixels, on for newly imported raws (#1345) (#1350)
* WIP: suppress isolated hot photosites in the raw mosaic (#820) Noise model fitted to the image, candidates 5 sigma above every same-colour neighbour, kept when two or more direct neighbours are lit (real light); adjacent hot pairs are replaced too. LIGHTCRAFT_HOT_PIXELS=0 turns it off, LIGHTCRAFT_HP_DEBUG=1 prints statistics. Renders are cached apart from older ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * WIP: hot pixel suppression off by default (LIGHTCRAFT_HOT_PIXELS=1 turns it on) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: mosaic outlier filter: cache key no longer names the #858 comparison switch (merged upstream) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: mosaic outlier filter: fixed thresholds (no LIGHTCRAFT_HP_K / _SPOT knobs), statistics under LIGHTCRAFT_PROFILE Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: Remove Hot Pixels, a box in Detail ▸ Noise Reduction, on for newly imported raws `Detail::nr_outliers` replaces single outlier photosites in the raw mosaic before demosaicing. It is true in `DevelopSettings::for_raw` and false by default, so edits saved before the box existed keep their look. The loaders take the flag (no more `LIGHTCRAFT_HOT_PIXELS`), a decoded source records it, and a cached raw source decoded with the other setting is decoded again. The box shows for raws only (`check:detail.nrOutliers`). * #1345: Remove Hot Pixels off keeps old settings' JSON; smart previews fit either setting - `detail.nr_outliers` is left out of the stored JSON when off, so settings saved before it keep their hash and cached thumbnails. Copy / paste, presets (also at an amount), preset updates and Auto Sync work on `DevelopSettings::to_json_for_merge`, which states it, so turning the box off still carries over. - `DecodedSource::fits_outliers` accepts a smart preview (`from_proxy`): it has no mosaic, so an offline raw with the box on keeps reusing the decoded proxy. (review of #1350) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: the Detail eye and Reset Detail switch Remove Hot Pixels off `DevelopSettings::removes_hot_pixels` (the box and the Detail section's eye) decides the raw decode in the app, the web worker and the thumbnail bench, so a Detail section that is off contributes nothing here either. Reset Detail resets the box to the general default (off), like the sliders it resets. (review of #1350) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: the hot pixel limit counts only replaced photosites, and can be set The 0.2 % limit was checked against the candidates before real light was filtered out, so a dense star field reached it with its stars alone and kept its hot pixels too. It now counts the photosites that would be replaced, as the module docs say. The limit is now a parameter (RawImage::suppress_hot_pixels_with, default HOT_PIXEL_MAX_SHARE) and a setting: Settings > Performance > Hot pixel limit, applied with the new command app.hotPixelLimit. Changing it forgets the decoded sources and changes the render cache keys of photos that remove hot pixels; at the default every key stays as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #1345: the app's default hot pixel limit is the default The setting is an f32: 0.2 % read back as 0.2000000029… % was a new limit, so every start forgot the decoded sources and changed the render keys of photos that remove hot pixels. A value within a millionth of the default now is the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Format the imports * Web loader test states what it decodes with * Format the media exports --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com> |
||
|
|
8f7798207a |
Merge storytold/main at 61005f3d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvoftP5JSqAEJ9t7FSczTs |
||
|
|
c18081f0b9 | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
61005f3dc1 |
Culling: the loupe uses built previews; photos prepared ahead and in the background (#3077)
* PERF-CULL-0: ui.inspect reports how the loupe showed each photo it was stepped to
perf.loupeSteps records, per step, the ms from the step to the first frame
with anything of the photo, with a full-size picture (camera JPEG, cached
or rendered) and with LightCraft's own render, measured on the frames the
app draws, so culling benchmarks exclude the control channel's round trip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 95f0343848fc0a238f80f07ceb292eaf6779aeaa)
* PERF-CULL-1: browsing a folder shows its first photos within a second
The Local view read and hashed every file of the folder before showing any
of them (2,933 EOS R5 C CR3s: 30.6 s to the first photo). It now hands the
files the library lacks straight to the import, ordered the way the view
sorts them (file time for the date sorts), which adds them batch by batch:
first photo 0.9 s, twelve thumbnails 1.4 s, the whole folder in the same
~34 s as before, in the background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit b766558e7ef4f802919a144da53e444f70e0186b)
* PERF-CULL-2: the loupe makes the next photos' camera previews ahead of the step
Culling steps through photos, and each step waited for the next raw's
embedded JPEG to be read and decoded (p50 187 ms, p95 332 ms on EOS R5 C
CR3s) and then for a full raw develop. Now:
- the loupe's stand-ins for the next five photos in the direction of
travel and the one behind are made ahead (Slot::QuickAhead, priority
just below the loupe's own render) and taken over at the step, so a
step shows the camera JPEG at once (p50 0 ms);
- a slot keeps its photo while the photo stays wanted, so photos that
shift places as the user steps are not made twice;
- decoded camera JPEGs stay in the memory LRU (never on disk), so going
back shows them again without a decode;
- while an arrow key is held (a photo left within 0.25 s), the raw
develop waits until the photo has stayed 0.12 s, and a render queued
for a photo already left is dropped, so the workers make the previews
instead of renders nobody sees.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 0430b95636d2b6adce8887af7b9464687c95549e)
* PERF-CULL-3: CR3 previews take the smallest embedded JPEG that covers the view; plain sRGB JPEGs decode straight to 8-bit
Upstream's embedded_preview_covering (
|
||
|
|
4c1d2e919b |
Merge storytold/main at eaef01e9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvoftP5JSqAEJ9t7FSczTs |
||
|
|
d3727199ab |
Merge storytold/main at eaef01e9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvoftP5JSqAEJ9t7FSczTs |
||
|
|
9056d642a5 | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
eaef01e986 |
Light tests allow a level of 8-bit dither (#3779)
Process V4's tests and the 8-bit dither landed separately, and each passed its own gate: a flat source now varies by a level, and a ramp can step back by one. The tests compared exact 8-bit values. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0b9ff64e83 | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
8972565249 |
LR-PROF-IMPORT: import the user's own DNG camera profiles (.dcp) (#1063)
* LR-PROF-IMPORT: import the user's own DNG camera profiles (.dcp)
profile.import (File > Import Profiles & Presets, drag & drop) now also
reads DNG Camera Profiles: the user's own .dcp files, e.g. made with a
colour-checker tool. They appear in the profile browser and menu under
Camera Profiles with dcp:<camera>-<file> ids; profiles.camera lists them
and profile.deleteCamera / profile.deleteImported remove them. A library
on disk keeps a copy in Profiles/. No profile ships with the app and the
repository holds none: tests build theirs in memory (write_dcp).
lightcraft_raw::dcp parses the file per DNG spec ch. 6 (magic RC in
place of 42; IFD 0 read through the existing DNG colour builders) and
DcpProfile::applied_to puts its matrices, illuminants and look in place
of a raw file's, keeping the shot's white balance, analog balance and
baseline exposure.
Choosing one (develop.profile) replaces the raw file's colour model when
it is decoded, before the starting colour and the white-balance scale
(WbScaleOf) are worked out, so the matrix path takes over from the fitted
JPEG look (CR3/CR2/ARW...) and white balance follows the profile. A
profile without a tone curve of its own (most chart-made ones) keeps the
photo's contrast: the DNG's tone curve, else the tone of the camera-JPEG
fit. The profile travels with SourceRef::File into FileLoader /
PairLoader (new Option<&DcpProfile> argument); a decoded source records
the profile it was made with and is not reused for another. Smart
previews and the web render worker keep the file's own colour; rendered
(non-raw) photos ignore it.
This reverses the documented "Adobe profile formats deliberately
unsupported" stance for user-supplied files only; docs updated.
Split out of #1012.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: build against upstream main (kelvin_presets, 3-arg loaders)
DcpProfile::applied_to keeps the file's white-balance presets (new
ColorData::kelvin_presets upstream): they are camera facts like as-shot
neutral. Upstream's new FileLoader test closures take the profile argument.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: hover previews and browser thumbnails develop the hovered camera profile
The loupe's hover preview (preview_job) and the profile browser's thumbnails (variant_job) took
the photo's own decoded source, so hovering an imported camera profile showed the built-in colour
(and hovering a built-in profile while one was applied kept the camera profile's). Both now decode
as the hovered settings develop: the source follows the settings' camera profile, as the main
render does, and a variant's cache key already follows its settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: build against upstream main (face-tile test loader and render_bench take the camera profile)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* CI: the catalog's staging-link test module is Unix-only (unused import broke clippy on Windows)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* CI: same-folder export test expects the '/'-joined subfolder (failed on Windows)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* CI: folder-drop tests build widget ids with the Folders tree's '/' paths (failed on Windows)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: rustfmt after merging main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: a real D750 camera profile as a pinned test corpus, checked against Lightroom
dieli's CC0 Nikon D750 ColorChecker set (Calibrite PROFILER dual-illuminant DCP,
matrix-only; the two chart NEFs; Lightroom Classic 15.6 renders with the profile)
is fetched by cargo xtask corpus --download into corpus/d750-colorchecker/, pinned
to commit f37f4054 and by sha256; nothing is committed.
Tests (skip without the corpus): the profile parses as matrix-only with
Cool White Fluorescent + D55; the matrices blend by inverse temperature; the
matrix-only path inverts the colour matrix and adapts to D50; it takes over the
chart NEFs' colour; imported and applied end to end, the charts develop within
mean dE2000 3.84 (flash) / 3.35 (indoor) of Lightroom's renders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: a single .dcp file is a camera profile only, not also an unreadable LUT
profile.import read every plain file that was neither a folder nor a .zip as a .cube LUT, so importing
one .dcp imported the camera profile and also reported it as failed ("stream did not contain valid
UTF-8"). Only .cube files are read as LUTs now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: review fixes for imported camera profiles
- A library moved or restored elsewhere finds its camera profiles in its own Profiles/ folder.
- A grid thumbnail is cut from the decoded preview only when that preview used the same camera profile.
- Render and source cache keys carry a fingerprint of the profile file, so a deleted or replaced profile never
has its old renders stand in.
- Camera profiles can be favourites, and favourites/recent entries survive reopening the library.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LR-PROF-IMPORT: smart previews without the camera profile, current-profile source facts, unsupported files reported
CodeRabbit review of
|
||
|
|
56d1cc2865 |
pipeline: apply 8x8 ordered Bayer dithering to prevent display banding (#3509)
* pipeline: apply 8x8 ordered Bayer dithering to prevent display banding Quantizing float linear/display pixel values to 8-bit integers using round-to-nearest produces noticeable Mach bands and contour stepping on smooth gradients (skies, shallow depth-of-field backgrounds, skin tones). This applies an 8x8 ordered Bayer dithering pattern centered around round-to-nearest with sub-LSB amplitude in both CPU finish (crates/pipeline) and GPU compute shader (crates/gpu/src/wgsl/finish.wgsl). Exact integer levels, pure black, and white remain noise-free, while fractional gradients smoothly dissolve into continuous gradations. Dithering coordinates account for window/tile view offsets to maintain phase alignment across zoom tiles. * pipeline, gpu: address review feedback on Bayer dithering - Always pack VIG_VIEW in GPU params so windowed renders without a vignette maintain the correct absolute view origin for Bayer dithering coordinates. - Restore the full Bayer threshold range (0..1) without deadzones, preserving float white via direct half-LSB rounding to prevent edge quantization drop. - Return the true sampled bounds midpoint in geometry sub-pixel edge detector fallbacks. - Add docstrings to satisfy coverage checks. * pipeline, gpu: narrow highlight white guard to allow dithering on 254.5 * tests(pipeline): assert edge contrast in manual keystone tracking windows * docs(pipeline): add docstrings to touched test functions * Export tests: count distinct levels however the dither orders them * Mask geometry tests ignore a level of 8-bit dither --------- Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com> |
||
|
|
2e66a00d55 |
Tests: two catalog flakes; a readback waits as long as a render (#3777)
a_failed_write_is_held_reported_and_retried raced the log thread: it could fail the first append before the second arrived, which is then refused by design. The test now hands both over in one append. a_file_path_rule_allocates_nothing_per_photo counted heap blocks on every thread, so the test harness's own thread could fail it (four blocks, on PRs that don't touch this code). An allocation per photo is thousands, so the bound stays far below that. read_texture waited 10 s where the render path waits 60 s, so a view could be dropped from a headless screenshot while earlier work ran. The GPU guide now says what the code does about the window's card. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
20afbcc584 | Merge storytold/main into feat/windows-monitor-profile | ||
|
|
fc97c72864 |
LR-BEHAV-RENDER-FIDELITY: calibrated Contrast and Blacks in process V3 (#523) (#1383)
* LR-BEHAV-RENDER-FIDELITY: split V3 Contrast and Blacks Port calibrated global Contrast and Blacks with shared CPU and GPU tables. Preserve V2 Whites, local adjustments, other source paths, and all V1 and V2 renders and persistent cache keys. Generate the constants from public Contrast and Blacks fits. Cover numeric references, legacy renders, keys that include the resolved process, windows and GPU parity. Document the measurements, limits and provisional number. Co-Authored-By: Codex <noreply@openai.com> * LR-BEHAV-RENDER-FIDELITY: reduce V3 preview cost and sanitize fits Compose calibrated CPU preview operators over linear input and skip unused mix endpoints. Keep high-bit-depth exports on the original operator path. Require three distinct photos per published error group and band, regenerate the fit hashes, and cover the sanitizer with synthetic tests. Document release measurements and identify the separate measurement tool proposed in #1181. Co-Authored-By: Codex <noreply@openai.com> * LR-BEHAV-RENDER-FIDELITY: preserve V3 luminance evaluation Retain the original operator evaluation for luminance mixes, including preceding Contrast, so preview interpolation cannot change the cutoff. Keep the fused per-channel path and exact deep exports. Add CPU and GPU regressions for both uniform cutoff fixtures. Document the measured CPU cost of positive Blacks and the retained fused path. Co-Authored-By: Codex <noreply@openai.com> * LR-BEHAV-RENDER-FIDELITY: finish split-module integration Import the persistent process-key helper directly in the session module. Keep the existing V3 operator selection and evaluation order in an inline helper so the combined finish-stage closure stays within the complexity limit. Refresh the V1/V2 golden hashes only after the exact fixture produces identical hashes on plain current main. Keep the cross-platform legacy samples unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LR-BEHAV-RENDER-FIDELITY: date the calibrated Light gap summary Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com> |
||
|
|
67782d8f65 |
Auto Sync: As Shot, Auto and Temp apply to each selected photo's own white balance (#3571)
* LRC-DEV-AUTOSYNC: Auto Sync carries white balance as a mode or on each photo's own scale: As Shot and Auto per photo, a Temp move keeps each photo's tint (N-A3, note 18) Auto Sync already sets every other setting to the active photo's number. White balance is the exception: main stores the resolved Kelvin and tint with the mode, and Auto Sync copied those numbers. As Shot then gave every selected photo the active photo's camera white, Auto gave them the active photo's Auto white, a Temp move from As Shot gave them the active photo's tint too, and a JPEG beside a raw got the raw's Kelvin read on its own scale. Now (sync_same.rs) the mode goes on each photo and each applies it to itself: As Shot its own camera white, Auto its own white worked out on its own pixels, a light preset its numbers. A typed or dragged Temp or Tint sets the same Kelvin on every raw (an old relative-scale value converted first) and only the slider that moved, and a photo on another scale moves by as much as the active one did. One undo step, as before. Tests (tests/autosync_wb.rs): As Shot / Tungsten / Auto per photo with three different as-shot whites and undo; a typed and a dragged Temp keep each photo's tint; the old relative scale; a JPEG beside a raw. Three of the four fail on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LRC-DEV-AUTOSYNC: Auto's per-photo pixels are read in parallel; the row says which sync does what Review of #3571: - When Auto is synced, the other selected photos' thumbnails that aren't in memory are now decoded in parallel (one per core), not one after another on the UI thread. - The checklist row says the per-photo white balance belongs to Auto Sync, while Sync Settings copies the active photo's stored one. - The row and the test notes now say a Temp is the same number only on a photo of the same scale, and that other photos move by as much as the active photo did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LRC-DEV-AUTOSYNC: Auto's per-photo pixels are read a core's worth at a time, each with its decoder's facts Review of #3571: - A large selection no longer holds every decoded thumbnail at once. They are decoded in batches of one per core (at most 16), and each batch's white balance is worked out before the next is decoded. - Each photo's Auto uses the facts its own decoder gave, read right after its source is cached, so a photo pushed out of the cache by later ones never falls back to catalog metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * LRC-DEV-AUTOSYNC: a light preset with a tint of its own on a JPEG carries that tint; Auto's facts are read before the cache can evict them (review of #3585) - On the relative scale the stored numbers are what renders, so a preset the active photo overrides (`develop.wb {mode, tint}`) travels as its numbers, like Custom, not as the preset's tint. - `sources_now` reads each photo's decoder facts before storing the decoded source: the cache's budget may evict that entry at once, and the header facts alone lack the camera colour Auto uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com> |
||
|
|
4da8573b55 | Merge storytold/main into feat/windows-monitor-profile |