Files
lightcraft/docs/tint-direction.md
T
aiongg f3bb7a9f30 CI: one test binary per crate, line tables only, one WASM check (#2710)
* CI: dev profile keeps line tables for workspace crates and no debug info for dependencies

* Control server tests: persistent_requests_and_slow_replies_outlive_the_idle_read_timeout no longer races the idle timeout

Each request (and the closing half-shutdown) reaches the socket before the
worker reads, so on a slow machine only the reply waits outlast the 100 ms
idle timeout, as the test intends. The idle test sends its input the same
way, and the no-frames test allows 5 s instead of 2 s to give up.

* CI: one integration-test binary per crate

Each crate's tests/*.rs become modules of tests/it/main.rs (51 test files
into 13 binaries, plus 6 that stay their own). Inner #![cfg] attributes
move to the mod lines. Files that change state for the whole process keep
a binary of their own: codecs jxl_tail_heap and raw sr2_heap (dhat global
allocator), gpu shutdown, shutdown_first and memory, ui-egui shutdown.
Docs, comments and the GPU probe name the new paths and targets
(--test it <module>::); the CI report names an it binary's skips after
the crate.

* CI: tests under nextest, doctests apart, one wasm check

xtask ci runs the tests with cargo nextest run --lib --bins --tests when
cargo-nextest is installed, else cargo test with the same targets and a
one-line notice; doctests run after them with cargo test --workspace --doc.
The heif and hardware GPU steps use the same runner. Examples stay out of
the test build (clippy --all-targets still checks them). Concurrent tests
under nextest follow the RUST_TEST_THREADS cap (NEXTEST_TEST_THREADS).
The wasm check is one cargo check --keep-going over the same crates, and
clippy runs with --keep-going.

* CI: tests run under cargo test by default, nextest with LIGHTCRAFT_NEXTEST=1 (per-process scene cache)
2026-10-23 13:34:30 -04:00

2.4 KiB

Global Tint direction

Negative (left) Tint adds green; positive (right) adds magenta, as the slider's track shows and as in Lightroom and the DNG specification (issue #188). It is a global white-balance change, not a Nikon-specific decoder fix.

Reproducible evidence

The existing UI already declares Track::Tint as green → magenta (crates/develop/src/controls.rs) and paints a green left endpoint and purple right endpoint (crates/ui-egui/src/widgets.rs). Local mask tint also implements the correction direction: positive tint reduces green (crates/pipeline/src/finish.rs). The old global conversion contradicts both.

The synthetic tint_negative_is_green_and_positive_is_magenta regression renders an 18% neutral patch. Before the fix, setting global Tint to -50 produces sRGB [129, 113, 128]: red and blue exceed green, so the result is magenta. The test asserts the output direction for rendered sources, relative-WB RAW, and calibrated RAW with a nonzero As Shot reference. The complementary auto/picker test checks that correcting a green patch returns positive Tint and neutralises the patch.

Run the evidence without private samples:

cargo test -p lightcraft-pipeline tint_ -- --nocapture
cargo test -p lightcraft-gpu --test it equivalence::tint_directions -- --nocapture
cargo test -p lightcraft-color cct

Why the sign changes

temp_tint_to_xy describes the source illuminant removed by Bradford adaptation. To add magenta to the output, the source white must be greener (positive Duv), not more magenta. Therefore the correction convention requires Duv = +tint / TINT_SCALE, and the inverse conversion must return tint = +Duv * TINT_SCALE.

The CPU and GPU consume the same wb_matrix_for result. RAW white-point inference, auto WB and the picker use the inverse conversion. Both directions change together; local mask tint and calibration shadow tint are unchanged. The render-cache version increases to invalidate stale output.

Existing edits

Before this change the sign was the other way round, so a saved non-zero custom Tint (a catalog edit, a preset, history) now renders with the opposite green/magenta shift; As Shot, Auto and the presets that compute their values are unaffected, and re-running Auto or the picker gives values in the new convention. Tint values imported from Lightroom XMP (crs:Tint) were rendered the wrong way round before and now render as in Lightroom. No saved values are rewritten.