Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dev_history.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2015, 2018 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011 Bruce Guenter.
6 Copyright (C) 2010-2012 Henrik Andersson.
7 Copyright (C) 2011 Karl Mikaelsson.
8 Copyright (C) 2011 Mikko Ruohola.
9 Copyright (C) 2011 Omari Stephens.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2019 Tobias Ellinghaus.
13 Copyright (C) 2012-2014, 2016, 2020-2021 Aldric Renaudin.
14 Copyright (C) 2012 Antony Dovgal.
15 Copyright (C) 2012 Moritz Lipp.
16 Copyright (C) 2012 Richard Wonka.
17 Copyright (C) 2012-2014, 2016-2017 Ulrich Pegelow.
18 Copyright (C) 2013-2022 Pascal Obry.
19 Copyright (C) 2014, 2020 Dan Torop.
20 Copyright (C) 2014 parafin.
21 Copyright (C) 2014-2015 Pedro Côrte-Real.
22 Copyright (C) 2014-2017 Roman Lebedev.
23 Copyright (C) 2016 Alexander V. Smal.
24 Copyright (C) 2017, 2021 luzpaz.
25 Copyright (C) 2018-2019 Edgardo Hoszowski.
26 Copyright (C) 2019 Alexander Blinne.
27 Copyright (C) 2019-2020, 2022-2026 Aurélien PIERRE.
28 Copyright (C) 2019-2021 Diederik Ter Rahe.
29 Copyright (C) 2019-2022 Hanno Schwalm.
30 Copyright (C) 2019 Heiko Bauke.
31 Copyright (C) 2019-2020 Philippe Weyland.
32 Copyright (C) 2020-2021 Chris Elston.
33 Copyright (C) 2020 GrahamByrnes.
34 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
35 Copyright (C) 2020 Hubert Kowalski.
36 Copyright (C) 2020 JP Verrue.
37 Copyright (C) 2020-2021 Ralf Brown.
38 Copyright (C) 2021 paolodepetrillo.
39 Copyright (C) 2021 Sakari Kapanen.
40 Copyright (C) 2022 Martin Bařinka.
41 Copyright (C) 2023 Alynx Zhou.
42 Copyright (C) 2023 lologor.
43 Copyright (C) 2023 Luca Zulberti.
44 Copyright (C) 2023 Ricky Moon.
45 Copyright (C) 2025-2026 Guillaume Stutin.
46 Copyright (C) 2025 Miguel Moquillon.
47
48 darktable is free software: you can redistribute it and/or modify
49 it under the terms of the GNU General Public License as published by
50 the Free Software Foundation, either version 3 of the License, or
51 (at your option) any later version.
52
53 darktable is distributed in the hope that it will be useful,
54 but WITHOUT ANY WARRANTY; without even the implied warranty of
55 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56 GNU General Public License for more details.
57
58 You should have received a copy of the GNU General Public License
59 along with darktable. If not, see <http://www.gnu.org/licenses/>.
60*/
61#include "common/darktable.h"
62#include "common/history.h"
63
64#include "common/undo.h"
66#include "common/image_cache.h"
68#include "common/iop_order.h"
69#include "develop/dev_history.h"
70#include "develop/blend.h"
71#include "develop/develop.h"
72#include "develop/imageop.h"
73#include "develop/masks.h"
74#include "develop/supervisor.h"
75
76#include "gui/presets.h"
77
78#include <inttypes.h>
79
80#include <glib.h>
81
82static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
83 const int modversion, const char *operation, const void *module_params,
84 const int param_length, const gboolean enabled, const void *blendop_params,
85 const int bl_length, const int blendop_version, const int multi_priority,
86 const char *multi_name, const char *preset_name, int *legacy_params,
87 const gboolean presets);
88
96
97gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets);
98
119static void _dev_history_db_row_cb(void *user_data, const int32_t id, const int num, const int modversion,
120 const char *operation, const void *module_params, const int param_length,
121 const gboolean enabled, const void *blendop_params, const int bl_length,
122 const int blendop_version, const int multi_priority, const char *multi_name,
123 const char *preset_name)
124{
126 _process_history_db_entry(ctx->dev, ctx->imgid, id, num, modversion, operation, module_params, param_length, enabled,
127 blendop_params, bl_length, blendop_version, multi_priority, multi_name, preset_name,
128 ctx->legacy_params, ctx->presets);
129}
130
131// returns the first history item with hist->module == module
133{
134 dt_dev_history_item_t *hist_mod = NULL;
135 for(GList *history = g_list_first(history_list); history; history = g_list_next(history))
136 {
137 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
138
139 if(hist->module == module)
140 {
141 hist_mod = hist;
142 break;
143 }
144 }
145 return hist_mod;
146}
147
149{
150 dt_dev_history_item_t *hist_mod = NULL;
151 for(GList *history = g_list_nth(history_list, history_end -1); history; history = g_list_previous(history))
152 {
153 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
154
155 if(hist->module == module)
156 {
157 hist_mod = hist;
158 break;
159 }
160 }
161 return hist_mod;
162}
163
165 const gboolean enabled, const void *params, const int32_t params_size,
166 const dt_develop_blend_params_t *blend_params, GList *forms)
167{
168 if(IS_NULL_PTR(hist) || IS_NULL_PTR(module)) return FALSE;
169
170 if(IS_NULL_PTR(hist->params))
171 {
172 hist->params = g_malloc0(module->params_size);
173 if(IS_NULL_PTR(hist->params)) return FALSE;
174 }
175
176 if(IS_NULL_PTR(hist->blend_params))
177 {
178 hist->blend_params = g_malloc0(sizeof(dt_develop_blend_params_t));
179 if(IS_NULL_PTR(hist->blend_params)) return FALSE;
180 }
181
182 if(!IS_NULL_PTR(hist->forms))
183 {
184 g_list_free_full(hist->forms, (void (*)(void *))dt_masks_form_unref);
185 hist->forms = NULL;
186 }
187 hist->forms = forms;
188
189 module->enabled = enabled;
190 hist->enabled = enabled;
191 hist->module = module;
192 hist->params_size = module->params_size;
193 hist->module_version = module->version();
196 hist->iop_order = module->iop_order;
197 hist->multi_priority = module->multi_priority;
198 g_strlcpy(hist->op_name, module->op, sizeof(hist->op_name));
199 g_strlcpy(hist->multi_name, module->multi_name, sizeof(hist->multi_name));
200
201 const void *src_params = params ? params : module->params;
202 const int32_t src_size = params ? params_size : module->params_size;
203 const int32_t sz = MIN(module->params_size, src_size);
204 if(!IS_NULL_PTR(src_params) && !IS_NULL_PTR(hist->params) && sz > 0) memcpy(hist->params, src_params, sz);
205
206 const dt_develop_blend_params_t *src_blend = blend_params ? blend_params : module->blend_params;
207 if(src_blend && hist->blend_params) memcpy(hist->blend_params, src_blend, sizeof(dt_develop_blend_params_t));
208
209 // Apply to module to keep the hash in sync.
210 if(hist->params && module->params && module->params_size > 0)
211 memcpy(module->params, hist->params, module->params_size);
213
214 dt_iop_compute_module_hash(module, hist->forms);
215 hist->hash = module->hash;
216
217 return TRUE;
218}
219
221{
222 int max_priority = 0;
223 for(const GList *l = g_list_first(dev->iop); l; l = g_list_next(l))
224 {
225 const dt_iop_module_t *m = (const dt_iop_module_t *)l->data;
226 if(strcmp(m->op, op)) continue;
227 max_priority = MAX(max_priority, m->multi_priority);
228 }
229 return max_priority + 1;
230}
231
232dt_iop_module_t *dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
233 const int multi_priority)
234{
235 const char *name = (multi_name && *multi_name) ? multi_name : NULL;
236 dt_iop_module_t *module = dt_iop_get_module_by_instance_name(dev->iop, op, name);
237 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
238 module = dt_iop_get_module_by_op_priority(dev->iop, op, multi_priority);
239 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
240 module = dt_iop_get_module_by_op_priority(dev->iop, op, 0);
241 return module;
242}
243
244dt_iop_module_t *dt_dev_create_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
245 const int multi_priority, gboolean use_next_priority)
246{
248 if(IS_NULL_PTR(base)) base = dt_iop_get_module_by_op_priority(dev->iop, op, -1);
249 if(IS_NULL_PTR(base)) return NULL;
250
251 if((base->flags() & IOP_FLAGS_ONE_INSTANCE) == IOP_FLAGS_ONE_INSTANCE)
252 return base;
253
254 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
255 if(IS_NULL_PTR(module)) return NULL;
256
257 if(dt_iop_load_module(module, base->so, dev))
258 {
259 fprintf(stderr, "[dt_dev_create_module_instance] can't load module %s\n", op);
260 dt_free(module);
261 return NULL;
262 }
263
264 module->instance = base->instance;
265 module->enabled = FALSE;
266 module->multi_priority = use_next_priority ? dt_dev_next_multi_priority_for_op(dev, op) : multi_priority;
267 g_strlcpy(module->multi_name, multi_name ? multi_name : "", sizeof(module->multi_name));
268
269 dev->iop = g_list_append(dev->iop, module);
270 return module;
271}
272
274 const dt_iop_module_t *mod_src)
275{
276 mod_dest->enabled = mod_src->enabled;
277
278 const int32_t sz_dest = mod_dest->params_size;
279 const int32_t sz_src = mod_src->params_size;
280
281 // If both param sizes don't match, we have a serious problem
282 assert(sz_dest == sz_src);
283 if(sz_dest != sz_src) return 1;
284
285 const int32_t sz = MIN(sz_dest, sz_src);
286 if(sz > 0) memcpy(mod_dest->params, mod_src->params, sz);
287
288 if(mod_src->blend_params) dt_iop_commit_blend_params(mod_dest, mod_src->blend_params);
289
290 if(dev_src && dt_masks_copy_used_forms_for_module(dev_dest, dev_src, mod_src)) return 1;
291 return 0;
292}
293
295 const dt_dev_history_item_t *hist_src, dt_iop_module_t *mod_dest,
296 dt_dev_history_item_t **out_hist)
297{
298 if(IS_NULL_PTR(hist_src) || IS_NULL_PTR(hist_src->module) || IS_NULL_PTR(mod_dest) || IS_NULL_PTR(out_hist))
299 {
301 "[dt_dev_history_item_from_source_history_item] invalid input: hist=%s hist_module=%s dest=%s out=%s\n",
302 hist_src ? "yes" : "no", hist_src && hist_src->module ? "yes" : "no",
303 mod_dest ? "yes" : "no", out_hist ? "yes" : "no");
304 return 1;
305 }
306
308 if(IS_NULL_PTR(hist))
309 {
311 "[dt_dev_history_item_from_source_history_item] allocation failed: src=%s multi='%s'\n",
312 hist_src->module->op, hist_src->module->multi_name);
313 return 1;
314 }
315
316 if(dt_masks_copy_used_forms_for_module(dev_dest, dev_src, hist_src->module))
317 {
319 "[dt_dev_history_item_from_source_history_item] Forms copy failed: src=%s multi='%s'\n",
320 hist_src->module->op, hist_src->module->multi_name);
322 return 1;
323 }
324
325 gboolean raster_used = FALSE;
326 gboolean drawn_used = FALSE;
327 gboolean parametric_used = FALSE;
328 GList *forms_snapshot = NULL;
329 if(dt_iop_module_needs_mask_history_ext(hist_src->module, &raster_used, &drawn_used, &parametric_used))
330 {
331 if(drawn_used)
332 {
333 forms_snapshot = dt_masks_snapshot_current_forms(dev_dest, FALSE);
334 if(IS_NULL_PTR(forms_snapshot))
335 {
337 "[dt_dev_history_item_from_source_history_item] %s '%s' uses drawn mask but there is no destination mask forms to snapshot\n",
338 hist_src->module->op, hist_src->module->multi_name);
339
341 return 1;
342 }
343 }
344 }
345
346 if(!dt_dev_history_item_update_from_params(dev_dest, hist, mod_dest, hist_src->enabled, hist_src->params,
347 hist_src->module->params_size, hist_src->blend_params, forms_snapshot))
348 {
350 "[dt_dev_history_item_from_source_history_item] params update failed: src=%s multi='%s' "
351 "dest=%s multi='%s' src_params=%d dest_params=%d\n",
352 hist_src->module->op, hist_src->module->multi_name, mod_dest->op, mod_dest->multi_name,
353 hist_src->module->params_size, mod_dest->params_size);
355 return 1;
356 }
357
358 *out_hist = hist;
359 return 0;
360}
361
362int dt_dev_merge_history_into_image(dt_develop_t *dev_src, int32_t dest_imgid, const GList *mod_list,
363 gboolean merge_iop_order, const dt_history_merge_strategy_t mode,
364 const gboolean paste_instances, const char *source_label,
365 dt_hm_batch_state_t *batch)
366{
367 if(dest_imgid <= 0) return 1;
368 if(IS_NULL_PTR(mod_list)) return 0;
369
370 dt_develop_t dev_dest = { 0 };
371 dt_dev_init(&dev_dest, FALSE);
372 const gboolean first_run = dt_dev_reload_history_items(&dev_dest, dest_imgid);
373
374 if(first_run)
375 {
376 /* Match the persistent state produced by opening the destination in darkroom
377 * after a history deletion: the first-run defaults, auto-presets, image
378 * flags and resulting module order must exist before paste/style merging.
379 */
380 dt_image_t *image = dt_image_cache_get(darktable.image_cache, dest_imgid, 'w');
381 if(!IS_NULL_PTR(image))
382 {
383 *image = dev_dest.image_storage;
385 }
386
387 dt_dev_write_history_ext(&dev_dest, dest_imgid);
388 }
389
390 /* Honor the high-level "use source pipeline order" choice on every image, including freshly recreated
391 * destinations. The topological solve still falls back to destination order when source constraints are
392 * unsatisfiable, so this stays safe while making the batch decision apply uniformly. */
393 const int ret_val = dt_history_merge(&dev_dest, dev_src, dest_imgid, mod_list, merge_iop_order, mode,
394 paste_instances, source_label, batch);
395
396 if(ret_val == 0)
397 {
399 dt_dev_write_history(&dev_dest, FALSE);
400 }
401
402 dt_dev_cleanup(&dev_dest);
403 return ret_val;
404}
405
414{
415 dt_dev_history_item_t *hist_mod = NULL;
416 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
417 {
418 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
419 if(!hist || !hist->module) continue;
420
421 if(strcmp(hist->module->op, module->op) == 0)
422 {
423 hist_mod = hist;
424 break;
425 }
426 }
427 return hist_mod;
428}
429
442 const dt_iop_module_t *mod_src,
443 gboolean *created,
444 gboolean *reused_base)
445{
446 *created = FALSE;
447 *reused_base = FALSE;
448
449 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
450 return dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
451
452 dt_iop_module_t *module
453 = dt_dev_get_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority);
454 if(module) return module;
455
456 if(_search_history_by_op(dev_dest, mod_src) == NULL)
457 {
458 module = dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
459 if(!IS_NULL_PTR(module))
460 {
461 *reused_base = TRUE;
462 return module;
463 }
464 }
465
466 module = dt_dev_create_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority, FALSE);
467 if(module) *created = TRUE;
468
469 return module;
470}
471
472// dev_src is used only to copy masks, if no mask will be copied it can be null
474{
475 gboolean created = FALSE;
476 gboolean reused_base = FALSE;
477 dt_iop_module_t *module = _history_merge_resolve_dest_instance(dev_dest, mod_src, &created, &reused_base);
478 if(IS_NULL_PTR(module)) return 0;
479
480 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
481 {
482 dt_print(DT_DEBUG_HISTORY, "[dt_history_merge_module_into_history] %s (%s) will be overriden in target history by parameters from source history\n",
483 mod_src->name(), mod_src->multi_name);
484 }
485 else if(created)
486 {
487 dt_print(DT_DEBUG_HISTORY, "[dt_history_merge_module_into_history] %s (%s) will be inserted as a new instance in target history\n",
488 mod_src->name(), mod_src->multi_name);
489 }
490 else if(reused_base)
491 {
492 dt_print(DT_DEBUG_HISTORY, "[dt_history_merge_module_into_history] %s (%s) will be enabled in target history with parameters from source history\n",
493 mod_src->name(), mod_src->multi_name);
494 }
495
496 g_strlcpy(module->multi_name, mod_src->multi_name, sizeof(module->multi_name));
497
498 if(dt_dev_copy_module_contents(dev_dest, dev_src, module, mod_src)) return 0;
499
500 dt_dev_add_history_item_ext(dev_dest, module, FALSE, FALSE);
501
502 return 1;
503}
512GList *dt_history_duplicate(GList *hist)
513{
514 GList *result = NULL;
515 for(GList *h = g_list_first(hist); h; h = g_list_next(h))
516 {
517 const dt_dev_history_item_t *old = (dt_dev_history_item_t *)(h->data);
519
520 memcpy(new, old, sizeof(dt_dev_history_item_t));
521 dt_atomic_set_int(&new->refcount, 1); // independent deep copy, not a shared reference
522
523 dt_iop_module_t *module = (old->module) ? old->module : dt_iop_get_module(old->op_name);
524
525 if(old->params && old->params_size > 0)
526 {
527 new->params = malloc(old->params_size);
528 memcpy(new->params, old->params, old->params_size);
529 }
530
531 if(IS_NULL_PTR(module))
532 fprintf(stderr, "[_duplicate_history] can't find base module for %s\n", old->op_name);
533
534 if(old->blend_params && old->blendop_params_size > 0)
535 {
536 new->blend_params = malloc(old->blendop_params_size);
537 memcpy(new->blend_params, old->blend_params, old->blendop_params_size);
538 }
539
540 if(old->forms)
541 {
542 // Share references with old->forms instead of deep-copying: forms are refcounted
543 // and cloned on write (dt_masks_cow_touch), not on snapshot (see masks_history.h).
544 new->forms = g_list_copy(old->forms);
545 for(GList *form_node = new->forms; form_node; form_node = g_list_next(form_node))
546 dt_masks_form_ref((dt_masks_form_t *)form_node->data);
547 }
548
549 result = g_list_prepend(result, new);
550 }
551
552 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
553}
554
563
565{
566 dt_iop_module_t *module;
568};
569
577static void _history_invalidate_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item)
578{
579 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
580 dt_undo_history_t *hist = (dt_undo_history_t *)item;
581 dt_dev_invalidate_history_module(hist->before_snapshot, module);
582 dt_dev_invalidate_history_module(hist->after_snapshot, module);
583}
584
590
596static void _history_undo_data_free(gpointer data)
597{
598 dt_undo_history_t *hist = (dt_undo_history_t *)data;
599 if(IS_NULL_PTR(hist)) return;
600 g_list_free_full(hist->before_snapshot, dt_dev_free_history_item);
601 hist->before_snapshot = NULL;
602 g_list_free_full(hist->after_snapshot, dt_dev_free_history_item);
603 hist->after_snapshot = NULL;
604 g_list_free_full(hist->before_iop_order_list, dt_free_gpointer);
605 hist->before_iop_order_list = NULL;
606 g_list_free_full(hist->after_iop_order_list, dt_free_gpointer);
607 hist->after_iop_order_list = NULL;
608 dt_free(hist);
609}
610
622static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
623{
624 if(type != DT_UNDO_HISTORY) return;
625
626 dt_develop_t *dev = (dt_develop_t *)user_data;
627 dt_undo_history_t *hist = (dt_undo_history_t *)data;
628 if(IS_NULL_PTR(dev) || IS_NULL_PTR(hist)) return;
629
630 GList *snapshot = (action == DT_ACTION_UNDO) ? hist->before_snapshot : hist->after_snapshot;
631 const int history_end = (action == DT_ACTION_UNDO) ? hist->before_end : hist->after_end;
632
633 GList *iop_order_list
634 = (action == DT_ACTION_UNDO) ? hist->before_iop_order_list : hist->after_iop_order_list;
635
636 GList *history_temp = dt_history_duplicate(snapshot);
637 GList *iop_order_temp = dt_ioppr_iop_order_copy_deep(iop_order_list);
638
641 dev->history = history_temp;
642 dt_dev_set_history_end_ext(dev, history_end);
643 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
644 dev->iop_order_list = iop_order_temp;
648
650 // TODO: check if we need to rebuild the full pipeline and do it only if needed
652
653 if(dev->gui_module)
654 {
659 if(bd)
660 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->showmask),
662 }
663
664 // Ensure all UI pieces (history treeview, iop order, etc.) resync after undo/redo.
665 // Undo callbacks bypass dt_dev_undo_end_record(), so we need to raise the change signal here.
666 if(darktable.gui && dev->gui_attached && dev == darktable.develop)
668}
669
677
697
705
740
741
750{
751 GList *history = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev));
752 while(history)
753 {
754 // We need to use a while because we are going to dynamically remove entries at the end
755 // of the list, so we can't know the number of iterations
756 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
757 if(IS_NULL_PTR(hist) || IS_NULL_PTR(hist->module))
758 {
760 "[dt_dev_add_history_item_ext] archival history item %s at %i is past history limit (%i) and will be kept\n",
761 hist ? hist->op_name : "(null)", g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
762 history = g_list_next(history);
763 continue;
764 }
765
766 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history item %s at %i is past history limit (%i)\n",
767 hist->module->op, g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
768
769 // In case user wants to insert new history items before auto-enabled or mandatory modules,
770 // we forbid it, unless we already have at least one lower history entry.
771
772 // Check if an earlier instance of mandatory module exists
773 gboolean earlier_entry = FALSE;
774 if((hist->module->hide_enable_button || hist->module->default_enabled))
775 {
776 for(GList *prior_history = g_list_previous(history); prior_history;
777 prior_history = g_list_previous(prior_history))
778 {
779 dt_dev_history_item_t *prior_hist = (dt_dev_history_item_t *)(prior_history->data);
780 if(!prior_hist || !prior_hist->module) continue;
781 if(prior_hist->module->so == hist->module->so)
782 {
783 earlier_entry = TRUE;
784 break;
785 }
786 }
787 }
788
789 // In case we delete the current link, we need to update the incrementer now
790 // to not loose the reference
791 GList *link = history;
792 history = g_list_next(history);
793
794 // Finally: attempt removing the obsoleted entry
795 if((!hist->module->hide_enable_button && !hist->module->default_enabled)
796 || earlier_entry)
797 {
798 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] removing obsoleted history item: %s at %i\n", hist->module->op, g_list_index(dev->history, hist));
800 dt_supervisor_history(DT_SV_DELETE, hist->hash, hist->module->op, hist->module->multi_priority,
801 hist->module->multi_name, hist->module->iop_order,
802 g_list_index(dev->history, hist), dev->image_storage.id, hist->enabled,
803 hist->module, hist->params, hist->blend_params, hist->forms);
805 dev->history = g_list_delete_link(dev->history, link);
806 }
807 else
808 {
809 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] obsoleted history item will be kept: %s at %i\n", hist->module->op, g_list_index(dev->history, hist));
810 }
811 }
812}
813
814gboolean dt_dev_add_history_item_ext(dt_develop_t *dev, struct dt_iop_module_t *module, gboolean enable,
815 gboolean force_new_item)
816{
817 // If this history item is the first for this module,
818 // we need to notify the pipeline that its topology may change (aka insert a new node).
819 // Since changing topology is expensive, we want to do it only when needed.
820 gboolean add_new_pipe_node = FALSE;
821
822 if(IS_NULL_PTR(module))
823 {
824 // module = NULL means a mask was changed from the mask manager and that's where this function is called.
825 // Find it now, even though it is not enabled and won't be.
826 module = dt_masks_get_mask_manager(dev);
827 if(!IS_NULL_PTR(module))
828 {
829 // Mask manager is an IOP that never processes pixel aka it's an ugly hack to record mask history
830 force_new_item = FALSE;
831 enable = FALSE;
832 }
833 else
834 {
835 return add_new_pipe_node;
836 }
837 }
838
839 // look for leaks on top of history
841
842 // Check if the current module to append to history is actually the same as the last one in history,
843 GList *last = g_list_last(dev->history);
844 gboolean new_is_old = FALSE;
845 if(last && last->data && !force_new_item)
846 {
847 dt_dev_history_item_t *last_item = (dt_dev_history_item_t *)last->data;
848 dt_iop_module_t *last_module = last_item->module;
849 new_is_old = dt_iop_check_modules_equal(module, last_module);
850 add_new_pipe_node = FALSE;
851 }
852 else
853 {
854 const dt_dev_history_item_t *previous_item =
855 dt_dev_history_get_last_item_by_module(dev->history, module, g_list_length(dev->history));
856 // check if NULL first or prevous_item->module will segfault
857 // We need to add a new pipeline node if:
858 add_new_pipe_node = (IS_NULL_PTR(previous_item)) // it's the first history entry for this module
859 || (previous_item->enabled != module->enabled); // the previous history entry is disabled
860 // if previous history entry is disabled and we don't have any other entry,
861 // it is possible the pipeline will not have this node.
862 }
863
865 const gboolean is_new_item = force_new_item || !new_is_old;
866 if(is_new_item)
867 {
868 // Create a new history entry
870 dev->history = g_list_append(dev->history, hist);
871 hist->num = g_list_index(dev->history, hist);
872 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] new history entry added for %s at position %i\n",
873 module->name(), hist->num);
874 }
875 else
876 {
877 // Reuse previous history entry. Copy-on-write: a slow reader (pipe resync snapshot, DB
878 // write job snapshot) may still be holding a reference to this exact item -- clone before
879 // mutating if so, never mutate a shared item in place.
880 hist = dt_dev_history_cow_touch(dev, (dt_dev_history_item_t *)last->data);
881
882 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history entry reused for %s at position %i\n",
883 module->name(), hist->num);
884 }
885
886 // Always resync history with all module internals
887 if(enable) module->enabled = TRUE;
888
889 // Include masks if module supports blending and masks are in use, or if it's the mask manager.
890 const gboolean include_masks = dt_iop_module_needs_mask_history(module);
891
892 GList *forms_snapshot = NULL;
893 if(include_masks)
894 {
895 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] committing masks for module %s at history position %i\n", module->name(), hist->num);
896 // FIXME: this copies ALL drawn masks AND masks groups used by all modules to any module history using masks.
897 // Kudos to the idiots who thought it would be reasonable. Expect database bloating and perf penalty.
898 forms_snapshot = dt_masks_snapshot_current_forms(dev, TRUE);
899 }
900 // else forms_snapshot stays NULL
901
902 // Capture the previous parameter hash: an in-place reuse rewrites it below,
903 // which the supervisor must see as a rekey (delete old hash + add new hash).
904 const uint64_t old_param_hash = hist->hash;
905
906 // Fill history item and recompute hash (also applies params/blend_params to module to keep hash consistent).
907 dt_dev_history_item_update_from_params(dev, hist, module, module->enabled, module->params, module->params_size,
908 module->blend_params, forms_snapshot);
909
910 if(include_masks && hist->forms)
911 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] masks committed for module %s at history position %i\n", module->name(), hist->num);
912 else if(include_masks)
913 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] masks NOT committed for module %s at history position %i\n", module->name(), hist->num);
914
915 // It is assumed that the last-added history entry is always on top
916 // so its cursor index is always equal to the number of elements,
917 // keeping in mind that history_end = 0 is the raw image, aka not a dev->history GList entry.
918 // So dev->history_end = index of last history entry + 1 = length of history
919 dt_dev_set_history_end_ext(dev, g_list_length(dev->history));
920
922 {
923 if(is_new_item)
925 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
926 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
927 else if(old_param_hash != hist->hash)
928 {
929 // in-place overwrite changed the hash: delete old key, add new key, then
930 // refresh the new entry's current state (enabled/index may have changed).
931 dt_supervisor_rekey(old_param_hash, hist->hash);
933 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
934 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
935 }
936 else
938 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
939 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
940 }
941
942 return add_new_pipe_node;
943}
944
946{
947 uint64_t hash = 5381;
948 for(GList *hist = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev) - 1);
949 hist;
950 hist = g_list_previous(hist))
951 {
952 dt_dev_history_item_t *item = (dt_dev_history_item_t *)hist->data;
953 hash = dt_hash(hash, (const char *)&item->hash, sizeof(uint64_t));
954 }
955 dt_print(DT_DEBUG_HISTORY, "[dt_dev_history_get_hash] history hash: %" PRIu64 ", history end: %i, items %i\n", hash, dt_dev_get_history_end_ext(dev), g_list_length(dev->history));
956 return hash;
957}
958
959
960// The next 2 functions are always called from GUI controls setting parameters
961// This is why they directly start a pipeline recompute.
962// Otherwise, please keep GUI and pipeline fully separated.
963
964void dt_dev_add_history_item_real(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean redraw)
965{
966 gboolean add_new_pipe_node = FALSE;
967
970
972 // Measures time actually spent blocked waiting for the lock (e.g. behind the
973 // background dt_dev_write_history() job holding it as reader) -- not time spent
974 // doing work while holding it. Threshold avoids logging the common sub-ms case.
975 const double _wrlock_wait_start = dt_get_wtime();
977 const double _wrlock_wait_ms = (dt_get_wtime() - _wrlock_wait_start) * 1000.0;
978 if(_wrlock_wait_ms > 1.0)
980 "[dt_dev_add_history_item_real] tid %lu (GUI thread) blocked %.2f ms acquiring history_mutex\n",
981 (unsigned long)pthread_self(), _wrlock_wait_ms);
982 add_new_pipe_node = dt_dev_add_history_item_ext(dev, module, enable, FALSE);
985
986 // Run the delayed post-commit actions if implemented
987 if(!IS_NULL_PTR(module) && !IS_NULL_PTR(module->post_history_commit)) module->post_history_commit(module);
988
989 // Republish any dev->proxy state this module derives from its params (e.g. temperature's WB
990 // coeffs) on the main thread, before the pipeline recompute below. This covers user edits and
991 // resets; pipelines only read dev->proxy. (Bulk history loads go through pop_history_items_ext.)
992 if(!IS_NULL_PTR(module) && !IS_NULL_PTR(module->commit_proxy)) module->commit_proxy(module);
993
994 // Figure out if the current history item includes masks/forms
995 GList *last_history = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev) - 1);
996 dt_dev_history_item_t *hist = NULL;
997 gboolean has_forms = FALSE;
998 if(last_history)
999 {
1000 hist = (dt_dev_history_item_t *)last_history->data;
1001 has_forms = (!IS_NULL_PTR(hist->forms));
1002 }
1003
1004 // We don't update history hash in dt_dev_add_history_item_ext
1005 // because it can be called within loops, so that can be expensive.
1007 if(dev->image_storage.id > 0)
1008 {
1010 if(cache_img)
1011 {
1012 cache_img->history_hash = dt_dev_get_history_hash(dev);
1014 }
1015 }
1016
1017 // Recompute pipeline last
1018 const gboolean has_raster = module && dt_iop_module_has_raster_mask(module);
1019 if(!IS_NULL_PTR(module) && !(has_forms || has_raster) && !add_new_pipe_node)
1020 {
1021 // If we have a module and it doesn't use drawn or raster masks,
1022 // we only need to resync the top-most history item with pipeline
1024 }
1025 else
1026 {
1027 // We either don't have a module, meaning we have the mask manager, or
1028 // we have a module and it uses masks (drawn or raster), or the current
1029 // history commit changes the pipeline topology. The latter happens for
1030 // example when enabling/disabling a module or when a module gets its first
1031 // history entry. In all these cases, updating only the top-most synced
1032 // history tail is insufficient because the set of active pipe nodes itself
1033 // may differ from the previous run.
1034 // Because masks can affect several modules anywhere, not necessarily sequentially,
1035 // we need a full resync of all pipeline with history.
1036 // Note that the blendop params (thus their hash) references the raster mask provider
1037 // in its consumer, and the consumer in its provider. So updating the whole pipe
1038 // resyncs the cumulative hashes too, and triggers a new recompute from the provider on update.
1040 }
1041
1043
1044 if(!IS_NULL_PTR(darktable.gui) && dev->gui_attached && !IS_NULL_PTR(module))
1045 {
1046 // If module params change the geometry of the ROI,
1047 // update immediately so we avoid drawing glitches.
1048 if(module->modify_roi_in || module->modify_roi_out)
1050
1051
1052 // Changing a parameter of a disabled module enables it,
1053 // so update the GUI toggle state to reflect it.
1054 dt_gui_freeze_begin(); // don't run GUI callbacks when setting GUI state
1057 }
1058
1059 // Save history straight away. Regular GUI edits are the only place where
1060 // we accept an asynchronous write because `dev` is the long-lived darkroom
1061 // context and there is no immediate DB read on the same control path.
1063}
1064
1066{
1068 if(IS_NULL_PTR(item)) return NULL;
1069 dt_atomic_set_int(&item->refcount, 1);
1070 return item;
1071}
1072
1073void dt_dev_free_history_item(gpointer data)
1074{
1076 if(IS_NULL_PTR(item)) return; // nothing to free
1077
1078 // Despite the name (kept so every existing g_list_free_full/GDestroyNotify call site gets
1079 // refcount-safety for free), this is now dev_history's unref: dev->history and a form cloned
1080 // by dt_dev_history_cow_touch each hold their own reference. Only the last one out actually
1081 // frees the buffers.
1082 if(dt_atomic_sub_int(&item->refcount, 1) != 1) return;
1083
1084 dt_free(item->params);
1085 dt_free(item->blend_params);
1086 g_list_free_full(item->forms, (void (*)(void *))dt_masks_form_unref);
1087 item->forms = NULL;
1088 dt_free(item);
1089}
1090
1092{
1093 if(IS_NULL_PTR(item)) return NULL;
1094 dt_atomic_add_int(&item->refcount, 1);
1095 return item;
1096}
1097
1098// Same per-field duplication as dt_history_duplicate(), factored out for cow-touch's
1099// single-item clone: fresh struct, own params/blend_params buffers, forms shared by reference
1100// (already refcounted, cloned on write by dt_masks_cow_touch -- not on this snapshot).
1102{
1104 memcpy(new_item, old, sizeof(dt_dev_history_item_t));
1105 dt_atomic_set_int(&new_item->refcount, 1); // fresh object: reset, don't inherit old's refcount
1106
1107 new_item->params = NULL;
1108 if(old->params && old->params_size > 0)
1109 {
1110 new_item->params = malloc(old->params_size);
1111 memcpy(new_item->params, old->params, old->params_size);
1112 }
1113
1114 new_item->blend_params = NULL;
1115 if(old->blend_params && old->blendop_params_size > 0)
1116 {
1117 new_item->blend_params = malloc(old->blendop_params_size);
1118 memcpy(new_item->blend_params, old->blend_params, old->blendop_params_size);
1119 }
1120
1121 new_item->forms = NULL;
1122 if(old->forms)
1123 {
1124 new_item->forms = g_list_copy(old->forms);
1125 for(GList *form_node = new_item->forms; form_node; form_node = g_list_next(form_node))
1126 dt_masks_form_ref((dt_masks_form_t *)form_node->data);
1127 }
1128
1129 return new_item;
1130}
1131
1133{
1134 if(IS_NULL_PTR(dev) || IS_NULL_PTR(hist)) return hist;
1135 if(dt_atomic_get_int(&hist->refcount) <= 1) return hist;
1136
1137 // dt_pthread_rwlock_wrlock is same-thread-recursive (dtpthread.h), so this is safe whether
1138 // or not the caller already holds history_mutex as writer (dt_dev_add_history_item_real
1139 // does; other callers of dt_dev_add_history_item_ext may not).
1141
1142 GList *node = g_list_find(dev->history, hist);
1143 if(IS_NULL_PTR(node))
1144 {
1145 // Not (yet) a member of dev->history: nothing to splice.
1147 return hist;
1148 }
1149
1151 node->data = clone;
1152
1153 // Raw pointer cache outside dev->history: each pipe's own last-synced-item marker (used by
1154 // dt_dev_pixelpipe_synch_top to bound the resync range). dt_dev_pixelpipe_change() holds
1155 // history_mutex for its whole resync, same as this splice, so a plain compare-and-assign is
1156 // enough -- no concurrent access is possible.
1157 dt_dev_pixelpipe_t *const pipes[] = { dev->pipe, dev->preview_pipe, dev->virtual_pipe };
1158 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
1159 if(pipes[i] && pipes[i]->last_history_item == hist)
1160 pipes[i]->last_history_item = clone;
1161
1163
1164 dt_dev_free_history_item(hist); // releases dev->history's old reference (unref semantics)
1165
1166 return clone;
1167}
1168
1170{
1171 g_list_free_full(snapshot, dt_dev_free_history_item);
1172}
1173
1175{
1176 if(IS_NULL_PTR(dev->history)) return;
1177 // Canonical history is being cleared (image unload, compress rebuild, ...):
1178 // mark every item deleted in the supervisor before freeing the structs.
1180 for(GList *l = dev->history; l; l = g_list_next(l))
1181 {
1182 const dt_dev_history_item_t *h = (const dt_dev_history_item_t *)l->data;
1183 if(h && h->module)
1184 dt_supervisor_history(DT_SV_DELETE, h->hash, h->module->op, h->module->multi_priority,
1185 h->module->multi_name, h->module->iop_order, h->num,
1186 dev->image_storage.id, h->enabled, h->module, h->params, h->blend_params, h->forms);
1187 }
1188 g_list_free_full(g_steal_pointer(&dev->history), dt_dev_free_history_item);
1189 dev->history = NULL;
1190}
1191
1192gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
1193{
1194 // Recreate the whole history from scratch.
1195 // Backend only: GUI updates and pixelpipe rebuilds need to be triggered by callers.
1198 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
1202 return first_run;
1203}
1204
1205
1220{
1221 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1222 {
1223 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1225
1226 // Reset the enabled state to the module default too. Neither dt_iop_load_default_params()
1227 // nor dt_iop_reload_defaults() touch it, so without this a module keeps whatever enabled
1228 // state the previous history point left it in. The history overlay below (_history_to_module)
1229 // only re-enables modules that have an entry within the current slice; a module whose only
1230 // history entry lies *after* history_end would otherwise stay stale-enabled while parked at
1231 // INT_MAX, becoming an enabled ghost node after gamma that the pixelpipe format pass disables
1232 // for "unexpected input buffer format" (issue #961). This restores the reset that was dropped
1233 // when the reload path was refactored (commit 892dad90a9).
1234 module->enabled = module->default_enabled;
1235
1236 if(module->multi_priority == 0)
1237 module->iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, module->op, module->multi_priority);
1238 else
1239 module->iop_order = INT_MAX;
1240
1241 // Last chance & desperate attempt at enabling/disabling critical modules
1242 // when history is garbled - This might prevent segfaults on invalid data
1243 if(module->force_enable)
1244 module->enabled = (module->force_enable(module, module->enabled) != 0);
1245
1246 // make sure that always-on modules are always on. duh.
1247 if(module->default_enabled == 1 && module->hide_enable_button == 1)
1248 module->enabled = TRUE;
1249
1250 dt_iop_compute_module_hash(module, dev->forms);
1251 }
1252}
1253
1254// Dump the content of an history entry into its associated module params, blendops, etc.
1263static inline void _history_to_module(const dt_dev_history_item_t *const hist, dt_iop_module_t *module)
1264{
1265 module->enabled = (hist->enabled != 0);
1266
1267 // Update IOP order stuff, that applies to all modules regardless of their internals
1268 module->iop_order = hist->iop_order;
1270
1271 // Copy instance name
1272 g_strlcpy(module->multi_name, hist->multi_name, sizeof(module->multi_name));
1273
1274 // Copy params from history entry to module internals
1275 memcpy(module->params, hist->params, module->params_size);
1277
1278 // Get the module hash
1279 dt_iop_compute_module_hash(module, hist->forms);
1280}
1281
1282
1284{
1285 dt_print(DT_DEBUG_HISTORY, "[dt_dev_pop_history_items_ext] loading history entries into modules...\n");
1286
1287 // Ensure `dev->image_storage` is up-to-date before modules reload their defaults.
1288 // This avoids using incomplete RAW metadata (WB coeffs, matrices) on newly-inited images.
1290
1291 // Reset every module to its default params first; the history overlay below then overwrites the
1292 // ones that have entries. Per-image defaults were already computed at history init / instance
1293 // creation, so we do NOT recompute them here (no reload_defaults: GUI-unsafe, and it re-derived
1294 // cross-module defaults against a pipeline-populated proxy). See _dt_dev_modules_reload_defaults.
1296
1297 const int history_end = dt_dev_get_history_end_ext(dev);
1298
1299 // go through history up to history_end and set modules params
1300 GList *history = g_list_first(dev->history);
1301 GList *forms = NULL;
1302 for(int i = 0; i < history_end && history; i++)
1303 {
1304 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1305 dt_iop_module_t *module = hist->module;
1306 if(module) _history_to_module(hist, module);
1307
1308 // Update the reference to the form snapshot that doesn't belong
1309 // conceptually to history items
1310 if(hist->forms) forms = hist->forms;
1311
1312 history = g_list_next(history);
1313 }
1314
1315 // Nuke dev->forms and replace it with the last hist->forms in history.
1317
1318 // History (metadata + presets) is now fully applied to module params. Let modules publish any
1319 // dev->proxy state derived from their EFFECTIVE params (e.g. temperature's WB coeffs, consumed by
1320 // channelmixerrgb) on this main/history thread, BEFORE the pipeline resync below runs. dev->proxy
1321 // is a GUI/main-thread inter-module channel (pipelines must not touch it); this is the single
1322 // main-thread publish point for bulk history loads.
1323 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1324 {
1325 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1326 if(module->commit_proxy) module->commit_proxy(module);
1327 }
1328
1329 dt_ioppr_resync_pipeline(dev, 0, "dt_dev_pop_history_items_ext end", TRUE);
1330
1331 // Reloading defaults might have changed the global history hash
1333
1334 // Update darkroom sizes in case clipping & distortion changed.
1335 // This is now handled by the wrapper after releasing the history lock.
1336}
1337
1339{
1344 // Update darkroom sizes after releasing the history lock to avoid deadlocks.
1347}
1348
1350{
1351 if(!dev->gui_attached) return;
1352
1353 // Match the live module instances to the reloaded history before touching GTK.
1354 // This loop may remove obsolete instances or expose instances newly created
1355 // while reading a style/history from the database.
1359
1361
1362 for(GList *module = g_list_first(dev->iop); module; module = g_list_next(module))
1363 {
1364 dt_iop_module_t *mod = (dt_iop_module_t *)(module->data);
1365
1366 // History reload is backend-only and creates new multi-instances without
1367 // GTK state. Attach every missing GUI here, after releasing history_mutex,
1368 // so styles and global history actions expose their complete module set.
1369 if(!dt_iop_is_hidden(mod) && IS_NULL_PTR(mod->expander))
1370 {
1371 if(IS_NULL_PTR(mod->widget)) dt_iop_gui_init(mod);
1373 }
1374
1375 // Parameters, enabled state, headers and blending controls may all have
1376 // changed, therefore refresh every module rather than only history entries.
1377 dt_iop_gui_update(mod);
1378 }
1379
1382
1384}
1385
1387{
1388 if(!dev->gui_attached) return;
1389
1390 if(rebuild)
1392 else
1394}
1395
1397{
1399 && g_list_find(imgs, GINT_TO_POINTER(darktable.develop->image_storage.id));
1400}
1401
1403{
1404 if(IS_NULL_PTR(dev)) return;
1405
1408 // Re-derive dev->roi.processed_{width,height} (and the natural scale) from the new
1409 // history through the virtual pipe. Without this, a history change that alters the
1410 // final image geometry (e.g. removing a crop/ashift step) still renders at the old,
1411 // stale ROI -- same fix as _history_apply_history_end() in libs/history.c.
1415}
1416
1422static void _cleanup_history(const int32_t imgid)
1423{
1425}
1426
1427guint dt_dev_mask_history_overload(GList *dev_history, guint threshold)
1428{
1429 // Count all the mask forms used x history entries, up to a certain threshold.
1430 // Stop counting when the threshold is reached, for performance.
1431 guint states = 0;
1432 for(GList *history = g_list_first(dev_history); history; history = g_list_next(history))
1433 {
1434 dt_dev_history_item_t *hist_item = (dt_dev_history_item_t *)(history->data);
1435 states += g_list_length(hist_item->forms);
1436 if(states > threshold) break;
1437 }
1438 return states;
1439}
1440
1441void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
1442{
1443 if(IS_NULL_PTR(dev) || imgid <= 0) return;
1444
1445 if(darktable.gui && dev->gui_attached)
1446 {
1447 const guint states = dt_dev_mask_history_overload(dev->history, 250);
1448 if(states > 250)
1449 dt_toast_log(_("Image #%i history is storing %d mask states. n"
1450 "Consider compressing history and removing unused masks to keep reads/writes manageable."),
1451 imgid, states);
1452 }
1453
1454 // Reload metadata, drop the stale mipmap and refresh the lighttable thumbnail.
1455 // refresh_filmstrip = FALSE: in darkroom the filmstrip is best-effort, spawning another export
1456 // thread would slow down the current one and we want resources allocated to the realtime main
1457 // preview, not diverted to cosmetic GUI refreshes.
1459}
1460
1461
1462// helper used to synch a single history item with db
1463int dt_dev_write_history_item(const int32_t imgid, dt_dev_history_item_t *h, int32_t num)
1464{
1465 if(IS_NULL_PTR(h)) return 1;
1466
1467 dt_print(DT_DEBUG_HISTORY, "[dt_dev_write_history_item] writing history for module %s (%s) (enabled %i) at pipe position %i for image %i\n",
1468 h->op_name, h->multi_name, h->enabled, h->iop_order, imgid);
1469
1470 const char *operation = h->module ? h->module->op : h->op_name;
1471 const int params_size = h->module ? h->module->params_size : h->params_size;
1472 const int module_version = h->module ? h->module->version() : h->module_version;
1473 const int blendop_params_size
1474 = h->blend_params ? (h->blendop_params_size > 0 ? h->blendop_params_size : (int)sizeof(dt_develop_blend_params_t)) : 0;
1475 const int blendop_version
1477
1478 dt_history_db_write_history_item(imgid, num, operation, h->params, params_size, module_version, h->enabled != 0,
1479 h->blend_params, blendop_params_size, blendop_version, h->multi_priority, h->multi_name);
1480
1481 // write masks (if any)
1482 if(h->forms)
1483 dt_print(DT_DEBUG_HISTORY, "[dt_dev_write_history_item] drawn mask found for module %s (%s) for image %i\n", h->op_name, h->multi_name, imgid);
1484
1485 for(GList *forms = g_list_first(h->forms); forms; forms = g_list_next(forms))
1486 {
1487 dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
1488 if (form)
1489 dt_masks_write_masks_history_item(imgid, num, form);
1490 }
1491
1492 return 0;
1493}
1494
1496{
1497 // No-op: SQL statement caching/cleanup for history lives in common/history.c (dt_history_cleanup()).
1498}
1499
1500
1501
1502void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid)
1503{
1504 dt_image_t *cache_img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
1505 if(IS_NULL_PTR(cache_img)) return;
1506
1507 dt_print(DT_DEBUG_HISTORY, "[dt_dev_write_history_ext] writing history for image %i...\n", imgid);
1508
1510
1511 _cleanup_history(imgid);
1512
1513 // write history entries
1514 int i = 0;
1515 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
1516 {
1517 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1518 dt_dev_write_history_item(imgid, hist, i);
1519 i++;
1520 }
1521
1523
1524 // write the current iop-order-list for this image
1526
1527 cache_img->history_hash = dt_dev_get_history_hash(dev);
1528
1530}
1531
1532// Schedule history write as a background job to avoid blocking the GUI.
1533// If scheduling fails, fall back to synchronous write to preserve behaviour.
1535{
1537 if(IS_NULL_PTR(d)) return 1;
1538 dt_pthread_rwlock_rdlock(&d->history_mutex);
1539 const double _write_start = dt_get_wtime();
1540 dt_dev_write_history_ext(d, d->image_storage.id);
1541 const double _write_ms = (dt_get_wtime() - _write_start) * 1000.0;
1542 if(_write_ms > 1.0)
1544 "[_dt_dev_write_history_job_run] tid %lu full history+masks rewrite for image %i took %.2f ms\n",
1545 (unsigned long)pthread_self(), d->image_storage.id, _write_ms);
1546 // Clear before unlocking: dt_dev_add_history_item_real() always ends with a
1547 // dt_dev_write_history() call after it releases its own write lock, and that call
1548 // must never see history_write_pending still set from a write whose read already
1549 // finished -- otherwise the commit it just made would silently never get queued.
1550 dt_atomic_set_int(&d->history_write_pending, 0);
1551 dt_pthread_rwlock_unlock(&d->history_mutex);
1552 dt_dev_history_notify_change(d, d->image_storage.id);
1553 return 0;
1554}
1555
1556// Write TO XMP, so from the dev perspective, it's a read
1557void dt_dev_write_history(dt_develop_t *dev, gboolean async)
1558{
1559 if(!async)
1560 {
1565 return;
1566 }
1567
1568 // Coalesce: a write already queued or running for this dev will read dev->history/
1569 // dev->forms live when it (finally) runs, so it necessarily picks up whatever is
1570 // committed by the time this call happens. Queuing another one would just repeat the
1571 // exact same full history+masks_history rewrite for no additional freshness.
1572 if(dt_atomic_exch_int(&dev->history_write_pending, 1) != 0) return;
1573
1575 dev->image_storage.id);
1576 dt_control_job_set_params(job, dev, NULL);
1577
1579 {
1580 // scheduling failed: dispose job, clear the flag we just set, and run synchronously
1584 }
1585}
1586
1594static gboolean _dev_auto_apply_presets(dt_develop_t *dev, int32_t imgid)
1595{
1596 dt_image_t *image = &dev->image_storage;
1597 const gboolean has_matrix = dt_image_is_matrix_correction_supported(image);
1598 const char *workflow_preset = has_matrix ? _("scene-referred default") : "\t\n";
1599
1600 int iformat = 0;
1601 if(dt_image_needs_rawprepare(image))
1602 iformat |= FOR_RAW;
1603 else
1604 iformat |= FOR_LDR;
1605
1606 if(dt_image_is_hdr(image))
1607 iformat |= FOR_HDR;
1608
1609 int excluded = 0;
1610 if(dt_image_monochrome_flags(image))
1611 excluded |= FOR_NOT_MONO;
1612 else
1613 excluded |= FOR_NOT_COLOR;
1614
1615 int legacy_params = 0;
1616 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = TRUE };
1617 dt_history_db_foreach_auto_preset_row(imgid, image, workflow_preset, iformat, excluded, _dev_history_db_row_cb, &ctx);
1618
1619 // now we want to auto-apply the iop-order list if one corresponds and none are
1620 // still applied. Note that we can already have an iop-order list set when
1621 // copying an history or applying a style to a not yet developed image.
1622
1623 if(!dt_ioppr_has_iop_order_list(imgid))
1624 {
1625 void *params = NULL;
1626 int32_t params_len = 0;
1627 if(dt_history_db_get_autoapply_ioporder_params(imgid, image, iformat, excluded, &params, &params_len))
1628 {
1629 GList *iop_list = dt_ioppr_deserialize_iop_order_list(params, params_len);
1630 dt_ioppr_write_iop_order_list(iop_list, imgid);
1631 g_list_free_full(iop_list, dt_free_gpointer);
1632 iop_list = NULL;
1634 dt_free(params);
1635 }
1636 else
1637 {
1638 // No auto-applied order exists for this image. Persist the built-in order
1639 // matching the input class; the non-RAW order is named ANSEL_JPG but also
1640 // covers rendered formats such as TIFF/PNG/HDR.
1641 const dt_iop_order_t default_order = dt_image_needs_rawprepare(image)
1644 GList *iop_list = dt_ioppr_get_iop_order_list_version(default_order);
1645 dt_ioppr_write_iop_order_list(iop_list, imgid);
1646 g_list_free_full(iop_list, dt_free_gpointer);
1647 iop_list = NULL;
1649 }
1650 }
1651
1652 // Notify our private image copy that auto-presets got applied
1654
1655 return TRUE;
1656}
1657
1668static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited)
1669{
1670 // Module already in history: don't prepend extra entries.
1671 //
1672 // Two independent sources can already cover this module and both must be checked:
1673 // - dev->history in memory: dt_dev_replace_history_on_image() (image duplication, history
1674 // "replace" paste) loads dev->history from a *source* image, then points
1675 // dev->image_storage at a freshly created *destination* image whose DB history is still
1676 // empty (nothing written yet). The DB check alone always reports "missing" there, so every
1677 // default_enabled module (temperature, colorin, colorout, demosaic...) got a fresh
1678 // default-params entry silently appended after the correctly copied one, clobbering the
1679 // user's settings in the copy -- duplicating an image did not reproduce its development
1680 // identically.
1681 // - the DB row for dev->image_storage.id: dt_dev_read_history_ext() (normal darkroom/export
1682 // history load) calls this function on a freshly emptied dev->history, *before* the real
1683 // history rows are read from DB into memory. The in-memory check alone would always report
1684 // "missing" there too, causing every already-edited image to get spurious duplicate default
1685 // entries prepended ahead of its real history on every single load.
1688 return;
1689
1690 // Module has no user params: no history: don't prepend either
1691 if((module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1692 && (module->default_enabled || (module->force_enable && module->force_enable(module, FALSE))))
1693 {
1694 module->enabled = TRUE;
1695 return;
1696 }
1697
1698 dt_image_t *image = &dev->image_storage;
1699
1700 // Prior to Darktable 3.0, modules enabled by default which still had
1701 // default params (no user change) were not inserted into history/DB.
1702 // We need to insert them here with default params.
1703 // But defaults have changed since then for some modules, so we need to ensure
1704 // we insert them with OLD defaults.
1705 if(module->default_enabled || (module->force_enable && module->force_enable(module, FALSE)))
1706 {
1707 module->enabled = TRUE;
1708 const gboolean has_matrix = dt_image_is_matrix_correction_supported(image);
1709 const gboolean is_raw = dt_image_is_raw(image);
1710
1711 if(!strcmp(module->op, "temperature")
1712 && (image->change_timestamp == -1) // change_timestamp is not defined for old pics
1713 && is_raw && is_inited && has_matrix)
1714 {
1715 dt_print(DT_DEBUG_HISTORY, "[history] Image history seems older than Darktable 3.0, we will insert white balance.\n");
1716
1717 // Temp revert to legacy defaults
1718 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "legacy");
1719 dt_iop_reload_defaults(module);
1720
1721 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1722
1723 // Go back to current defaults
1724 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1725 dt_iop_reload_defaults(module);
1726 }
1727 else
1728 {
1729 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1730 }
1731 }
1732 else if(module->workflow_enabled && !is_inited)
1733 {
1734 module->enabled = TRUE;
1735 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1736 }
1737
1738 if(module->enabled)
1739 dt_print(DT_DEBUG_HISTORY, "[history] %s was inserted into history by default (enabled %i)\n", module->op, module->enabled);
1740}
1741
1742// Returns TRUE if this is a freshly-inited history on which we just applied auto presets and defaults,
1743// FALSE if we had an earlier history
1744gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets)
1745{
1746 const gboolean is_inited = (dev->image_storage.flags & DT_IMAGE_AUTO_PRESETS_APPLIED);
1747
1748 // Make sure this is set
1749 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1750
1751 // make sure all modules default params are loaded to init history
1752 for(GList *iop = g_list_first(dev->iop); iop; iop = g_list_next(iop))
1753 {
1754 dt_iop_module_t *module = (dt_iop_module_t *)(iop->data);
1755 dt_iop_reload_defaults(module);
1756 _insert_default_modules(dev, module, is_inited);
1757 }
1758
1759 // On virgin history image, apply auto stuff (ours and user's)
1760 if(apply_auto_presets && !is_inited) _dev_auto_apply_presets(dev, imgid);
1761 dt_print(DT_DEBUG_HISTORY, "[history] temporary history initialised with default params and presets\n");
1762
1763 return !is_inited;
1764}
1765
1766int dt_dev_replace_history_on_image(dt_develop_t *dev_src, const int32_t dest_imgid,
1767 const gboolean reload_defaults, const char *msg)
1768{
1769 if(dest_imgid <= 0) return 1;
1770
1771 dt_dev_ensure_image_storage(dev_src, dest_imgid);
1772
1773 if(reload_defaults)
1774 {
1775 dt_dev_init_default_history(dev_src, dest_imgid, FALSE);
1776 dt_ioppr_resync_pipeline(dev_src, dest_imgid, msg, FALSE);
1777 }
1778
1780 dt_dev_write_history(dev_src, FALSE);
1781
1782 return 0;
1783}
1784
1785// populate hist->module
1795{
1796 dt_iop_module_t *match = NULL;
1797
1798 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1799 {
1800 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1801 if(!strcmp(module->op, hist->op_name))
1802 {
1803 if(module->multi_priority == hist->multi_priority)
1804 {
1805 // Found exact match at required priority: we are done
1806 hist->module = module;
1807 break;
1808 }
1809 else if(hist->multi_priority > 0)
1810 {
1811 // Found the right kind of module but the wrong instance.
1812 // Current history entry is targeting an instance that may exist later in the pipe, so keep looping/looking.
1813 match = module;
1814 }
1815 }
1816 }
1817
1818 if(!hist->module && match)
1819 {
1820 // We found a module having the required name but not the required instance number:
1821 // add a new instance of this module by using its ->so property
1822 dt_iop_module_t *new_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1823 if(!dt_iop_load_module(new_module, match->so, dev))
1824 {
1825 dev->iop = g_list_append(dev->iop, new_module);
1826 // Just init, it will get rewritten later by resync IOP order methods:
1827 new_module->instance = match->instance;
1828 hist->module = new_module;
1829 }
1830 }
1831 // else we found an already-existing instance and it's in hist->module already
1832
1833 if(hist->module) hist->module->enabled = (hist->enabled != 0);
1834}
1835
1836
1848static void _sync_blendop_params(dt_dev_history_item_t *hist, const void *blendop_params, const int bl_length,
1849 const int blendop_version, int *legacy_params)
1850{
1851 const gboolean is_valid_blendop_version = (blendop_version == dt_develop_blend_version());
1852 const gboolean is_valid_blendop_size = (bl_length == sizeof(dt_develop_blend_params_t));
1853
1854 hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
1857
1858 if(!IS_NULL_PTR(blendop_params) && is_valid_blendop_version && is_valid_blendop_size)
1859 {
1860 memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
1861 }
1862 else if(blendop_params
1863 && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params,
1864 dt_develop_blend_version(), bl_length)
1865 == 0)
1866 {
1868 }
1869 else
1870 {
1871 memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
1872 }
1873}
1874
1888static int _sync_params(dt_dev_history_item_t *hist, const void *module_params, const int param_length,
1889 const int modversion, int *legacy_params, const char *preset_name)
1890{
1891 hist->params_size = hist->module->params_size;
1892 hist->module_version = hist->module->version();
1893 const gboolean is_valid_module_version = (modversion == hist->module->version());
1894 const gboolean is_valid_params_size = (param_length == hist->module->params_size);
1895
1896 hist->params = malloc(hist->module->params_size);
1897 if(is_valid_module_version && is_valid_params_size)
1898 {
1899 memcpy(hist->params, module_params, hist->module->params_size);
1900 }
1901 else
1902 {
1903 if(!hist->module->legacy_params
1904 || hist->module->legacy_params(hist->module, module_params, labs(modversion),
1905 hist->params, labs(hist->module->version())))
1906 {
1907 gchar *preset = (preset_name) ? g_strdup_printf(_("from preset %s"), preset_name)
1908 : g_strdup("");
1909
1910 fprintf(stderr, "[dev_read_history] module `%s' %s version mismatch: history is %d, dt %d.\n", hist->module->op,
1911 preset, modversion, hist->module->version());
1912
1913 dt_control_log(_("module `%s' %s version mismatch: %d != %d"), hist->module->op,
1914 preset, hist->module->version(), modversion);
1915
1916 dt_free(preset);
1917 return 1;
1918 }
1919 else
1920 {
1921 // NOTE: spots version was bumped from 1 to 2 in 2013.
1922 // This handles edits made prior to Darktable 1.4.
1923 // Then spots was deprecated in 2021 in favour of retouch.
1924 // How many edits out there still need the legacy conversion in 2025 ?
1925 if(!strcmp(hist->module->op, "spots") && modversion == 1)
1926 {
1927 // quick and dirty hack to handle spot removal legacy_params
1928 memcpy(hist->blend_params, hist->module->blend_params, sizeof(dt_develop_blend_params_t));
1929 }
1931 }
1932
1933 /*
1934 * Fix for flip iop: previously it was not always needed, but it might be
1935 * in history stack as "orientation (off)", but now we always want it
1936 * by default, so if it is disabled, enable it, and replace params with
1937 * default_params. if user want to, he can disable it.
1938 * NOTE: Flip version was bumped from 1 to 2 in 2014.
1939 * This handles edits made prior to Darktable 1.6.
1940 * How many edits out there still need the legacy conversion in 2025 ?
1941 */
1942 if(!strcmp(hist->module->op, "flip") && hist->enabled == 0 && labs(modversion) == 1)
1943 {
1944 memcpy(hist->params, hist->module->default_params, hist->module->params_size);
1945 hist->enabled = TRUE;
1946 }
1947 }
1948
1949 return 0;
1950}
1951
1952// WARNING: this does not set hist->forms
1976static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
1977 const int modversion, const char *operation, const void *module_params,
1978 const int param_length, const gboolean enabled, const void *blendop_params,
1979 const int bl_length, const int blendop_version, const int multi_priority,
1980 const char *multi_name, const char *preset_name, int *legacy_params,
1981 const gboolean presets)
1982{
1983 // Sanity checks
1984 const gboolean is_valid_id = (id == imgid);
1985 const gboolean has_operation = (!IS_NULL_PTR(operation));
1986
1987 if(!(has_operation && is_valid_id))
1988 {
1989 fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n",
1990 dev->image_storage.filename);
1991 return;
1992 }
1993
1999 if(!dt_iop_get_module_from_list(dev->iop, operation)) return;
2000
2001 int iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, operation, multi_priority);
2002
2003 // Init a bare minimal history entry
2005 hist->module = NULL;
2006 hist->num = num;
2007 hist->iop_order = iop_order;
2008 hist->multi_priority = multi_priority;
2009 hist->enabled = (enabled != 0); // cast to gboolean
2010 g_strlcpy(hist->op_name, operation, sizeof(hist->op_name));
2011 g_strlcpy(hist->multi_name, multi_name ? multi_name : "", sizeof(hist->multi_name));
2012
2013 // Find a .so file that matches our history entry, aka a module to run the params stored in DB
2014 _find_so_for_history_entry(dev, hist);
2015
2016 if(!hist->module)
2017 {
2018 // Keep the serialized history entry even though no live module can consume it.
2019 hist->params_size = MAX(param_length, 0);
2020 hist->module_version = modversion;
2021 hist->blendop_params_size = MAX(bl_length, 0);
2022 hist->blendop_version = blendop_version;
2023 if(!IS_NULL_PTR(module_params) && param_length > 0)
2024 {
2025 hist->params = malloc(param_length);
2026 memcpy(hist->params, module_params, param_length);
2027 }
2028 if(!IS_NULL_PTR(blendop_params) && bl_length > 0)
2029 {
2030 hist->blend_params = malloc(bl_length);
2031 memcpy(hist->blend_params, blendop_params, bl_length);
2032 }
2033
2034 fprintf(
2035 stderr,
2036 "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n",
2037 operation, dev->image_storage.filename);
2038 dev->history = g_list_append(dev->history, hist);
2039 return;
2040 }
2041
2042 // Update IOP order stuff, that applies to all modules regardless of their internals
2043 // Needed now to de-entangle multi-instances
2044 hist->module->iop_order = hist->iop_order;
2045 dt_iop_update_multi_priority(hist->module, hist->multi_priority);
2046
2047 // When the stored module_order uses a non-CUSTOM built-in (one entry per module),
2048 // extra instances (multi_priority > 0) are absent from iop_order_list and get
2049 // iop_order = INT_MAX above. Insert a placeholder so the module gets a valid
2050 // sequential position instead of drifting through the pipeline at INT_MAX.
2051 if(iop_order == INT_MAX && hist->multi_priority > 0)
2052 {
2053 dt_iop_order_entry_t *new_entry = (dt_iop_order_entry_t *)calloc(1, sizeof(dt_iop_order_entry_t));
2054 g_strlcpy(new_entry->operation, operation, sizeof(new_entry->operation));
2055 g_strlcpy(new_entry->name, hist->multi_name, sizeof(new_entry->name));
2056 new_entry->instance = hist->multi_priority;
2057 new_entry->o.iop_order = 0;
2058
2059 // Insert after the last existing entry for this operation so extra instances
2060 // sit after the base, matching the order from dt_dev_module_duplicate.
2061 GList *place = NULL;
2062 for(GList *l = dev->iop_order_list; l; l = g_list_next(l))
2063 {
2064 const dt_iop_order_entry_t *e = (dt_iop_order_entry_t *)l->data;
2065 if(!strcmp(e->operation, operation)) place = l;
2066 }
2067 dev->iop_order_list = g_list_insert_before(dev->iop_order_list,
2068 place ? g_list_next(place) : NULL, new_entry);
2069
2070 iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, operation, hist->multi_priority);
2071 hist->iop_order = iop_order;
2072 hist->module->iop_order = iop_order;
2073 }
2074
2075 // module has no user params and won't bother us in GUI - exit early, we are done
2076 if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
2077 {
2078 // Since it's the last we hear from this module as far as history is concerned,
2079 // compute its hash here.
2080 dt_iop_compute_module_hash(hist->module, NULL);
2081
2082 // Done. We don't add to history
2083 dt_free(hist);
2084 return;
2085 }
2086
2087 // Copy module params if valid version, else try to convert legacy params
2088 if(_sync_params(hist, module_params, param_length, modversion, legacy_params, preset_name))
2089 {
2090 dt_free(hist);
2091 return;
2092 }
2093
2094 // So far, on error we haven't allocated any buffer, so we just freed the hist structure
2095
2096 // Last chance & desperate attempt at enabling/disabling critical modules
2097 // when history is garbled - This might prevent segfaults on invalid data
2098 if(hist->module->force_enable)
2099 hist->enabled = (hist->module->force_enable(hist->module, hist->enabled) != 0);
2100
2101 // make sure that always-on modules are always on. duh.
2102 if(hist->module->default_enabled == TRUE && hist->module->hide_enable_button == TRUE)
2103 hist->enabled = TRUE;
2104
2105 // Copy blending params if valid, else try to convert legacy params
2106 _sync_blendop_params(hist, blendop_params, bl_length, blendop_version, legacy_params);
2107
2108 if(presets && !IS_NULL_PTR(hist->blend_params) && !IS_NULL_PTR(hist->module)
2109 && !IS_NULL_PTR(hist->module->default_blendop_params))
2110 {
2111 dt_develop_blend_params_t preset_blend = *hist->blend_params;
2112 dt_develop_blend_params_t default_blend = *hist->module->default_blendop_params;
2113 preset_blend.mask_mode &= ~DEVELOP_MASK_ENABLED;
2114 default_blend.mask_mode &= ~DEVELOP_MASK_ENABLED;
2115 if(memcmp(&preset_blend, &default_blend, sizeof(dt_develop_blend_params_t)) == 0)
2116 hist->blend_params->mask_mode &= ~DEVELOP_MASK_ENABLED;
2117 }
2118
2119 dev->history = g_list_append(dev->history, hist);
2120
2121 dt_print(DT_DEBUG_HISTORY, "[history entry] read %s at pipe position %i (enabled %i) from %s %s\n", hist->op_name,
2122 hist->iop_order, hist->enabled, (presets) ? "preset" : "database", (presets) ? preset_name : "");
2123}
2124
2125
2126gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid)
2127{
2128 if(imgid == UNKNOWN_IMAGE) return FALSE;
2129
2130 // This should be inited already when creating a pipeline or entering darkroom
2131 // but some pathes don't handle it, so make sure we have modules loaded.
2132 if(IS_NULL_PTR(dev->iop))
2133 dev->iop = dt_dev_load_modules(dev);
2134
2135 // Ensure raw metadata (WB coeffs, matrices, etc.) is available for modules that
2136 // query it while (re)loading defaults (e.g. temperature/colorin).
2137 // This is redundant with `_dt_dev_load_raw()` called from `dt_dev_load_image()`,
2138 // but some call sites reload history without guaranteeing a prior FULL open.
2139 if(dt_dev_ensure_image_storage(dev, imgid))
2140 return FALSE;
2141
2142 // Start fresh
2144 int legacy_params = 0;
2146
2147 // Find the new history end from DB now, if defined.
2148 // Note: dt_dev_set_history_end_ext sanitizes the value with the actual history size.
2149 // It needs to run after dev->history is fully populated.
2150 int32_t history_end = dt_history_get_end(imgid);
2151
2152 // Find out if we already have an history, and how many items
2153 const int32_t db_items = dt_history_db_get_next_history_num(imgid);
2154
2155 // Load all the modules that may be required by the image format,
2156 // plus auto-presets if it's a new edit
2157 gboolean first_run = dt_dev_init_default_history(dev, imgid, (db_items == 0));
2158
2159 // Protect history DB reads with a cache read lock.
2160 // Release it before applying history to modules to avoid deadlocks.
2161 dt_image_t *read_lock_img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
2162 if(IS_NULL_PTR(read_lock_img)) return FALSE;
2163
2164 // Load DB history into dev->history
2165 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = FALSE };
2167 const int32_t history_length = g_list_length(dev->history);
2168
2169 if(history_length > db_items)
2170 {
2171 // We have now more history entries than when reading DB,
2172 // meaning the sanitization step added required default modules.
2173 // We need to shift history end by the same amount to still point to the same entry.
2174 // This is valid whether history_end was 0 or not
2175 history_end += history_length - db_items;
2176 }
2177
2178 // Set a provisional history_end so mask history can resolve current forms.
2179 // We will recompute hashes once all history items are committed below.
2180 dt_dev_set_history_end_ext(dev, history_end);
2181
2182 // Sanitize and flatten module order
2183 dt_ioppr_resync_pipeline(dev, imgid, "dt_dev_read_history_no_image end", FALSE);
2184
2185 // After resync, iop_order_list was renumbered with correct sequential values.
2186 // Fix hist->iop_order for entries recovered from a missing order entry: they
2187 // got a placeholder value of 0 above; sync them from the now-correct module order.
2188 for(GList *h = dev->history; h; h = g_list_next(h))
2189 {
2191 if(hist->module && hist->iop_order < 1 && hist->module->iop_order > 0)
2192 hist->iop_order = hist->module->iop_order;
2193 }
2194
2195 // Update "masks history"
2196 // This design is stupid because `dt_dev_history_item_t *hist->forms` is not read
2197 // from the DB history items (`main.history`), but from the `main.masks_history`,
2198 // and later on, `dev->forms` is restored at the `history_end` index from dumping
2199 // the content of the `dt_dev_history_item_t *hist->forms` snapshot.
2200 // This only means that `hist->forms` doesn't belong to the `dt_dev_history_item_t *`
2201 // but should live in its own branch. See `dt_dev_pop_history_items_ext()`
2202 dt_masks_read_masks_history(dev, imgid);
2203
2205 read_lock_img = NULL;
2206
2207 // Now we have fully-populated history items:
2208 // Commit params to modules and publish the masks on the raster stack for other modules to find
2209 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
2210 {
2211 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
2212 if(!hist)
2213 {
2214 fprintf(stderr, "[dt_dev_read_history_ext] we have no history item. This is not normal.\n");
2215 continue;
2216 }
2217 else if(!hist->module)
2218 {
2219 dt_print(DT_DEBUG_HISTORY, "[history] keeping archival history item %s (%s) without live module binding\n",
2220 hist->op_name, hist->multi_name);
2221 continue;
2222 }
2223
2224 dt_iop_module_t *module = hist->module;
2225 _history_to_module(hist, module);
2226 hist->hash = hist->module->hash;
2227
2228 // Register the freshly-read history item now (its hash is final), so the
2229 // node<->history resynchronization can resolve `params` to a real entry.
2231 dt_supervisor_history(DT_SV_CREATE, hist->hash, module->op, module->multi_priority,
2232 module->multi_name, module->iop_order, g_list_index(dev->history, hist),
2233 imgid, hist->enabled, module, hist->params, hist->blend_params, hist->forms);
2234
2235 dt_print(DT_DEBUG_HISTORY, "[history] successfully loaded module %s history (enabled: %i)\n", hist->module->op, hist->enabled);
2236 }
2237
2240 // The call above seeds dev->forms_hash from a freshly-zeroed dev, which always compares
2241 // as "changed" the first time -- that's establishing the baseline against what was just
2242 // loaded from disk, not a pending edit. Clear it so the first mask interaction (even a
2243 // no-op selection click) doesn't trigger a spurious history commit + full DB rewrite.
2244 dev->forms_changed = FALSE;
2245
2246 dt_dev_set_history_end_ext(dev, history_end);
2247 // Note: dt_dev_set_history_end already updates dev->history_hash
2248
2249 dt_print(DT_DEBUG_HISTORY, "[history] dt_dev_read_history_ext completed\n");
2250 return first_run;
2251}
2252
2253
2255{
2256 for(; list; list = g_list_next(list))
2257 {
2258 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)list->data;
2259 if (hitem->module == module)
2260 {
2261 hitem->module = NULL;
2262 }
2263 }
2264}
2265
2270
2278{
2279 return (module->flags() & IOP_FLAGS_NO_HISTORY_STACK);
2280}
2281
2289{
2290 return module->default_enabled || (module->force_enable && module->force_enable(module, module->enabled));
2291}
2292
2300{
2301 return module->has_defaults ? module->has_defaults(module) : TRUE;
2302}
2303
2311{
2312 if(!module->blend_params || !module->default_blendop_params) return TRUE;
2313
2314 return memcmp(module->blend_params, module->default_blendop_params,
2315 sizeof(dt_develop_blend_params_t)) == 0;
2316}
2317
2328
2329typedef gboolean (*dt_iop_module_filter_t)(dt_iop_module_t *module);
2330
2338{
2339 for(GList *item = g_list_first(dev->iop); item; item = g_list_next(item))
2340 {
2341 dt_iop_module_t *module = (dt_iop_module_t *)(item->data);
2342 if(!_module_leaves_no_history(module) && filter(module))
2343 dt_dev_add_history_item_ext(dev, module, FALSE, TRUE);
2344 }
2345}
2346
2351{
2352 return module->enabled && _module_is_default_or_forced_enabled(module);
2353}
2354
2359{
2360 return module->enabled && !_module_is_default_or_forced_enabled(module)
2361 && _module_params_are_default(module);
2362}
2363
2368{
2369 return module->enabled && !_module_is_default_or_forced_enabled(module)
2370 && !_module_params_are_default(module);
2371}
2372
2377{
2378 return !module->enabled
2379 && (module->default_enabled || module->workflow_enabled || _module_has_nondefault_internal_params(module));
2380}
2381
2390static void _dt_dev_history_compress_internal(dt_develop_t *dev, const gboolean write_history)
2391{
2392 // Rebuild the history list under lock, but run expensive cross-subsystem
2393 // operations (history->modules sync, optional DB write) after releasing it.
2394 // This keeps history_mutex hold time short and avoids lock-order contention
2395 // with GUI/pipeline users touching masks/pixelpipe.
2397
2398 // Cleanup old history
2400
2401 // Rebuild an history from current pipeline.
2402 // First: modules enabled by default or forced enabled for technical reasons
2404
2405 // Second: modules enabled by user
2406 // 2.1 : start with modules that still have default params,
2408
2409 // 2.2 : then modules that are set to non-default
2411
2412 // Third: disabled modules that have history because they were enabled by default or
2413 // because their internal params (including blendops) differ from defaults. Maybe users
2414 // want to re-enable them later, or it's modules enabled by default that were manually disabled.
2415 // Put them the end of the history, so user can truncate it after the last enabled item
2416 // to get rid of disabled history if needed.
2418
2419 dt_dev_set_history_end_ext(dev, g_list_length(dev->history));
2421
2428 if(write_history)
2429 {
2433 }
2434}
2435
2436void dt_dev_history_compress_ext(dt_develop_t *dev, gboolean write_history)
2437{
2438 _dt_dev_history_compress_internal(dev, write_history);
2439}
2440
2445
2446void dt_dev_history_truncate(dt_develop_t *dev, const int32_t imgid)
2447{
2449
2450 // Remove tail entries (num >= end).
2451 // history_end is a cursor expressed in "number of applied items" terms:
2452 // - keep items [0..history_end-1]
2453 // - remove items [history_end..]
2454 GList *link = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev));
2455 while(link)
2456 {
2457 GList *next = g_list_next(link);
2458 const dt_dev_history_item_t *h = (const dt_dev_history_item_t *)link->data;
2459 if(dt_supervisor_active() && h && h->module)
2460 dt_supervisor_history(DT_SV_DELETE, h->hash, h->module->op, h->module->multi_priority,
2461 h->module->multi_name, h->module->iop_order, h->num,
2462 dev->image_storage.id, h->enabled, h->module, h->params, h->blend_params, h->forms);
2463 dt_dev_free_history_item(link->data);
2464 dev->history = g_list_delete_link(dev->history, link);
2465 link = next;
2466 }
2467
2469
2470 // Re-apply history and resync iop order from the truncated stack.
2478 dt_dev_write_history_ext(dev, imgid);
2480}
2481
2483{
2484 if(dt_dev_get_history_end_ext(dev) == g_list_length(dev->history))
2486 else
2488}
2489
2490
2499static int _check_deleted_instances(dt_develop_t *dev, GList **_iop_list, GList *history_list)
2500{
2501 GList *iop_list = *_iop_list;
2502 int deleted_module_found = 0;
2503
2504 // we will check on dev->iop if there's a module that is not in history
2505 GList *modules = iop_list;
2506 while(modules)
2507 {
2508 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2509 if(IS_NULL_PTR(mod)) continue;
2510
2511 int delete_module = 0;
2512
2513 // base modules are a special case
2514 // most base modules won't be in history and must not be deleted
2515 // but the user may have deleted a base instance of a multi-instance module
2516 // and then undo and redo, so we will end up with two entries in dev->iop
2517 // with multi_priority == 0, this can't happen and the extra one must be deleted
2518 // dev->iop is sorted by (priority, multi_priority DESC), so if the next one is
2519 // a base instance too, one must be deleted
2520 if(mod->multi_priority == 0)
2521 {
2522 GList *modules_next = g_list_next(modules);
2523 if(modules_next)
2524 {
2525 dt_iop_module_t *mod_next = (dt_iop_module_t *)modules_next->data;
2526 if(strcmp(mod_next->op, mod->op) == 0 && mod_next->multi_priority == 0)
2527 {
2528 // is the same one, check which one must be deleted
2529 const int mod_in_history = (dt_dev_history_get_first_item_by_module(history_list, mod) != NULL);
2530 const int mod_next_in_history = (dt_dev_history_get_first_item_by_module(history_list, mod_next) != NULL);
2531
2532 // current is in history and next is not, delete next
2533 if(mod_in_history && !mod_next_in_history)
2534 {
2535 mod = mod_next;
2536 modules = modules_next;
2537 delete_module = 1;
2538 }
2539 // current is not in history and next is, delete current
2540 else if(!mod_in_history && mod_next_in_history)
2541 {
2542 delete_module = 1;
2543 }
2544 else
2545 {
2546 if(mod_in_history && mod_next_in_history)
2547 fprintf(
2548 stderr,
2549 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) both in history\n",
2550 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2551 mod_next->multi_priority);
2552 else
2553 fprintf(
2554 stderr,
2555 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) none in history\n",
2556 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2557 mod_next->multi_priority);
2558 }
2559 }
2560 }
2561 }
2562 // this is a regular multi-instance and must be in history
2563 else
2564 {
2565 delete_module = (dt_dev_history_get_first_item_by_module(history_list, mod) == NULL);
2566 }
2567
2568 // if module is not in history we delete it
2569 if(delete_module && mod)
2570 {
2571 deleted_module_found = 1;
2572
2574
2576
2577 // we remove the plugin effectively
2578 if(!dt_iop_is_hidden(mod))
2579 {
2580 // we just hide the module to avoid lots of gtk critical warnings
2581 gtk_widget_hide(mod->expander);
2582
2583 // this is copied from dt_iop_gui_delete_callback(), not sure why the above sentence...
2585 gtk_widget_destroy(mod->widget);
2586 }
2587
2588 iop_list = g_list_delete_link(iop_list, modules);
2589
2590 // remove the module reference from all snapshots
2592
2593 // don't delete the module, a pipe may still need it
2594 dev->alliop = g_list_append(dev->alliop, mod);
2595
2597
2598 // and reset the list
2599 modules = iop_list;
2600 continue;
2601 }
2602
2603 modules = g_list_next(modules);
2604 }
2605 if(deleted_module_found) iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2606
2607 *_iop_list = iop_list;
2608
2609 return deleted_module_found;
2610}
2611
2618static int _rebuild_multi_priority(GList *history_list)
2619{
2620 int changed = 0;
2621 for(const GList *history = history_list; history; history = g_list_next(history))
2622 {
2623 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)history->data;
2624
2625 // if multi_priority is different in history and dev->iop
2626 // we keep the history version
2627 if(hitem->module && hitem->module->multi_priority != hitem->multi_priority)
2628 {
2629 dt_iop_update_multi_priority(hitem->module, hitem->multi_priority);
2630 changed = 1;
2631 }
2632 }
2633 return changed;
2634}
2635
2643static void _reset_module_instance(GList *hist, dt_iop_module_t *module, int multi_priority)
2644{
2645 for(; hist; hist = g_list_next(hist))
2646 {
2647 dt_dev_history_item_t *hit = (dt_dev_history_item_t *)hist->data;
2648
2649 if(IS_NULL_PTR(hit->module) && strcmp(hit->op_name, module->op) == 0 && hit->multi_priority == multi_priority)
2650 {
2651 hit->module = module;
2652 }
2653 }
2654}
2655
2663static void _undo_items_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data)
2664{
2665 struct _cb_data *udata = (struct _cb_data *)user_data;
2666 dt_undo_history_t *hdata = (dt_undo_history_t *)data;
2667 _reset_module_instance(hdata->after_snapshot, udata->module, udata->multi_priority);
2668}
2669
2680static int _create_deleted_modules(GList **_iop_list, GList *history_list)
2681{
2682 GList *iop_list = *_iop_list;
2683 int changed = 0;
2684 gboolean done = FALSE;
2685
2686 GList *l = history_list;
2687 while(l)
2688 {
2689 GList *next = g_list_next(l);
2690 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)l->data;
2691
2692 // this fixes the duplicate module when undo: hitem->multi_priority = 0;
2693 if(IS_NULL_PTR(hitem->module))
2694 {
2695 changed = 1;
2696
2697 const dt_iop_module_t *base_module = dt_iop_get_module_from_list(iop_list, hitem->op_name);
2698 if(IS_NULL_PTR(base_module))
2699 {
2700 fprintf(stderr, "[_create_deleted_modules] can't find base module for %s\n", hitem->op_name);
2701 return changed;
2702 }
2703
2704 // from there we create a new module for this base instance. The goal is to do a very minimal setup of the
2705 // new module to be able to write the history items. From there we reload the whole history back and this
2706 // will recreate the proper module instances.
2707 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
2708 if(dt_iop_load_module(module, base_module->so, base_module->dev))
2709 {
2710 return changed;
2711 }
2712 module->instance = base_module->instance;
2713
2714 // adjust the multi_name of the new module
2715 g_strlcpy(module->multi_name, hitem->multi_name, sizeof(module->multi_name));
2717 module->iop_order = hitem->iop_order;
2718
2719 // we insert this module into dev->iop
2720 iop_list = g_list_insert_sorted(iop_list, module, dt_sort_iop_by_order);
2721
2722 // if not already done, set the module to all others same instance
2723 if(!done)
2724 {
2725 _reset_module_instance(history_list, module, hitem->multi_priority);
2726
2727 // and do that also in the undo/redo lists
2728 struct _cb_data udata = { module, hitem->multi_priority };
2730 done = TRUE;
2731 }
2732
2733 hitem->module = module;
2734 }
2735 l = next;
2736 }
2737
2738 *_iop_list = iop_list;
2739
2740 return changed;
2741}
2742
2743
2744// returns 1 if the topology of the pipe has changed, aka it needs a full rebuild
2745// 0 means only internal parameters of pipe nodes have change, so it's a mere resync
2746int dt_dev_history_refresh_nodes_ext(dt_develop_t *dev, GList **iop, GList *history)
2747{
2748 GList *iop_list = *iop;
2749
2750 // topology has changed?
2751 int pipe_remove = 0;
2752
2753 // we have to check if multi_priority has changed since history was saved
2754 // we will adjust it here
2755 if(_rebuild_multi_priority(history))
2756 {
2757 pipe_remove = 1;
2758 iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2759 }
2760
2761 // check if this undo a delete module and re-create it
2762 if(_create_deleted_modules(&iop_list, history))
2763 pipe_remove = 1;
2764
2765 // check if this is a redo of a delete module or an undo of an add module
2766 if(_check_deleted_instances(dev, &iop_list, history))
2767 pipe_remove = 1;
2768
2769 *iop = iop_list;
2770
2771 // if topology has changed, we need to reorder modules in GUI
2772 if(pipe_remove) dt_dev_signal_modules_moved(dev);
2773
2774 return pipe_remove;
2775}
2776
2777
2778/* ---------------------------------------------------------------------------------------------------
2779 * Out-of-history transient param channel (see dev_history.h).
2780 *
2781 * Thread-safety: the slot is guarded by `dev->transient_params_mutex`. Writers (GUI/worker thread)
2782 * `_set`/`_clear`; the pipeline reader (`_get`) copies out under the same lock and never dereferences
2783 * the publishing module. The published payload is a plain byte copy of the module params struct, so the
2784 * pipeline never touches GUI-owned `module->params`.
2785 * ------------------------------------------------------------------------------------------------- */
2786
2787void dt_dev_transient_params_set(dt_iop_module_t *module, const void *params, const size_t params_size,
2788 const void *blend_params, const size_t blend_size)
2789{
2790 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev) || IS_NULL_PTR(params) || params_size == 0) return;
2791 dt_develop_t *dev = module->dev;
2792
2794
2795 if(dev->transient_params.params_size != (int32_t)params_size)
2796 {
2798 dev->transient_params.params = g_malloc0(params_size);
2800 }
2802 memcpy(dev->transient_params.params, params, params_size);
2803
2804 if(!IS_NULL_PTR(blend_params) && blend_size > 0)
2805 {
2806 if(dev->transient_params.blend_size != (int32_t)blend_size)
2807 {
2809 dev->transient_params.blend_params = g_malloc0(blend_size);
2810 dev->transient_params.blend_size = (int32_t)blend_size;
2811 }
2813 memcpy(dev->transient_params.blend_params, blend_params, blend_size);
2814 }
2815 else
2816 {
2819 }
2820
2821 dev->transient_params.module = module;
2822 dev->transient_params.serial++;
2823
2825
2826 /* Intentionally does NOT trigger a pipe recompute. The caller flags the pipe the way that fits its
2827 * use case: a realtime module (drawlayer) raises DT_DEV_PIPE_TOP_CHANGED on the main pipe for a fast
2828 * focused-piece resync, while a geometry-changing edit (crop/ashift) drives a full
2829 * dt_dev_pixelpipe_resync_history_all() so every pipe (main, preview, virtual) replans ROI/formats.
2830 * Auto-triggering here too would route those edits through the partial focused-piece resync as well
2831 * and race the full one (garbled geometry on warm re-edits). */
2832}
2833
2835{
2836 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev)) return;
2837 dt_develop_t *dev = module->dev;
2838
2840 if(dev->transient_params.module == module)
2841 {
2846 dev->transient_params.module = NULL;
2847 dev->transient_params.serial++;
2848 }
2850
2851 /* As with _set, the caller is responsible for re-triggering the pipe (resync / history commit) so it
2852 * re-commits the focused module's piece from history instead of the dropped transient snapshot. */
2853}
2854
2856 void *out_params, const size_t out_params_size,
2857 void *out_blend, const size_t out_blend_size, gboolean *out_has_blend)
2858{
2859 if(!IS_NULL_PTR(out_has_blend)) *out_has_blend = FALSE;
2860 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module) || IS_NULL_PTR(out_params) || out_params_size == 0) return FALSE;
2861
2862 gboolean ok = FALSE;
2864 if(dev->transient_params.module == module && !IS_NULL_PTR(dev->transient_params.params)
2865 && dev->transient_params.params_size == (int32_t)out_params_size)
2866 {
2867 memcpy(out_params, dev->transient_params.params, out_params_size);
2868 ok = TRUE;
2869
2870 if(!IS_NULL_PTR(out_blend) && out_blend_size > 0 && !IS_NULL_PTR(dev->transient_params.blend_params)
2871 && dev->transient_params.blend_size == (int32_t)out_blend_size)
2872 {
2873 memcpy(out_blend, dev->transient_params.blend_params, out_blend_size);
2874 if(!IS_NULL_PTR(out_has_blend)) *out_has_blend = TRUE;
2875 }
2876 }
2878 return ok;
2879}
2880
2882{
2883 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module)) return FALSE;
2885 const gboolean active = (dev->transient_params.module == module);
2887 return active;
2888}
void reload_defaults(dt_iop_module_t *module)
Definition ashift.c:5648
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
int dt_atomic_sub_int(dt_atomic_int *var, int decr)
int dt_atomic_add_int(dt_atomic_int *var, int incr)
int dt_atomic_exch_int(dt_atomic_int *var, int value)
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:565
#define m
Definition basecurve.c:278
int dt_develop_blend_version(void)
Definition blend.c:1651
int dt_develop_blend_legacy_params(dt_iop_module_t *module, const void *const old_params, const int old_version, void *new_params, const int new_version, const int length)
Definition blend.c:1715
void dt_iop_gui_update_blendif(dt_iop_module_t *module)
Definition blend_gui.c:3513
const float threshold
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
gboolean dt_history_check_module_exists(int32_t imgid, const char *operation, gboolean enabled)
gboolean dt_history_set_end(const int32_t imgid, const int32_t history_end)
gboolean dt_history_db_write_history_item(const int32_t imgid, const int num, const char *operation, const void *op_params, const int op_params_size, const int module_version, const gboolean enabled, const void *blendop_params, const int blendop_params_size, const int blendop_version, const int multi_priority, const char *multi_name)
gboolean dt_history_db_get_autoapply_ioporder_params(const int32_t imgid, const dt_image_t *image, const int iformat, const int excluded, void **params, int32_t *params_len)
int32_t dt_history_db_get_next_history_num(const int32_t imgid)
void dt_history_db_foreach_auto_preset_row(const int32_t imgid, const dt_image_t *image, const char *workflow_preset, const int iformat, const int excluded, dt_history_db_row_cb cb, void *user_data)
int32_t dt_history_get_end(const int32_t imgid)
gboolean dt_history_db_delete_dev_history(const int32_t imgid)
void dt_history_db_foreach_history_row(const int32_t imgid, dt_history_db_row_cb cb, void *user_data)
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
gboolean dt_image_is_raw(const dt_image_t *img)
int dt_image_monochrome_flags(const dt_image_t *img)
gboolean dt_image_is_hdr(const dt_image_t *img)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
int type
char * name
void dt_conf_set_string(const char *name, const char *val)
void dt_toast_log(const char *msg,...)
Definition control.c:824
void dt_control_log(const char *msg,...)
Definition control.c:777
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define UNKNOWN_IMAGE
Definition darktable.h:194
@ DT_DEBUG_HISTORY
Definition darktable.h:762
@ DT_DEBUG_VERBOSE
Definition darktable.h:765
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1123
static double dt_get_wtime(void)
Definition darktable.h:976
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
void dt_dev_add_history_item_real(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean redraw)
Thread-safe wrapper around dt_dev_add_history_item_ext().
int dt_history_merge_module_into_history(dt_develop_t *dev_dest, dt_develop_t *dev_src, dt_iop_module_t *mod_src)
Merge a single module instance into a destination history.
void dt_apply_dev_history_update(dt_develop_t *dev)
Reload the current darkroom history and refresh every dependent GUI.
static gboolean _module_blend_params_are_default(dt_iop_module_t *module)
Check if blend params match defaults.
static void _history_to_module(const dt_dev_history_item_t *const hist, dt_iop_module_t *module)
Apply a history item to a module instance.
static int _rebuild_multi_priority(GList *history_list)
Resync module multi_priority values from history.
int dt_dev_write_history_item(const int32_t imgid, dt_dev_history_item_t *h, int32_t num)
void dt_dev_history_release_snapshot(GList *snapshot)
int dt_dev_next_multi_priority_for_op(dt_develop_t *dev, const char *op)
Return the next available multi_priority for an operation.
static void _dt_dev_modules_reload_defaults(dt_develop_t *dev)
Reset every module to its (already-computed) default params before history is overlaid.
gboolean dt_dev_transient_params_get(dt_develop_t *dev, const dt_iop_module_t *module, void *out_params, const size_t out_params_size, void *out_blend, const size_t out_blend_size, gboolean *out_has_blend)
static void _undo_items_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data)
Undo iterator callback to fix module pointers in snapshots.
static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
Apply an undo/redo history snapshot to a develop context.
static void _history_invalidate_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item)
Undo iterator callback to invalidate module pointers in snapshots.
static gboolean _compress_enabled_user_nondefault_params(dt_iop_module_t *module)
Filter: enabled modules with non-default params (user edits).
static void _dev_history_db_row_cb(void *user_data, const int32_t id, const int num, const int modversion, const char *operation, const void *module_params, const int param_length, const gboolean enabled, const void *blendop_params, const int bl_length, const int blendop_version, const int multi_priority, const char *multi_name, const char *preset_name)
Adapter callback for history DB rows.
static void _find_so_for_history_entry(dt_develop_t *dev, dt_dev_history_item_t *hist)
Bind a history entry to a module instance (.so) in dev->iop.
int dt_dev_history_refresh_nodes_ext(dt_develop_t *dev, GList **iop, GList *history)
Refresh GUI module nodes to match history state.
static gboolean _module_params_are_default(dt_iop_module_t *module)
Check if module params match defaults.
static gboolean _module_is_default_or_forced_enabled(dt_iop_module_t *module)
Check if a module is enabled by default or force-enabled.
static int _sync_params(dt_dev_history_item_t *hist, const void *module_params, const int param_length, const int modversion, int *legacy_params, const char *preset_name)
Load or convert module params into a history item.
gboolean _module_leaves_no_history(dt_iop_module_t *module)
Return whether a module never writes history entries.
dt_dev_history_item_t * dt_dev_history_item_create(void)
Allocate a fresh, blank history item with refcount 1.
int dt_dev_replace_history_on_image(dt_develop_t *dev_src, const int32_t dest_imgid, const gboolean reload_defaults, const char *msg)
Replace an image history with the content of dev_src.
static int _create_deleted_modules(GList **_iop_list, GList *history_list)
Recreate missing module instances referenced by history.
void dt_dev_transient_params_clear(dt_iop_module_t *module)
Drop the transient slot if it belongs to module.
void dt_dev_history_cleanup(void)
Cleanup cached statements or state used by history I/O.
dt_iop_module_t * dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name, const int multi_priority)
Find a module instance by op name and instance metadata.
void dt_dev_transient_params_set(dt_iop_module_t *module, const void *params, const size_t params_size, const void *blend_params, const size_t blend_size)
Out-of-history transient param channel.
static int _check_deleted_instances(dt_develop_t *dev, GList **_iop_list, GList *history_list)
Detect and handle module instances that exist in iop list but not in history.
void dt_dev_free_history_item(gpointer data)
Release a reference to a history item (used as GList free callback).
gboolean dt_dev_history_item_update_from_params(dt_develop_t *dev, dt_dev_history_item_t *hist, dt_iop_module_t *module, const gboolean enabled, const void *params, const int32_t params_size, const dt_develop_blend_params_t *blend_params, GList *forms)
dt_iop_module_t * dt_dev_create_module_instance(dt_develop_t *dev, const char *op, const char *multi_name, const int multi_priority, gboolean use_next_priority)
Create a new module instance from an existing base .so.
GList * dt_history_duplicate(GList *hist)
Deep-copy a history list.
static dt_dev_history_item_t * _dt_dev_history_item_duplicate_one(const dt_dev_history_item_t *old)
int dt_dev_copy_module_contents(dt_develop_t *dev_dest, dt_develop_t *dev_src, dt_iop_module_t *mod_dest, const dt_iop_module_t *mod_src)
static dt_iop_module_t * _history_merge_resolve_dest_instance(dt_develop_t *dev_dest, const dt_iop_module_t *mod_src, gboolean *created, gboolean *reused_base)
Resolve or create the destination module instance for history merge.
static void _dev_history_add_filtered(dt_develop_t *dev, dt_iop_module_filter_t filter)
Append history items for modules that pass a filter.
void dt_dev_history_free_history(dt_develop_t *dev)
Free the whole history list attached to dev->history.
static void _history_undo_data_free(gpointer data)
Free an undo history snapshot structure.
void dt_dev_history_compress_ext(dt_develop_t *dev, gboolean write_history)
Variant of history compression that optionally skips DB writeback.
gboolean(* dt_iop_module_filter_t)(dt_iop_module_t *module)
void dt_dev_history_undo_invalidate_module(dt_iop_module_t *module)
Invalidate a module pointer inside undo snapshots.
void dt_dev_history_compress(dt_develop_t *dev)
Compress an history from a loaded pipeline, aka simply take a snapshot of all modules parameters....
dt_dev_history_item_t * dt_dev_history_item_ref(dt_dev_history_item_t *item)
Take a reference on a history item. Pair with dt_dev_free_history_item().
void dt_dev_pop_history_items_ext(dt_develop_t *dev)
Apply history items to module params up to dev->history_end.
static dt_dev_history_item_t * _search_history_by_op(dt_develop_t *dev, const dt_iop_module_t *module)
Find the first history item matching a module operation name.
gboolean dt_dev_history_is_image_in_dev(GList *imgs)
Check whether any image of the list is the one currently loaded in the darkroom.
int dt_dev_history_item_from_source_history_item(dt_develop_t *dev_dest, dt_develop_t *dev_src, const dt_dev_history_item_t *hist_src, dt_iop_module_t *mod_dest, dt_dev_history_item_t **out_hist)
uint64_t dt_dev_history_compute_hash(dt_develop_t *dev)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
int dt_dev_merge_history_into_image(dt_develop_t *dev_src, int32_t dest_imgid, const GList *mod_list, gboolean merge_iop_order, const dt_history_merge_strategy_t mode, const gboolean paste_instances, const char *source_label, dt_hm_batch_state_t *batch)
Merge a list of modules into a destination image history via dt_history_merge().
void dt_dev_pop_history_items(dt_develop_t *dev)
Thread-safe wrapper around dt_dev_pop_history_items_ext(), then update GUI.
void dt_dev_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
void dt_dev_history_truncate(dt_develop_t *dev, const int32_t imgid)
static gboolean _compress_enabled_default_or_forced(dt_iop_module_t *module)
Filter: enabled modules that are default/forced enabled.
static void _reset_module_instance(GList *hist, dt_iop_module_t *module, int multi_priority)
Rebind history items to a module instance after recreation.
dt_dev_history_item_t * dt_dev_history_get_last_item_by_module(GList *history_list, dt_iop_module_t *module, int history_end)
Find the last history item referencing a module up to history_end.
void dt_dev_history_compress_or_truncate(dt_develop_t *dev)
Compress history if history_end is at top, otherwise truncate.
gboolean dt_dev_transient_params_active(dt_develop_t *dev, const dt_iop_module_t *module)
static void _dt_dev_history_compress_internal(dt_develop_t *dev, const gboolean write_history)
Rebuild history from current pipeline state.
static void _cleanup_history(const int32_t imgid)
Delete all history entries for an image from the DB.
static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num, const int modversion, const char *operation, const void *module_params, const int param_length, const gboolean enabled, const void *blendop_params, const int bl_length, const int blendop_version, const int multi_priority, const char *multi_name, const char *preset_name, int *legacy_params, const gboolean presets)
Build a history item from DB row data and append it to dev->history.
void dt_dev_history_undo_start_record_locked(dt_develop_t *dev)
Start an undo record with history_mutex already locked.
void dt_dev_history_undo_end_record(dt_develop_t *dev)
Finish an undo record for history changes.
void dt_dev_history_undo_end_record_locked(dt_develop_t *dev)
Finish an undo record with history_mutex already locked.
gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets)
Initialize module defaults and insert required default modules.
gboolean dt_history_module_skip_copy(const int flags)
Determine whether a module should be skipped during history copy.
guint dt_dev_mask_history_overload(GList *dev_history, guint threshold)
static int _dt_dev_write_history_job_run(dt_job_t *job)
void dt_dev_history_pixelpipe_update(dt_develop_t *dev, gboolean rebuild)
Rebuild or resync pixelpipes after backend history changes.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
void dt_dev_history_undo_start_record(dt_develop_t *dev)
Start an undo record for history changes.
static void _remove_history_leaks(dt_develop_t *dev)
Remove history items past history_end when allowed.
static void _sync_blendop_params(dt_dev_history_item_t *hist, const void *blendop_params, const int bl_length, const int blendop_version, int *legacy_params)
Load or convert blendop params into a history item.
void dt_dev_invalidate_history_module(GList *list, dt_iop_module_t *module)
Remove a module pointer from a history list.
void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid)
Write dev->history to DB and XMP for a given image id.
void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
Notify the rest of the app that history changes were written.
gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
Reload history from DB and rebuild pipelines/GUI state.
static gboolean _compress_disabled_with_history(dt_iop_module_t *module)
Filter: disabled modules that still need history entries.
gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid)
Read history and masks from DB and populate dev->history.
gboolean dt_dev_add_history_item_ext(dt_develop_t *dev, struct dt_iop_module_t *module, gboolean enable, gboolean force_new_item)
Append or update a history item for a module.
static gboolean _compress_enabled_user_default_params(dt_iop_module_t *module)
Filter: enabled modules with default params (user enabled).
static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited)
Insert default modules into history when needed.
static gboolean _dev_auto_apply_presets(dt_develop_t *dev, int32_t imgid)
Apply auto-presets and default iop order for a fresh history.
static gboolean _module_has_nondefault_internal_params(dt_iop_module_t *module)
Check if any module params (including blend params) are non-default.
dt_dev_history_item_t * dt_dev_history_get_first_item_by_module(GList *history_list, dt_iop_module_t *module)
Find the first history item referencing a module.
dt_dev_history_item_t * dt_dev_history_cow_touch(dt_develop_t *dev, dt_dev_history_item_t *hist)
Copy-on-write gate for mutating a history item in place.
void dt_dev_set_history_end_ext(struct dt_develop_t *dev, const uint32_t index)
Set the history end index (GUI perspective).
Definition develop.c:1729
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1723
#define dt_dev_pixelpipe_resync_history_all(dev)
#define dt_dev_pixelpipe_rebuild_all(dev)
#define dt_dev_pixelpipe_update_history_all(dev)
void dt_dev_signal_modules_moved(dt_develop_t *dev)
Definition develop.c:1672
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1240
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:356
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:225
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1678
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:98
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1745
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1689
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:900
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1245
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:510
dt_dev_pixelpipe_display_mask_t
Definition develop.h:116
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
Definition develop.h:515
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:452
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:502
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:534
void * legacy_params(dt_imageio_module_format_t *self, const void *const old_params, const size_t old_params_size, const int old_version, const int new_version, size_t *new_size)
Definition exr.cc:290
@ FOR_RAW
Definition gui/presets.h:41
@ FOR_NOT_MONO
Definition gui/presets.h:43
@ FOR_LDR
Definition gui/presets.h:40
@ FOR_HDR
Definition gui/presets.h:42
@ FOR_NOT_COLOR
Definition gui/presets.h:44
static gboolean enable(dt_image_t *image)
Definition highlights.c:653
int dt_history_merge(dt_develop_t *dev_dest, dt_develop_t *dev_src, const int32_t dest_imgid, const GList *mod_list, const gboolean merge_iop_order, const dt_history_merge_strategy_t strategy, const gboolean force_new_modules, const char *source_label, dt_hm_batch_state_t *batch)
Merge a list of modules into a destination image, solving pipeline topologies for proper insertion of...
dt_history_merge_strategy_t
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:119
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:117
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:49
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
Definition imageop.c:3017
gboolean dt_iop_module_needs_mask_history_ext(const dt_iop_module_t *module, gboolean *raster, gboolean *drawn, gboolean *parametric)
Definition imageop.c:1676
void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:2036
void dt_iop_load_default_params(dt_iop_module_t *module)
Definition imageop.c:142
void dt_iop_compute_module_hash(dt_iop_module_t *module, GList *masks)
Definition imageop.c:1928
void dt_iop_reload_defaults(dt_iop_module_t *module)
Definition imageop.c:1278
void dt_iop_gui_set_expander(dt_iop_module_t *module)
Definition imageop.c:2793
void dt_iop_gui_init(dt_iop_module_t *module)
Definition imageop.c:1235
void dt_iop_commit_blend_params(dt_iop_module_t *module, const dt_develop_blend_params_t *blendop_params)
Definition imageop.c:1705
void dt_iop_gui_update(dt_iop_module_t *module)
Definition imageop.c:2133
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1567
gboolean dt_iop_module_needs_mask_history(const dt_iop_module_t *module)
Definition imageop.c:1699
void dt_iop_gui_set_enable_button(dt_iop_module_t *module)
Definition imageop.c:1211
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2221
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1133
gboolean dt_iop_check_modules_equal(dt_iop_module_t *mod_1, dt_iop_module_t *mod_2)
Definition imageop.c:1856
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:3160
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:3122
@ IOP_FLAGS_HIDDEN
Definition imageop.h:200
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:198
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:207
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:202
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:204
struct dt_iop_tonecurve_params_t preset
gboolean dt_ioppr_has_iop_order_list(int32_t imgid)
Check whether the image has an explicit order list stored in DB.
Definition iop_order.c:1073
GList * dt_ioppr_get_iop_order_list_version(dt_iop_order_t version)
Return the built-in order list for a given version.
Definition iop_order.c:1044
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:2008
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:2002
gboolean dt_ioppr_write_iop_order_list(GList *iop_order_list, const int32_t imgid)
Persist an order list to the DB for a given image.
Definition iop_order.c:1013
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
Definition iop_order.c:867
void dt_ioppr_set_default_iop_order(dt_develop_t *dev, const int32_t imgid)
Set dev->iop_order_list to the default order for a given image.
Definition iop_order.c:1331
void dt_ioppr_resync_pipeline(dt_develop_t *dev, const int32_t imgid, const char *msg, gboolean check_duplicates)
Resynchronize pipeline order and related structures.
Definition iop_order.c:1263
GList * dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
Deserialize an order list from a binary blob.
Definition iop_order.c:2638
dt_iop_order_t
Definition iop_order.h:143
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:148
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:149
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:135
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:405
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:129
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:112
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:153
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:55
dt_masks_edit_mode_t
Definition masks.h:201
@ DT_MASKS_EDIT_OFF
Definition masks.h:202
void dt_masks_read_masks_history(dt_develop_t *dev, const int32_t imgid)
void dt_masks_write_masks_history_item(const int32_t imgid, const int num, dt_masks_form_t *form)
dt_masks_edit_mode_t dt_masks_get_edit_mode(struct dt_iop_module_t *module)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
int dt_masks_copy_used_forms_for_module(dt_develop_t *dev_dest, dt_develop_t *dev_src, const struct dt_iop_module_t *mod_src)
GList * dt_masks_snapshot_current_forms(dt_develop_t *dev, gboolean reset_changed)
void dt_masks_replace_current_forms(dt_develop_t *dev, GList *forms)
dt_masks_form_t * dt_masks_form_ref(dt_masks_form_t *form)
void dt_masks_form_unref(dt_masks_form_t *form)
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_module_t *int multi_priority
struct dt_undo_t * undo
Definition darktable.h:815
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_image_cache_t * image_cache
Definition darktable.h:805
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_control_t * control
Definition darktable.h:801
dt_iop_params_t * params
Definition dev_history.h:53
dt_atomic_int refcount
Definition dev_history.h:72
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:56
struct dt_iop_module_t *gboolean enabled
Definition dev_history.h:51
gpointer last_history_item
dt_atomic_int shutdown
int32_t gui_attached
Definition develop.h:162
GList * iop_order_list
Definition develop.h:291
int undo_history_before_end
Definition develop.h:298
dt_image_t image_storage
Definition develop.h:259
GList * undo_history_before_snapshot
Definition develop.h:297
GList * iop
Definition develop.h:285
struct dt_develop_t::@18 transient_params
GList * undo_history_before_iop_order_list
Definition develop.h:299
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
gboolean forms_changed
Definition develop.h:332
uint64_t serial
Definition develop.h:315
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
GList * history
Definition develop.h:275
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
struct dt_iop_module_t *void * params
Definition develop.h:310
GList * alliop
Definition develop.h:287
dt_atomic_int history_write_pending
Definition develop.h:281
dt_pthread_mutex_t transient_params_mutex
Definition develop.h:317
int32_t blend_size
Definition develop.h:314
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
GList * forms
Definition develop.h:327
int undo_history_depth
Definition develop.h:296
void * blend_params
Definition develop.h:313
int32_t params_size
Definition develop.h:312
uint64_t history_hash
Definition image.h:322
int32_t flags
Definition image.h:319
GTimeSpan change_timestamp
Definition image.h:333
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
int32_t id
Definition image.h:319
GtkWidget * showmask
Definition blend.h:341
gpointer blend_data
Definition imageop.h:355
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:353
GtkWidget * widget
Definition imageop.h:374
char multi_name[128]
Definition imageop.h:400
struct dt_develop_t * dev
Definition imageop.h:333
gboolean default_enabled
Definition imageop.h:340
GModule *dt_dev_operation_t op
Definition imageop.h:286
int32_t instance
Definition imageop.h:288
gboolean workflow_enabled
Definition imageop.h:342
gboolean enabled
Definition imageop.h:335
dt_iop_module_so_t * so
Definition imageop.h:396
GtkWidget * expander
Definition imageop.h:382
int request_mask_display
Definition imageop.h:298
struct dt_develop_blend_params_t * default_blendop_params
Definition imageop.h:353
int32_t params_size
Definition imageop.h:346
dt_iop_params_t * params
Definition imageop.h:344
Definition iop_order.h:154
char name[25]
Definition iop_order.h:162
int iop_order
Definition iop_order.h:157
char operation[20]
Definition iop_order.h:160
union dt_iop_order_entry_t::@8 o
int32_t instance
Definition iop_order.h:161
GList * before_snapshot
dt_dev_pixelpipe_display_mask_t request_mask_display
GList * before_iop_order_list
GList * after_iop_order_list
dt_masks_edit_mode_t mask_edit_mode
void dt_supervisor_rekey(const uint64_t old_hash, const uint64_t new_hash)
void dt_supervisor_history(const dt_sv_op_t op, const uint64_t param_hash, const char *op_name, const int multi_priority, const char *multi_name, const int iop_order, const int history_index, const int32_t imgid, const gboolean enabled, const dt_iop_module_t *module, const void *params, const dt_develop_blend_params_t *blend_params, GList *forms)
Definition supervisor.c:804
@ DT_SV_UPDATE
Definition supervisor.h:78
@ DT_SV_CREATE
Definition supervisor.h:77
@ DT_SV_DELETE
Definition supervisor.h:80
static gboolean dt_supervisor_active(void)
Definition supervisor.h:94
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_undo_iterate_internal(dt_undo_t *self, uint32_t filter, gpointer user_data, void(*apply)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item))
Definition undo.c:364
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
Definition undo.c:163
dt_undo_type_t
Definition undo.h:39
@ DT_UNDO_HISTORY
Definition undo.h:42
void * dt_undo_data_t
Definition undo.h:67
dt_undo_action_t
Definition undo.h:62
@ DT_ACTION_UNDO
Definition undo.h:63