darktable-org / darktable-org/darktable
`agx`: "read exposure" leaves a stale `curve_gamma` in history
@kofa73 is already working on this.
Since Aug 26, 2026.
- Dominant language
- C
- Stars
- 13.1k
- Forks
- 1.4k
- Avg merge
- 22h 14m
- Merged PRs (30d)
- 198
Description
Found by bots via code analysis, 2026-08-25.
Severity: latent. No wrong render while auto_gamma is on, because the pipe
recomputes the value. The stale parameter becomes live the moment the user turns
auto_gamma off, and the image then jumps.
Summary
agx's gui_changed() recomputes curve_gamma from the pivot whenever auto_gamma is
enabled — but the whole body is skipped during a GUI update, and
_read_exposure_params_callback() triggers exactly that path.
The chain:
_read_exposure_params_callback()(src/iop/agx.c:1158-1167) changes
p->range_black_relative_ev/p->range_white_relative_evvia
_adjust_relative_exposure_from_exposure_params(), then callsdt_iop_gui_update(self)
and immediatelydt_dev_add_history_item().dt_iop_gui_update()wrapsmodule->gui_update()inDT_ENTER_GUI_UPDATE()
(src/develop/imageop.c:2442).agx'sgui_update()ends withgui_changed(self, NULL, NULL)(:2259).gui_changed()guards its entire body withif(!DT_IN_GUI_UPDATE())(:1762), so the
block that recomputes the gamma is skipped:
if(g && p->auto_gamma) // :1798-1804
{
tone_mapping_params_t tone_mapping_params;
_set_log_mapping_params(self->params, &tone_mapping_params);
_adjust_pivot(self->params, &tone_mapping_params);
dt_bauhaus_slider_set(g->curve_gamma, tone_mapping_params.curve_gamma);
}
History is then committed with the old p->curve_gamma.
Why it does not show up immediately
While auto_gamma is on, the pipe ignores p->curve_gamma — _adjust_pivot()
(src/iop/agx.c:782-800) recomputes it:
if(p->auto_gamma)
tone_mapping_params->curve_gamma =
pivot_x > 0.f && p->curve_pivot_y_linear_output > 0.f
? log2f(p->curve_pivot_y_linear_output) / log2f(tone_mapping_params->pivot_x)
: p->curve_gamma;
so p->curve_gamma is only a fallback for a degenerate pivot. The slider is also hidden
in this state (gtk_widget_set_visible(g->curve_gamma, !p->auto_gamma), :1742).
The actual consequence
p->curve_gamma sits in history holding a value that no longer matches the pivot. When
the user disables auto_gamma, that stale value becomes the live one and the rendering
jumps discontinuously — with no edit that explains it.
Suggested fix
Recompute curve_gamma in the callback itself rather than relying on gui_changed()
running, or hoist the if(g && p->auto_gamma) block out of the !DT_IN_GUI_UPDATE()
guard. The latter needs care: the block calls dt_bauhaus_slider_set(), whose
value-changed handler is what writes p->curve_gamma, and that handler is itself
suppressed during a GUI update — so hoisting alone would move the slider without updating
the parameter. Writing p->curve_gamma directly and then setting the slider inside
DT_ENTER_GUI_UPDATE()/DT_LEAVE_GUI_UPDATE() is the clearer form.
Check the other callers of _update_pivot_x() for the same pattern.
Environment
Present in 56898c27b8. Static analysis only — nothing verified at runtime.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.