Files
lightcraft/docs/process-versions.md
T
d977d3e1a4 LR-EDIT-DETAIL-SHARPEN: Radius and Detail work, and sharpening acts at pixel scale (#757) (#762)
* LR-EDIT-DETAIL-SHARPEN: Radius and Detail work, and sharpening acts at pixel scale (#757)

* LR-EDIT-DETAIL-SHARPEN: an enlarging export keeps every Radius step (#757)

The sharpening blur's sigma was capped at 4 px, so a 2x export rendered
Radius 2 and 3 alike (both scaled past 4 px). The cap is now the largest
sigma the Gaussian taps support, GAUSS_SIGMA_MAX (64), which the CPU and
GPU blurs share. Test: enlarged_export_keeps_radius_steps_apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ti8KemZbjE2ZgHgV1q1BM

* LR-EDIT-DETAIL-SHARPEN: preserve process-aware previews after rebase (#757)

Use the V3 pixel-scale blur and Masking weight for the Radius, Detail and
Masking previews on both renderers. Preserve the legacy texture-band previews
for V1/V2 and compute the same Gaussian when sharpening is disabled.

Cover process selection, radius changes, and prepared/fresh plane equivalence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ti8KemZbjE2ZgHgV1q1BM

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 05:21:13 +01:00

6.7 KiB
Raw Blame History

Process versions

Every photo's develop settings record the rendering process they are interpreted by (DevelopSettings::process, a ProcessVersion in crates/develop/src/settings.rs). Lightroom calls this the process version. It lets the look of LightCraft improve (a new default tone curve, issue #146; stronger Light sliders, issue #196) without changing a single photo someone has already edited.

Process What it changes
V1 LightCraft's rendering from before process versions existed. Frozen.
V2 Highlights and Shadows keep fine detail (issue #632): their base layer (local::tone_base) is the edge-aware filter of a separable median of log luminance (radius 0.15 % of the long edge, at most 24 px, local::BASE_DETAIL) instead of log luminance itself, so twigs, lines and texture take their surroundings' shift and keep their contrast; region edges stay sharp. CPU (local::prepare) and GPU prepare branch on local::tone_base_detail (0 = no median under V1); the base plane is cached per process (local::BaseKey). Renders without Highlights or Shadows (global or local) are the same as V1's. Tone map unchanged.
V3 (the latest) Pixel-scale sharpening (issue #757): Radius uses the original photo’s pixels, Detail controls halo damping, and downsized sources retain a response-matched sharpening band. V1 and V2 keep texture-band sharpening exactly. The sharpen plane is keyed by radius and process and invalidated after noise-reduction changes. The Highlights/Shadows base and tone map are unchanged from V2.

The rule

A change that alters how existing settings render ships as a new process. V1 is frozen.

  • A deliberate change of look (tone model, slider response, default curve, how a stage interprets a value) goes into a new process. Photos stay on the process they have until someone runs Update to Current Process (or Reset).
  • Bug fixes that make output correct where it was broken (a crash, NaN or black output, a decoder error, CPU and GPU disagreeing) are not looks and can land in place, as before.
  • Changes to how a file is decoded or which camera colour it gets change the source, not how settings are interpreted; the process doesn't reach that stage today.

Neutral display tone map (JPEG, PNG and other display-referred sources with Contrast, Whites and Blacks at 0) is the identity up to white: the SDR shoulder no longer pulls 1.0 down to 0.9875 (8-bit 255 to 254, issue #1945). It landed in place as a bug fix, since the tone map promised identity at neutral settings; edited curves keep the shoulder and their V1 look.

AI mask edges (Object and Describe selections stored as a model's grid) follow the photo's edges when they are enlarged to the output (issue #3272): the plain enlargement spilled 1–2 px onto the background, which a raised Exposure turned into a bright outline around the subject in previews and exports. Existing AI masks become 1–2 px tighter at a subject's edges, on every process; their inside, their outside and edges without contrast in the photo are unchanged. It landed in place as a bug fix: the outline is output that isn't in the photo nor in the edit, not a look anyone chose.

Adding a process

  1. Add a variant to Process (V2), append it to Process::ALL (oldest first), give it its number in Process::version and make it Process::LATEST.
  2. The compiler then points at every match on Process. The base tone map is chosen in one place, lightcraft_pipeline::finish::base_tone (both the CPU and the GPU renderer take it from there): give V2 its own arm and leave V1's alone. A process that changes another stage branches there the same way, on s.process.process(); if that stage is cached (Plan::lin_key, the planes in local.rs), add the process to its key.
  3. Keep settings_saved_before_process_versions_load_as_v1 (crates/develop/src/tests_process.rs) passing: settings saved without the field must stay V1, not become the new latest. That is the field's #[serde(default = "ProcessVersion::legacy")]; the struct-level Default gives new settings the latest instead.
  4. Render a few procedural scenes on both processes and compare, and update docs/parity-checklist.md.

What gets which process

Process
A newly imported, merged or Lightroom-imported photo the latest
Settings saved without the field (catalogs and their snapshots, versions and history, lc:settings in XMP sidecars, browser libraries: everything from before process versions), or with a damaged value V1
Reset (develop.reset) the latest
Reset of a section or a slider unchanged
Update to Current Process (develop.updateProcess) the latest, for photos on an older one; one undo step. The menu item follows the selection; a call with ids / id acts on those photos whatever is selected
Virtual copy, duplicate the source's
Copy / paste / sync / Auto Sync / presets unchanged (see below)
crs: edits (XMP from other raw developers, Lightroom catalogs, imported presets) unchanged; a new photo has the latest
Restoring a version or history step the one it had

Copy, paste, sync and presets never carry the process: it is in no settings group, so the photos they change keep their own and interpret the pasted values with it. (Lightroom offers the process version as a separate checkbox when syncing; LightCraft's groups are coarser and keep it out.) Partial settings JSON that names process itself (develop.merge, a hand-written preset) sets it.

Lightroom edits are re-interpreted by LightCraft's mapper (crates/engine/src/crs.rs), which is written against the current rendering. crs:ProcessVersion numbers Adobe's renderer, not ours, and is ignored; a photo imported with Lightroom edits is on LightCraft's latest process, and an existing LightCraft photo that receives them keeps its own.

A number this build doesn't know (a library or sidecar written by a newer LightCraft) loads, is kept when saved, and renders with the newest process this build has. Update to Current Process never moves a photo to an older process; Reset does move it to this build's latest.

A damaged value (anything but a whole number from 0 to 2^32 - 1; 2.0 counts as 2) reads as V1, like a missing field, and costs nothing else: the rest of the settings load from the catalog, a snapshot, the op log or an lc:settings sidecar as usual. The damaged value isn't kept: the photo is saved as V1.

Storage

The process is saved as "process": <number> in the settings JSON, except V1, which is left out. Settings made before process versions existed are therefore saved exactly as before, and their preview-cache keys (DevelopSettings::hash64) don't change. develop.get (MCP get_develop) always states the process.