Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6c4e0fe
wb: added new param late_correction to temperature.c, exposed as a ch…
kofa73 Dec 10, 2025
f96e509
wb: Updated the rest of the files, but channelmixerrgb is broken, eve…
kofa73 Dec 16, 2025
e07a8c4
wb: Undid changes to channelmixerrgb.c, will go carefully next time
kofa73 Dec 16, 2025
5e9cb04
wb: Fixed publishing of late_correction in temperature.c
kofa73 Dec 17, 2025
1c7ea37
wb: added DT_ILLUMINANT_FROM_WB, separated find_temperature_from_raw_…
kofa73 Dec 17, 2025
79f9141
wb: fix switch-case fall-through in illuminants.h#illuminant_to_xy; v…
kofa73 Feb 28, 2026
eeb8c8b
wb: safe division in _get_d65_correction_ratios
kofa73 Feb 28, 2026
38a6fdb
wb: added _get_corrected_illuminant_xy to deduplicate repeated code
kofa73 Feb 28, 2026
a4e6e5f
wb: fix illuminants.h switch-case fall-through introduced when DT_ILL…
kofa73 Mar 6, 2026
278bd3e
wb: renamed find_temperature_from_... to find_illuminant_xy_from_...;…
kofa73 Apr 5, 2026
45de2f3
Doc updates
kofa73 May 31, 2026
a835dd5
Doc updates: wb + color calibration
kofa73 May 31, 2026
4a5336f
wb: removed 'as-shot to reference' (same as 'as shot' + 'prepare data…
kofa73 May 31, 2026
2ac056b
wb: make sure color calibration / white balance warning is shown when…
kofa73 Jun 6, 2026
d44a4e1
wb: don't modify p->x, p->y in _preview_pipe_finished_callback; switc…
kofa73 Jun 7, 2026
8229eb7
wb: minor clean-up in channelmixerrgb (GUI struct field names for cla…
kofa73 Jun 7, 2026
d5f6a15
wb: opposed.c hash: late_correction is a param of temperature (is bef…
kofa73 Jun 7, 2026
260132b
wb: tiny clean-up: unnecessary parens, accidental duplication
kofa73 Jun 7, 2026
f77c98f
wb: removed _temp_array_from_params from reload_defaults, d's first 4…
kofa73 Sep 19, 2026
a3a32da
wb: removed 2nd, redundant d->preset = DT_IOP_TEMP_AS_SHOT; from temp…
kofa73 Sep 19, 2026
aed36d2
wb: only enable late_correction for color raw by default
kofa73 Sep 20, 2026
9034f10
wb: read chr->adaptation->enabled via snapshot for thread safety
kofa73 Sep 20, 2026
3db3e0d
wb: add a proxy to allow temperature.c to determine whether CAT is en…
kofa73 Sep 20, 2026
8e487f9
wb: prepare to store per-pipe WB settings: introduce dt_dev_wb_t
kofa73 Sep 20, 2026
55d853d
wb: publish per-pipe WB settings
kofa73 Sep 23, 2026
0c83d12
wb: read per-pipe WB settings from pipe threads
kofa73 Sep 27, 2026
de7cf26
wb: don't write chr->wb.coeffs/late_correction at processing time
kofa73 Sep 27, 2026
24f4405
wb: rebase on master, doc/comment udpates; add dev-doc for wb_and_col…
kofa73 Oct 4, 2026
18dabbf
wb: derive color calibration's GUI WB from temperature's params
kofa73 Oct 12, 2026
21b134b
wb: minor comment update
kofa73 Oct 17, 2026
abf3f29
wb: fall back to D50 when the white balance illuminant lookup fails
kofa73 Oct 17, 2026
0fe6922
wb: minor comment correction
kofa73 Oct 18, 2026
33987ae
wb: fix the "prepare data for color calibration" checkbox
kofa73 Oct 18, 2026
b9226b7
color calibration: refresh camera illuminant after WB changes
kofa73 Oct 18, 2026
a04935d
wb: release notes entry for a bug present on master, fixed here: mast…
kofa73 Oct 25, 2026
7f4f452
wb: update the checkbox's reset value when defaults are reloaded
kofa73 Oct 25, 2026
491112b
color calibration: reject illuminants derived from extreme WB multipl…
kofa73 Oct 25, 2026
b25fc67
wb: release notes for transferring "as shot to reference" from anothe…
kofa73 Oct 25, 2026
366ec37
wb: fix 'gui_init() uses a GTK 3-only call for the checkbox and leaks…
kofa73 Oct 28, 2026
782a448
color calibration: fall back to the saved x/y if the camera lookup fails
kofa73 Oct 28, 2026
afd950a
wb: keep a saved "camera reference" when refreshing the GUI
kofa73 Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,21 @@ changes (where available).
instead of the copies bundled in the app. Without Homebrew, the
modules were not found at all.

- Fixed stale color temperature and illuminant color in color calibration
after white balance changes when using "as shot in camera".

- Fixed input color profile and highlight reconstruction (opposed and
segmentation modes) still applying part of the white balance
correction when the white balance module, set to "as shot to
reference", was switched off on a raw image. Edits made that way
render with different colors.

- Fixed a color cast when white balance in "as shot to reference" mode
was pasted from another image, or applied through a style or preset:
white balance applied the pasted multipliers, but input color profile
completed the correction with the target image's own as-shot
multipliers.

## Lua

### API Version
Expand Down
4 changes: 3 additions & 1 deletion dev-doc/GUI_Threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ instance. It is torn down again straight away, and `exposure`'s `gui_cleanup()`
instance behind it. In tree the callers carry that check:
`dt_dev_exposure_get_effective_exposure()` resolves a live, enabled instance itself and
passes that one, and the other two getters go through a helper that requires
`proxy.exposure.module` to be set (`src/develop/develop.c`).
`proxy.exposure.module` to be set (`src/develop/develop.c`). `dt_dev_cat_is_active()`, the
only caller of color calibration's `proxy.cat_is_active`, passes only the `channelmixerrgb`
instances it finds in `dev->iop` (`src/develop/develop.c`).

The producing side — usually `commit_params()` or `process()` — must take the same lock.
Publishing a raw pointer into `gui_data` through a proxy is worse still: the reader then
Expand Down
6 changes: 4 additions & 2 deletions dev-doc/IOP_Module_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ Called when an image is loaded, including when switching images. Its purpose is

When invoked through the wrapper `dt_iop_reload_defaults()`, the framework calls `dt_iop_load_default_params()` after the module's own `reload_defaults()` returns, copying `default_params` into `self->params`. So on the wrapper path, writing to `default_params` inside `reload_defaults()` immediately affects `self->params` as well. Most callers use the wrapper (for example `_dt_dev_load_pipeline_defaults()`, the per-module reset button, and instance duplication).

**Caveat: direct calls skip only the framework copy, not the module body.** A few sites invoke `module->reload_defaults(module)` directly instead of through the wrapper (for example, `dt_dev_read_history_ext()` calls `temperature->reload_defaults(temperature)` to refresh the white balance state). Those callers do **not** get the automatic `dt_iop_load_default_params()`, so the wholesale `default_params` -> `self->params` copy does not happen. This does **not** mean `self->params` is left untouched: the module's own `reload_defaults()` body may still write specific `self->params` fields directly. `temperature.reload_defaults()` does exactly this: it writes `p->preset` (via `p = self->params`) together with `default_params`, while the white balance coefficients are written to `default_params` only. On a direct call, the result is therefore a *partial* update: the fields the body touches change in `self->params`, the rest do not, and no history entry is recorded. If you add such a call, prefer `dt_iop_reload_defaults()`, or check exactly which `self->params` fields the body changes and sync the rest yourself.
**Caveat: direct calls skip only the framework copy, not the module body.** A few sites invoke `module->reload_defaults(module)` directly instead of through the wrapper (for example, `dt_dev_read_history_ext()` calls `temperature->reload_defaults(temperature)` to refresh the white balance state). Those callers do **not** get the automatic `dt_iop_load_default_params()`, so the wholesale `default_params` -> `self->params` copy does not happen. This does **not** mean `self->params` is left untouched: the module's own `reload_defaults()` body may still write specific `self->params` fields directly. `temperature.reload_defaults()` does exactly this: it writes `p->preset` and `p->late_correction` (via `p = self->params`) together with `default_params`, while the white balance coefficients are written to `default_params` only. On a direct call, the result is therefore a *partial* update: the fields the body touches change in `self->params`, the rest do not, and no history entry is recorded. If you add such a call, prefer `dt_iop_reload_defaults()`, or check exactly which `self->params` fields the body changes and sync the rest yourself.

#### Job 1 (all modules that implement this): compute image-specific default params

Expand All @@ -513,7 +513,9 @@ Widgets created with the `_from_params` helpers take their default value (the on

Some modules use `reload_defaults()` to write shared state, such as `dev->chroma`, that other modules depend on. This happens whether or not the module is enabled or has a history entry.

Example: `temperature.c` always computes the camera's as-shot white balance coefficients and its D65 reference coefficients, and writes them into `dev->chroma.as_shot[]` and `dev->chroma.D65coeffs[]`. `channelmixerrgb.c` reads `D65coeffs` in its own `reload_defaults()`, `commit_params()` and `process()` for chromatic adaptation. White balance allows only one instance, which is always present when defaults are loaded, so its `reload_defaults()` runs on every image load. Color calibration therefore has this reference even when the white balance module is disabled or absent from the history.
Example: `temperature.c` always computes the camera's as-shot white balance coefficients and its D65 reference coefficients, and writes them into `dev->chroma.as_shot[]` and `dev->chroma.wb.D65coeffs[]`. `channelmixerrgb.c` reads `D65coeffs` for chromatic adaptation, from `dev->chroma` in its own `reload_defaults()` and from the per-pipe copy `pipe->wb` in `commit_params()` and `process()`. White balance allows only one instance, which is always present when defaults are loaded, so its `reload_defaults()` runs on every image load. Color calibration therefore has this reference even when the white balance module is disabled or absent from the history.

[How white balance and color calibration share state](iop/wb_and_colorcalibration/README.md) lists which module writes and reads each field of `dev->chroma` and `pipe->wb`, and explains why the reverse-order default load is safe.

#### Where `default_params` is consumed

Expand Down
31 changes: 24 additions & 7 deletions dev-doc/Module_Lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,17 +365,25 @@ history up to the given position, updates every module's GUI, then calls

## 4. Cross-module interactions

### 4a. White balance -> color calibration (through `dev->chroma`)
### 4a. White balance -> color calibration (through `dev->chroma` and `pipe->wb`)

White balance (`temperature.c`) and color calibration (`channelmixerrgb.c`) share state in
`dev->chroma`; there is no signal. White balance writes the camera's reference coefficients in
`reload_defaults()` and the coefficients it applies in `commit_params()`; color calibration
reads them in `reload_defaults()`, `commit_params()` and `process()`.
`dev->chroma` and in each pipe's `pipe->wb`. There is no signal. White balance writes the
camera's reference coefficients into `dev->chroma` in `reload_defaults()`. In
`commit_params()`, it writes the coefficients it applies into `dev->chroma` and into its pipe's
`pipe->wb`. Color calibration reads `dev->chroma` in `reload_defaults()`, and `pipe->wb` in
`commit_params()` and `process()`. Its GUI calls `dt_dev_get_gui_wb()`, which asks white
balance, through a proxy, for the values that white balance's current params give.

Commit order does not guarantee that white balance commits first: `synch_all` replays the
history in history order, and `synch_top` re-commits only the top item. Color calibration
therefore recomputes its camera illuminant (`DT_ILLUMINANT_CAMERA`) in `process()`, which runs
after all the pipe's commits are done.
therefore recomputes the two illuminants that depend on white balance, `DT_ILLUMINANT_CAMERA`
and `DT_ILLUMINANT_FROM_WB`, in `process()`, which runs after all the pipe's commits are done.

[How white balance and color calibration
share state](iop/wb_and_colorcalibration/README.md) lists which module writes and reads each
field, explains the per-pipe copy `pipe->wb` and the proxies between the two modules' GUIs, and
shows why the reverse-order default load stays correct.

### 4b. Color-profile change (colorin / colorout)

Expand Down Expand Up @@ -424,7 +432,8 @@ Shared state that a `reload_defaults()` depends on must therefore be **reset to
value before the reverse default-load**. The framework does this for white balance: it calls
`dt_dev_reset_chroma()` just before `_dt_dev_load_pipeline_defaults()`. The neutral white
balance coefficients make color calibration compute its default illuminant from the image's own
as-shot coefficients, instead of from values left by the previous image.
as-shot coefficients, instead of from values left by the previous image. [How white balance
and color calibration share state](iop/wb_and_colorcalibration/README.md) has the details.

---

Expand Down Expand Up @@ -508,9 +517,17 @@ section above.
| `enter` | views/darkroom.c | Darkroom entry: load image, create GUIs, reload defaults, replay history |
| `_dev_load_requested_image` | views/darkroom.c | Darkroom image switch: keep base instances, read history, replay |

The white balance and color calibration symbols (`temperature.*`, `channelmixerrgb.*`, and the
`dev->chroma` helpers) are listed in [How white balance and color calibration share
state](iop/wb_and_colorcalibration/README.md#key-symbols).

## See also

- [IOP_Module_API.md](IOP_Module_API.md): the callback API reference (signatures, the two
[`reload_defaults`](IOP_Module_API.md#reload_defaults---per-image-defaults) jobs).
- [pixelpipe_architecture.md](pixelpipe_architecture.md): pipeline architecture, including the
[ordering-asymmetry note](pixelpipe_architecture.md#pipeline-ordering-asymmetry).
- [How white balance and color calibration share
state](iop/wb_and_colorcalibration/README.md): the data that the two modules exchange through
`dev->chroma` and `pipe->wb`, as a worked example of the ordering rule in
[section 5](#5-pipeline-ordering-asymmetry).
2 changes: 1 addition & 1 deletion dev-doc/New_Module_Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Ensure your module is in the correct place in `iop_order.c`.
- **Scene-referred** modules should generally be earlier.
- **Display-referred** modules should be later.

**Note:** Pipeline ordering affects `commit_params()` execution order. Modules that share state (e.g., via `dev->chroma`) depend on earlier modules having committed first. See [pixelpipe_architecture.md](pixelpipe_architecture.md#pipeline-ordering-asymmetry) for details.
**Note:** `commit_params()` does not always run in pipe order. During history replay, modules commit in history order. A module that reads state written by an earlier module, as color calibration reads white balance's `pipe->wb`, cannot rely on that module having committed first. See [pixelpipe_architecture.md](pixelpipe_architecture.md#pipeline-ordering-asymmetry) for details.

### Shortcuts
By using `dt_bauhaus` widgets, shortcuts are automatically supported.
Expand Down
Loading
Loading