Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2af6dcb
wb: added new param late_correction to temperature.c, exposed as a ch…
kofa73 Dec 10, 2025
3ca7e9d
wb: Updated the rest of the files, but channelmixerrgb is broken, eve…
kofa73 Dec 16, 2025
eb3ab93
wb: Undid changes to channelmixerrgb.c, will go carefully next time
kofa73 Dec 16, 2025
9d48254
wb: Fixed publishing of late_correction in temperature.c
kofa73 Dec 17, 2025
efa8df7
wb: added DT_ILLUMINANT_FROM_WB, separated find_temperature_from_raw_…
kofa73 Dec 17, 2025
9f02277
wb: fix switch-case fall-through in illuminants.h#illuminant_to_xy; v…
kofa73 Feb 28, 2026
4165b02
wb: safe division in _get_d65_correction_ratios
kofa73 Feb 28, 2026
152a693
wb: added _get_corrected_illuminant_xy to deduplicate repeated code
kofa73 Feb 28, 2026
894894b
wb: fix illuminants.h switch-case fall-through introduced when DT_ILL…
kofa73 Mar 6, 2026
e00edaa
wb: renamed find_temperature_from_... to find_illuminant_xy_from_...;…
kofa73 Apr 5, 2026
077fd7a
Doc updates
kofa73 May 31, 2026
98725b6
Doc updates: wb + color calibration
kofa73 May 31, 2026
18eb4dc
wb: removed 'as-shot to reference' (same as 'as shot' + 'prepare data…
kofa73 May 31, 2026
588cece
wb: make sure color calibration / white balance warning is shown when…
kofa73 Jun 6, 2026
2ff8464
wb: don't modify p->x, p->y in _preview_pipe_finished_callback; switc…
kofa73 Jun 7, 2026
e0affe4
wb: minor clean-up in channelmixerrgb (GUI struct field names for cla…
kofa73 Jun 7, 2026
0383a86
wb: opposed.c hash: late_correction is a param of temperature (is bef…
kofa73 Jun 7, 2026
7d2c708
wb: tiny clean-up: unnecessary parens, accidental duplication
kofa73 Jun 7, 2026
14b0925
wb: removed _temp_array_from_params from reload_defaults, d's first 4…
kofa73 Sep 19, 2026
90d817d
wb: removed 2nd, redundant d->preset = DT_IOP_TEMP_AS_SHOT; from temp…
kofa73 Sep 19, 2026
0ed22e7
wb: only enable late_correction for color raw by default
kofa73 Sep 20, 2026
adf3fa4
wb: read chr->adaptation->enabled via snapshot for thread safety
kofa73 Sep 20, 2026
f7ac8cb
wb: add a proxy to allow temperature.c to determine whether CAT is en…
kofa73 Sep 20, 2026
6e76ff8
wb: prepare to store per-pipe WB settings: introduce dt_dev_wb_t
kofa73 Sep 20, 2026
279bf20
wb: publish per-pipe WB settings
kofa73 Sep 23, 2026
342eb5a
wb: read per-pipe WB settings from pipe threads
kofa73 Sep 27, 2026
0925cdd
wb: don't write chr->wb.coeffs/late_correction at processing time
kofa73 Sep 27, 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
32 changes: 31 additions & 1 deletion dev-doc/IOP_Module_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This guide documents the functions that darktable Image Operation (IOP) modules

See also:
- [Pixelpipe Architecture](pixelpipe_architecture.md) for pipeline data flow and caching.
- [Module Lifecycle](Module_Lifecycle.md) for when these callbacks fire and what the system does around them.
- [Introspection System](introspection.md) for parameter management.
- [GUI Architecture](GUI.md) for GUI events, callbacks, and widget reparenting.
- [GUI Threading](GUI_Threading.md) for sharing `gui_data` between the GTK and pipe worker threads.
Expand Down Expand Up @@ -483,7 +484,13 @@ Access in `process_cl()` via `self->global_data`.

### `reload_defaults()` - Per-Image Defaults

Called when switching images. Update defaults based on image properties:
Called when switching images (and on first load). Its purpose is to make `self->default_params` image-specific — most modules have defaults that depend on whether the image is RAW, HDR, monochrome, etc., and those cannot be compile-time constants.

When invoked through the wrapper `dt_iop_reload_defaults()`, the harness 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 (e.g. `_dt_dev_load_pipeline_defaults()`, the per-module Reset button, instance duplication).

**Caveat — direct calls bypass only the framework copy, not the module body.** A few sites invoke `module->reload_defaults(module)` directly instead of through the wrapper (for example `develop.c` calls `temperature->reload_defaults(temperature)` to refresh WB side-effects). 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`) in lockstep with `default_params`, while the WB coefficient array is 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 audit exactly which `self->params` fields the body mutates and sync the rest yourself.

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

```c
void reload_defaults(dt_iop_module_t *self)
Expand All @@ -496,8 +503,31 @@ void reload_defaults(dt_iop_module_t *self)
}
```

Example: `exposure.c` disables itself for non-RAW images by setting `self->default_enabled = FALSE`. The exposure value in `default_params` stays at its static value because exposure is image-type-agnostic beyond that flag.

Common checks: `dt_image_is_raw()`, `dt_image_is_hdr()`, `dt_image_is_ldr()`, `dt_image_is_monochrome()`, `dt_image_is_bayerRGB()`.

#### Job 2 (some modules): write shared inter-module data as a side effect

Some modules use `reload_defaults()` to populate shared state in `dev->chroma` (or similar structures) that other modules depend on. This happens regardless of whether the module is enabled or has a history entry.

Example: `temperature.c` always computes the camera's as-shot WB coefficients and D65 reference coefficients from the image's EXIF data and writes them into `dev->chroma.wb.as_shot[]` and `dev->chroma.wb.D65coeffs[]`. `channelmixerrgb.c` reads these values during its own `commit_params()` to perform chromatic adaptation. If `reload_defaults()` did not run unconditionally, `channelmixerrgb` would have no access to the camera's native WB — even when the white balance module itself is disabled or absent from the history.

For the complete producer/consumer map, the reverse-order default-loading reasoning, and which `dev->chroma` fields each module actually reads, see [iop/wb_and_colorcalibration](iop/wb_and_colorcalibration/README.md).

#### Where `default_params` is consumed

**Initial image load.** Inside `dt_dev_read_history_ext()`, `_dt_dev_load_pipeline_defaults()` calls `reload_defaults()` for every module (in reverse pipe order) and copies each `default_params` into `params`. The function then adds the workflow default modules and any auto-presets, and builds `dev->history` **directly from the database rows** — it does *not* call `dt_dev_pop_history_items`. A module with a history row runs with the saved params from that row; a module without one keeps the image-specific `default_params` just loaded. This is true on both the first open of an image and the reopen of an edited one; the only difference is whether the database has any rows for that module.

**Undo / redo / history-panel navigation.** This is the separate path that uses `dt_dev_pop_history_items_ext()`, which does two passes:

1. Resets all modules: `module->params = module->default_params` — so modules absent from the replayed range start from their image-specific default, not garbage.
2. Replays history entries: `module->params = hist->params` for each entry up to the target position.

So `default_params` is consumed in two ways: as the starting point for modules with no (or not-yet-replayed) history, and as the value the per-module "Reset to defaults" button restores. It also sets the visual "default" markers on GUI sliders (via `dt_bauhaus_slider_set_default()`).

For the full sequence — load order, the `dev->chroma` side effects, and the reverse-iteration hazard — see [Module_Lifecycle.md](Module_Lifecycle.md).

### `change_image()` - Reset GUI State for the New Image

Called on an image switch, after `reload_defaults()` and before the new image's params reach `gui_update()`. Switching image does not tear down every module: each module's base instance — the one with the lowest `multi_priority` — is kept, GUI and all, and reused for the new image (`src/views/darkroom.c`). Anything in `gui_data` that describes the *old* image — a cached curve, a selected region, a pending readout — therefore survives unless you clear it here:
Expand Down
2 changes: 1 addition & 1 deletion dev-doc/Module_Groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Darktable uses a tabs-based interface in the right panel. Each tab corresponds t
- **Technical**: Basic corrections (demosaic, lens, crop).
- **Grading**: Color and tone grading.
- **Effects**: Artistic effects.
- **Quick Access Panel**: (Special case, detailed in `Quick_Access_Panel.md`).
- **Quick Access Panel**: (Special case, detailed in [Quick_Access_Panel.md](Quick_Access_Panel.md)).

## Implementation (`modulegroups.c`)

Expand Down
Loading
Loading