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*/
62#include "control/control.h"
63#include "common/conf.h"
64#include "history/history.h"
65#include "history/notify.h"
66#include "history/presets.h" // FOR_RAW/FOR_LDR/... matched against the image at auto-apply
68
69#include "common/undo.h"
70#include "caches/image_cache.h"
72#include "develop/iop_order.h"
73#include "develop/dev_history.h"
74#include "develop/blend.h"
75#include "develop/develop.h"
76#include "develop/imageop.h"
78#include "develop/masks.h"
79#include "develop/supervisor.h"
81#include "system/atomic.h"
82
83
84#include <inttypes.h>
85
86#include <glib.h>
87#include "common/hash.h"
88#include "gui/application.h"
89
90static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
91 const int modversion, const char *operation, const void *module_params,
92 const int param_length, const gboolean enabled, const void *blendop_params,
93 const int bl_length, const int blendop_version, const int multi_priority,
94 const char *multi_name, const char *preset_name, int *legacy_params,
95 const gboolean presets);
96
104
105gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets);
106
127static void _dev_history_db_row_cb(void *user_data, const int32_t id, const int num, const int modversion,
128 const char *operation, const void *module_params, const int param_length,
129 const gboolean enabled, const void *blendop_params, const int bl_length,
130 const int blendop_version, const int multi_priority, const char *multi_name,
131 const char *preset_name)
132{
134 _process_history_db_entry(ctx->dev, ctx->imgid, id, num, modversion, operation, module_params, param_length, enabled,
135 blendop_params, bl_length, blendop_version, multi_priority, multi_name, preset_name,
136 ctx->legacy_params, ctx->presets);
137}
138
139// returns the first history item with hist->module == module
141{
143 for(GList *history = g_list_first(history_list); history; history = g_list_next(history))
144 {
145 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
146
147 if(hist->module == module)
148 {
149 hist_mod = hist;
150 break;
151 }
152 }
153 return hist_mod;
154}
155
157{
159 for(GList *history = g_list_nth(history_list, history_end -1); history; history = g_list_previous(history))
160 {
161 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
162
163 if(hist->module == module)
164 {
165 hist_mod = hist;
166 break;
167 }
168 }
169 return hist_mod;
170}
171
173 const gboolean enabled, const void *params, const int32_t params_size,
174 const dt_develop_blend_params_t *blend_params, GList *forms)
175{
176 if(IS_NULL_PTR(hist) || IS_NULL_PTR(module)) return FALSE;
177
178 if(IS_NULL_PTR(hist->params))
179 {
180 hist->params = g_malloc0(module->params_size);
181 if(IS_NULL_PTR(hist->params)) return FALSE;
182 }
183
184 if(IS_NULL_PTR(hist->blend_params))
185 {
187 if(IS_NULL_PTR(hist->blend_params)) return FALSE;
188 }
189
190 if(!IS_NULL_PTR(hist->forms))
191 {
192 g_list_free_full(hist->forms, (void (*)(void *))dt_masks_form_unref);
193 hist->forms = NULL;
194 }
195 hist->forms = forms;
196
197 module->enabled = enabled;
198 hist->enabled = enabled;
199 hist->module = module;
200 hist->params_size = module->params_size;
201 hist->module_version = module->version();
204 hist->iop_order = module->iop_order;
205 hist->multi_priority = module->multi_priority;
206 g_strlcpy(hist->op_name, module->op, sizeof(hist->op_name));
207 g_strlcpy(hist->multi_name, module->multi_name, sizeof(hist->multi_name));
208
209 const void *src_params = params ? params : module->params;
210 const int32_t src_size = params ? params_size : module->params_size;
211 const int32_t sz = MIN(module->params_size, src_size);
212 if(!IS_NULL_PTR(src_params) && !IS_NULL_PTR(hist->params) && sz > 0) memcpy(hist->params, src_params, sz);
213
214 const dt_develop_blend_params_t *src_blend = blend_params ? blend_params : module->blend_params;
216
217 // Apply to module to keep the hash in sync.
218 if(hist->params && module->params && module->params_size > 0)
219 memcpy(module->params, hist->params, module->params_size);
221
222 // Committing a new/updated item: when no forms snapshot was taken (include_masks declined),
223 // the live dev->forms IS the state being persisted, so hash against it — a module whose
224 // blend params link a mask group must never hash blind to that group's content (#1060).
225 // History REPLAY must not use this helper's fallback: it hashes via _history_to_module()
226 // against the snapshot accumulated at the item's position instead.
228 hist->hash = module->hash;
229
230 return TRUE;
231}
232
234{
235 int max_priority = 0;
236 for(const GList *l = g_list_first(dev->iop); l; l = g_list_next(l))
237 {
238 const dt_iop_module_t *m = (const dt_iop_module_t *)l->data;
239 if(strcmp(m->op, op)) continue;
240 max_priority = MAX(max_priority, m->multi_priority);
241 }
242 return max_priority + 1;
243}
244
245dt_iop_module_t *dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
246 const int multi_priority)
247{
248 const char *name = (multi_name && *multi_name) ? multi_name : NULL;
249 dt_iop_module_t *module = dt_iop_get_module_by_instance_name(dev->iop, op, name);
250 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
251 module = dt_iop_get_module_by_op_priority(dev->iop, op, multi_priority);
252 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
253 module = dt_iop_get_module_by_op_priority(dev->iop, op, 0);
254 return module;
255}
256
257dt_iop_module_t *dt_dev_create_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
258 const int multi_priority, gboolean use_next_priority)
259{
261 if(IS_NULL_PTR(base)) base = dt_iop_get_module_by_op_priority(dev->iop, op, -1);
262 if(IS_NULL_PTR(base)) return NULL;
263
264 if((base->flags() & IOP_FLAGS_ONE_INSTANCE) == IOP_FLAGS_ONE_INSTANCE)
265 return base;
266
267 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
268 if(IS_NULL_PTR(module)) return NULL;
269
270 if(dt_iop_load_module(module, base->so, dev))
271 {
272 fprintf(stderr, "[dt_dev_create_module_instance] can't load module %s\n", op);
274 return NULL;
275 }
276
277 module->instance = base->instance;
278 module->enabled = FALSE;
279 module->multi_priority = use_next_priority ? dt_dev_next_multi_priority_for_op(dev, op) : multi_priority;
280 g_strlcpy(module->multi_name, multi_name ? multi_name : "", sizeof(module->multi_name));
281
282 dev->iop = g_list_append(dev->iop, module);
283 return module;
284}
285
288{
289 mod_dest->enabled = mod_src->enabled;
290
291 const int32_t sz_dest = mod_dest->params_size;
292 const int32_t sz_src = mod_src->params_size;
293
294 // If both param sizes don't match, we have a serious problem
296 if(sz_dest != sz_src) return 1;
297
298 const int32_t sz = MIN(sz_dest, sz_src);
299 if(sz > 0) memcpy(mod_dest->params, mod_src->params, sz);
300
301 if(mod_src->blend_params) dt_iop_commit_blend_params(mod_dest, mod_src->blend_params);
302
303 if(dev_src && dt_masks_copy_used_forms_for_module(dev_dest, dev_src, mod_src)) return 1;
304 return 0;
305}
306
310{
312 {
314 "[dt_dev_history_item_from_source_history_item] invalid input: hist=%s hist_module=%s dest=%s out=%s\n",
315 hist_src ? "yes" : "no", hist_src && hist_src->module ? "yes" : "no",
316 mod_dest ? "yes" : "no", out_hist ? "yes" : "no");
317 return 1;
318 }
319
321 if(IS_NULL_PTR(hist))
322 {
324 "[dt_dev_history_item_from_source_history_item] allocation failed: src=%s multi='%s'\n",
325 hist_src->module->op, hist_src->module->multi_name);
326 return 1;
327 }
328
329 if(dt_masks_copy_used_forms_for_module(dev_dest, dev_src, hist_src->module))
330 {
332 "[dt_dev_history_item_from_source_history_item] Forms copy failed: src=%s multi='%s'\n",
333 hist_src->module->op, hist_src->module->multi_name);
335 return 1;
336 }
337
338 gboolean raster_used = FALSE;
339 gboolean drawn_used = FALSE;
340 gboolean parametric_used = FALSE;
343 {
344 if(drawn_used)
345 {
348 {
350 "[dt_dev_history_item_from_source_history_item] %s '%s' uses drawn mask but there is no destination mask forms to snapshot\n",
351 hist_src->module->op, hist_src->module->multi_name);
352
354 return 1;
355 }
356 }
357 }
358
360 hist_src->module->params_size, hist_src->blend_params, forms_snapshot))
361 {
363 "[dt_dev_history_item_from_source_history_item] params update failed: src=%s multi='%s' "
364 "dest=%s multi='%s' src_params=%d dest_params=%d\n",
365 hist_src->module->op, hist_src->module->multi_name, mod_dest->op, mod_dest->multi_name,
366 hist_src->module->params_size, mod_dest->params_size);
368 return 1;
369 }
370
371 *out_hist = hist;
372 return 0;
373}
374
375int dt_dev_merge_history_into_image(dt_develop_t *dev_src, int32_t dest_imgid, const GList *mod_list,
376 gboolean merge_iop_order, const dt_history_merge_strategy_t mode,
377 const gboolean paste_instances, const char *source_label,
378 dt_hm_batch_state_t *batch)
379{
380 if(dest_imgid <= 0) return 1;
381 if(IS_NULL_PTR(mod_list)) return 0;
382
383 dt_develop_t dev_dest = { 0 };
384 dt_dev_init(&dev_dest, FALSE);
385 const gboolean first_run = dt_dev_reload_history_items(&dev_dest, dest_imgid);
386
387 if(first_run)
388 {
389 /* Match the persistent state produced by opening the destination in darkroom
390 * after a history deletion: the first-run defaults, auto-presets, image
391 * flags and resulting module order must exist before paste/style merging.
392 */
394 if(!IS_NULL_PTR(image))
395 {
396 *image = dev_dest.image_storage;
398 }
399
401 }
402
403 /* Honor the high-level "use source pipeline order" choice on every image, including freshly recreated
404 * destinations. The topological solve still falls back to destination order when source constraints are
405 * unsatisfiable, so this stays safe while making the batch decision apply uniformly. */
406 const int ret_val = dt_history_merge(&dev_dest, dev_src, dest_imgid, mod_list, merge_iop_order, mode,
408
409 if(ret_val == 0)
410 {
412 dt_dev_write_history(&dev_dest, FALSE);
413 }
414
415 dt_dev_cleanup(&dev_dest);
416 return ret_val;
417}
418
427{
429 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
430 {
431 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
432 if(!hist || !hist->module) continue;
433
434 if(strcmp(hist->module->op, module->op) == 0)
435 {
436 hist_mod = hist;
437 break;
438 }
439 }
440 return hist_mod;
441}
442
456 gboolean *created,
457 gboolean *reused_base) REQUIRES_SHARED(dev_dest->history_mutex)
458{
459 *created = FALSE;
461
462 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
463 return dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
464
465 dt_iop_module_t *module
466 = dt_dev_get_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority);
467 if(module) return module;
468
469 if(_search_history_by_op(dev_dest, mod_src) == NULL)
470 {
471 module = dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
472 if(!IS_NULL_PTR(module))
473 {
474 *reused_base = TRUE;
475 return module;
476 }
477 }
478
479 module = dt_dev_create_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority, FALSE);
480 if(module) *created = TRUE;
481
482 return module;
483}
484
485// dev_src is used only to copy masks, if no mask will be copied it can be null
487 REQUIRES(dev_dest->history_mutex)
488{
489 gboolean created = FALSE;
490 gboolean reused_base = FALSE;
491 dt_iop_module_t *module = _history_merge_resolve_dest_instance(dev_dest, mod_src, &created, &reused_base);
492 if(IS_NULL_PTR(module)) return 0;
493
494 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
495 {
496 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",
497 mod_src->name(), mod_src->multi_name);
498 }
499 else if(created)
500 {
501 dt_print(DT_DEBUG_HISTORY, "[dt_history_merge_module_into_history] %s (%s) will be inserted as a new instance in target history\n",
502 mod_src->name(), mod_src->multi_name);
503 }
504 else if(reused_base)
505 {
506 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",
507 mod_src->name(), mod_src->multi_name);
508 }
509
510 g_strlcpy(module->multi_name, mod_src->multi_name, sizeof(module->multi_name));
511
512 if(dt_dev_copy_module_contents(dev_dest, dev_src, module, mod_src)) return 0;
513
515
516 return 1;
517}
527{
528 GList *result = NULL;
529 for(GList *h = g_list_first(hist); h; h = g_list_next(h))
530 {
531 const dt_dev_history_item_t *old = (dt_dev_history_item_t *)(h->data);
533
534 memcpy(new, old, sizeof(dt_dev_history_item_t));
535 dt_atomic_set_int(&new->refcount, 1); // independent deep copy, not a shared reference
536
537 dt_iop_module_t *module = (old->module) ? old->module : dt_iop_get_module(old->op_name);
538
539 if(old->params && old->params_size > 0)
540 {
541 new->params = malloc(old->params_size);
542 memcpy(new->params, old->params, old->params_size);
543 }
544
546 fprintf(stderr, "[_duplicate_history] can't find base module for %s\n", old->op_name);
547
548 if(old->blend_params && old->blendop_params_size > 0)
549 {
550 new->blend_params = malloc(old->blendop_params_size);
551 memcpy(new->blend_params, old->blend_params, old->blendop_params_size);
552 }
553
554 if(old->forms)
555 {
556 // Share references with old->forms instead of deep-copying: forms are refcounted
557 // and cloned on write (dt_masks_cow_touch), not on snapshot (see masks_history.h).
558 new->forms = g_list_copy(old->forms);
561 }
562
563 result = g_list_prepend(result, new);
564 }
565
566 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
567}
568
569/* Installed by dt_dev_history_gui_init(); absent under ansel-cli and in tests. */
571
576
578
583
592
594{
595 dt_iop_module_t *module;
597};
598
606static void _history_invalidate_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item)
607{
608 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
609 dt_undo_history_t *hist = (dt_undo_history_t *)item;
610 dt_dev_invalidate_history_module(hist->before_snapshot, module);
611 dt_dev_invalidate_history_module(hist->after_snapshot, module);
612}
613
619
639
651/* No REQUIRES: this is a dt_undo_t callback, so its signature is fixed and carries no
652 * dt_develop_t to name the lock on -- it reaches the dev through user_data. The lock is
653 * taken inside. NO_THREAD_SAFETY_ANALYSIS rather than a contract clang cannot express. */
655{
656 if(type != DT_UNDO_HISTORY) return;
657
658 dt_develop_t *dev = (dt_develop_t *)user_data;
659 dt_undo_history_t *hist = (dt_undo_history_t *)data;
660 if(IS_NULL_PTR(dev) || IS_NULL_PTR(hist)) return;
661
662 GList *snapshot = (action == DT_ACTION_UNDO) ? hist->before_snapshot : hist->after_snapshot;
663 const int history_end = (action == DT_ACTION_UNDO) ? hist->before_end : hist->after_end;
664
665 GList *iop_order_list
667
670
673 dev->history = history_temp;
674 dt_dev_set_history_end_ext(dev, history_end);
680
682 // TODO: check if we need to rebuild the full pipeline and do it only if needed
684
685 /* Republish the darkroom geometry, like every other path that replays history in bulk:
686 * dt_dev_pop_history_items() does it, so does the history-row click in libs/history.c, and
687 * so do image load and compress/truncate. This one did not, and undo is the path where it
688 * matters most -- undoing a crop changes the processed size, and without this the geometry
689 * record (hence natural_scale, the preview dimensions, and everything the pipes plan from)
690 * kept describing the pre-undo image until some unrelated later caller happened to refresh
691 * it. dt_dev_history_pixelpipe_update() above rebuilds the virtual pipe, so this only folds
692 * and publishes.
693 *
694 * After the history lock is released, never inside it: dt_dev_get_thumbnail_size() resyncs
695 * the virtual pipe, which takes history_mutex as reader, and the same-thread reentrancy that
696 * makes this survivable is a safety net, not a licence -- the sibling call sites all note the
697 * same ordering constraint. */
699
700 if(dev->gui_module)
701 {
704 // the blending panel's widgets are the GUI half's to poke (dev_history_gui.c)
707 }
708
709 // Ensure all UI pieces (history treeview, iop order, etc.) resync after undo/redo.
710 // Undo callbacks bypass dt_dev_undo_end_record(), so we need to raise the change signal here.
711 // The interactive session's panels resync through the changed-handler; the
712 // dev == dt_dev_get_global() test is engine knowledge (only the interactive dev has
713 // panels), so it stays here rather than in the handler.
714 if(dev->gui_attached && dev == dt_dev_get_global())
716}
717
725
727{
728 if(IS_NULL_PTR(dev)) return;
729
730 if(dev->undo_history_depth == 0)
731 {
732 g_list_free_full(dev->undo_history_before_snapshot, dt_dev_free_history_item);
733 dev->undo_history_before_snapshot = NULL;
734 g_list_free_full(dev->undo_history_before_iop_order_list, dt_free_gpointer);
735 dev->undo_history_before_iop_order_list = NULL;
736 dev->undo_history_before_end = 0;
737
738 dev->undo_history_before_snapshot = dt_history_duplicate(dev->history);
739 dev->undo_history_before_end = dt_dev_get_history_end_ext(dev);
740 dev->undo_history_before_iop_order_list = dt_ioppr_iop_order_copy_deep(dev->iop_order_list);
741 }
742
743 dev->undo_history_depth++;
744}
745
753
755{
756 if(IS_NULL_PTR(dev) || dev->undo_history_depth <= 0) return;
757
758 dev->undo_history_depth--;
759 if(dev->undo_history_depth != 0) return;
760
761 if(IS_NULL_PTR(dev->undo_history_before_snapshot)) return;
762
764 hist->before_snapshot = dev->undo_history_before_snapshot;
765 hist->before_end = dev->undo_history_before_end;
766 hist->before_iop_order_list = dev->undo_history_before_iop_order_list;
767 dev->undo_history_before_snapshot = NULL;
768 dev->undo_history_before_end = 0;
769 dev->undo_history_before_iop_order_list = NULL;
770
771 hist->after_snapshot = dt_history_duplicate(dev->history);
773 hist->after_iop_order_list = dt_ioppr_iop_order_copy_deep(dev->iop_order_list);
774
775 if(dev->gui_module)
776 {
777 hist->mask_edit_mode = dt_masks_get_edit_mode(dev->gui_module);
778 hist->request_mask_display = dev->gui_module->request_mask_display;
779 }
780 else
781 {
784 }
785
787}
788
789
797static void _remove_history_leaks(dt_develop_t *dev) REQUIRES(dev->history_mutex)
798{
799 GList *history = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev));
800 while(history)
801 {
802 // We need to use a while because we are going to dynamically remove entries at the end
803 // of the list, so we can't know the number of iterations
804 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
805 if(IS_NULL_PTR(hist) || IS_NULL_PTR(hist->module))
806 {
808 "[dt_dev_add_history_item_ext] archival history item %s at %i is past history limit (%i) and will be kept\n",
809 hist ? hist->op_name : "(null)", g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
810 history = g_list_next(history);
811 continue;
812 }
813
814 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history item %s at %i is past history limit (%i)\n",
815 hist->module->op, g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
816
817 // In case user wants to insert new history items before auto-enabled or mandatory modules,
818 // we forbid it, unless we already have at least one lower history entry.
819
820 // Check if an earlier instance of mandatory module exists
821 gboolean earlier_entry = FALSE;
822 if((hist->module->hide_enable_button || hist->module->default_enabled))
823 {
826 {
828 if(!prior_hist || !prior_hist->module) continue;
829 if(prior_hist->module->so == hist->module->so)
830 {
832 break;
833 }
834 }
835 }
836
837 // In case we delete the current link, we need to update the incrementer now
838 // to not loose the reference
839 GList *link = history;
840 history = g_list_next(history);
841
842 // Finally: attempt removing the obsoleted entry
843 if((!hist->module->hide_enable_button && !hist->module->default_enabled)
844 || earlier_entry)
845 {
846 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));
848 dt_supervisor_history(DT_SV_DELETE, hist->hash, hist->module->op, hist->module->multi_priority,
849 hist->module->multi_name, hist->module->iop_order,
850 g_list_index(dev->history, hist), dev->image_storage.id, hist->enabled,
851 hist->module, hist->params, hist->blend_params, hist->forms);
853 dev->history = g_list_delete_link(dev->history, link);
854 }
855 else
856 {
857 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));
858 }
859 }
860}
861
863 gboolean force_new_item) REQUIRES(dev->history_mutex)
864{
865 // If this history item is the first for this module,
866 // we need to notify the pipeline that its topology may change (aka insert a new node).
867 // Since changing topology is expensive, we want to do it only when needed.
868 gboolean add_new_pipe_node = FALSE;
869
871 {
872 // module = NULL means a mask was changed from the mask manager and that's where this function is called.
873 // Find it now, even though it is not enabled and won't be.
874 module = dt_masks_get_mask_manager(dev);
875 if(!IS_NULL_PTR(module))
876 {
877 // Mask manager is an IOP that never processes pixel aka it's an ugly hack to record mask history
879 enable = FALSE;
880 }
881 else
882 {
883 return add_new_pipe_node;
884 }
885 }
886
887 // look for leaks on top of history
889
890 // Enabled as it will be recorded, which is what the topology test below compares against.
891 if(enable) module->enabled = TRUE;
892
893 // Check if the current module to append to history is actually the same as the last one in history,
894 GList *last = g_list_last(dev->history);
895 gboolean new_is_old = FALSE;
896 if(last && last->data && !force_new_item)
897 {
901 }
902
903 // The set of active pipe nodes changes when this is the module's first history entry, or when
904 // its last one is enabled differently -- whether the top item is rewritten in place (the module
905 // toggled again right after its last edit) or a new item is appended.
907 dt_dev_history_get_last_item_by_module(dev->history, module, g_list_length(dev->history));
908 add_new_pipe_node = IS_NULL_PTR(previous_item) || (previous_item->enabled != module->enabled);
909
911 const gboolean is_new_item = force_new_item || !new_is_old;
912 if(is_new_item)
913 {
914 // Create a new history entry
916 dev->history = g_list_append(dev->history, hist);
917 hist->num = g_list_index(dev->history, hist);
918 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] new history entry added for %s at position %i\n",
919 module->name(), hist->num);
920 }
921 else
922 {
923 // Reuse previous history entry. Copy-on-write: a slow reader (pipe resync snapshot, DB
924 // write job snapshot) may still be holding a reference to this exact item -- clone before
925 // mutating if so, never mutate a shared item in place.
926 hist = dt_dev_history_cow_touch(dev, (dt_dev_history_item_t *)last->data);
927
928 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history entry reused for %s at position %i\n",
929 module->name(), hist->num);
930 }
931
932 // Include masks if module supports blending and masks are in use, or if it's the mask manager.
934
935 // That predicate requires the matching mask_mode bit (e.g. DEVELOP_MASK_SHAPE) to be set,
936 // which is a *masking policy* decision — but whether the forms must be recorded is a *state
937 // integrity* decision: as soon as blend params reference an existing, non-empty group, the
938 // forms are part of this module's state. Skipping the snapshot here makes the history item
939 // hash blind to mask edits (stale pipe cache, issue #1060 family) and loses the mask on
940 // undo/redo (dt_dev_pop_history_items_ext() rebuilds dev->forms from the last item that
941 // carries one).
942 if(!include_masks && !IS_NULL_PTR(module->blend_params) && module->blend_params->mask_id != 0)
943 {
944 dt_masks_form_t *linked_group = dt_masks_get_from_id(dev, module->blend_params->mask_id);
946 }
947
949 if(include_masks)
950 {
951 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);
952 // FIXME: this copies ALL drawn masks AND masks groups used by all modules to any module history using masks.
953 // Kudos to the idiots who thought it would be reasonable. Expect database bloating and perf penalty.
955 }
956 // else forms_snapshot stays NULL
957
958 // Capture the previous parameter hash: an in-place reuse rewrites it below,
959 // which the supervisor must see as a rekey (delete old hash + add new hash).
960 const uint64_t old_param_hash = hist->hash;
961
962 // Fill history item and recompute hash (also applies params/blend_params to module to keep hash consistent).
964 module->blend_params, forms_snapshot);
965
966 if(include_masks && hist->forms)
967 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);
968 else if(include_masks)
969 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);
970
971 // It is assumed that the last-added history entry is always on top
972 // so its cursor index is always equal to the number of elements,
973 // keeping in mind that history_end = 0 is the raw image, aka not a dev->history GList entry.
974 // So dev->history_end = index of last history entry + 1 = length of history
975 dt_dev_set_history_end_ext(dev, g_list_length(dev->history));
976
978 {
979 if(is_new_item)
980 dt_supervisor_history(DT_SV_CREATE, hist->hash, module->op, module->multi_priority,
981 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
982 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
983 else if(old_param_hash != hist->hash)
984 {
985 // in-place overwrite changed the hash: delete old key, add new key, then
986 // refresh the new entry's current state (enabled/index may have changed).
988 dt_supervisor_history(DT_SV_UPDATE, hist->hash, module->op, module->multi_priority,
989 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
990 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
991 }
992 else
993 dt_supervisor_history(DT_SV_UPDATE, hist->hash, module->op, module->multi_priority,
994 module->multi_name, module->iop_order, hist->num, dev->image_storage.id,
995 hist->enabled, module, hist->params, hist->blend_params, hist->forms);
996 }
997
998 return add_new_pipe_node;
999}
1000
1002{
1003 uint64_t hash = 5381;
1004 for(GList *hist = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev) - 1);
1005 hist;
1006 hist = g_list_previous(hist))
1007 {
1008 dt_dev_history_item_t *item = (dt_dev_history_item_t *)hist->data;
1009 hash = dt_hash(hash, (const char *)&item->hash, sizeof(uint64_t));
1010 }
1011 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));
1012 return hash;
1013}
1014
1015
1016/* The pending-commit throttle queue lives in dev_history_gui.c: it exists to coalesce
1017 * slider-drag input on the GTK main loop, and its headless timeout is zero -- the
1018 * engine entry it drains into is dt_dev_history_commit_item_now() below. */
1019
1021{
1022 gboolean add_new_pipe_node = FALSE;
1023
1026
1028 // Measures time actually spent blocked waiting for the lock (e.g. behind the
1029 // background dt_dev_write_history() job holding it as reader) -- not time spent
1030 // doing work while holding it. Threshold avoids logging the common sub-ms case.
1031 const double _wrlock_wait_start = dt_get_wtime();
1033 const double _wrlock_wait_ms = (dt_get_wtime() - _wrlock_wait_start) * 1000.0;
1034 if(_wrlock_wait_ms > 1.0)
1036 "[dt_dev_add_history_item_real] tid %lu (GUI thread) blocked %.2f ms acquiring history_mutex\n",
1037 (unsigned long)pthread_self(), _wrlock_wait_ms);
1041
1042 // Run the delayed post-commit actions if implemented
1043 if(!IS_NULL_PTR(module) && !IS_NULL_PTR(module->post_history_commit)) module->post_history_commit(module);
1044
1045 // Republish any dev->proxy state this module derives from its params (e.g. temperature's WB
1046 // coeffs) on the main thread, before the pipeline recompute below. This covers user edits and
1047 // resets; pipelines only read dev->proxy. (Bulk history loads go through pop_history_items_ext.)
1048 if(!IS_NULL_PTR(module) && !IS_NULL_PTR(module->commit_proxy)) module->commit_proxy(module);
1049
1050 // Figure out if the current history item includes masks/forms.
1051 // Read under the lock: the write lock was released above, so the background
1052 // dt_dev_write_history() job can be walking (and dropping references on) this very list.
1053 // Held only for the two list walks -- the image-cache update below stays outside it, so
1054 // the history_mutex -> image_cache order used by the write job is not introduced here.
1055 gboolean has_forms = FALSE;
1059 if(last_history)
1060 {
1062 has_forms = (!IS_NULL_PTR(hist->forms));
1063 }
1064
1065 // We don't update history hash in dt_dev_add_history_item_ext
1066 // because it can be called within loops, so that can be expensive.
1067 history_hash = dt_dev_history_compute_hash(dev);
1069
1070 dt_dev_set_history_hash(dev, history_hash);
1071 if(dev->image_storage.id > 0)
1072 {
1074 if(cache_img)
1075 {
1076 cache_img->history_hash = dt_dev_get_history_hash(dev);
1078 }
1079 }
1080
1081 // Recompute pipeline last
1082 const gboolean has_raster = module && dt_iop_module_has_raster_mask(module);
1084 {
1085 // If we have a module and it doesn't use drawn or raster masks,
1086 // we only need to resync the top-most history item with pipeline
1088 }
1089 else
1090 {
1091 // We either don't have a module, meaning we have the mask manager, or
1092 // we have a module and it uses masks (drawn or raster), or the current
1093 // history commit changes the pipeline topology. The latter happens for
1094 // example when enabling/disabling a module or when a module gets its first
1095 // history entry. In all these cases, updating only the top-most synced
1096 // history tail is insufficient because the set of active pipe nodes itself
1097 // may differ from the previous run.
1098 // Because masks can affect several modules anywhere, not necessarily sequentially,
1099 // we need a full resync of all pipeline with history.
1100 // Note that the blendop params (thus their hash) references the raster mask provider
1101 // in its consumer, and the consumer in its provider. So updating the whole pipe
1102 // resyncs the cumulative hashes too, and triggers a new recompute from the provider on update.
1103 // A module switched on or off is the one of these the user is likely to switch back: the
1104 // outputs it replaces are kept for that (dt_dev_pixelpipe_t.keep_outputs).
1106 {
1109 }
1111 }
1112
1114
1115 // The post-commit presentation work -- refreshing the viewport size when the module
1116 // changes ROI geometry, and syncing the enable toggle a param edit may have flipped --
1117 // is the GUI half's (dev_history_gui.c), reached through the handler.
1120
1121 // Save history straight away. Regular GUI edits are the only place where
1122 // we accept an asynchronous write because `dev` is the long-lived darkroom
1123 // context and there is no immediate DB read on the same control path.
1125}
1126
1128{
1130 if(IS_NULL_PTR(item)) return NULL;
1131 dt_atomic_set_int(&item->refcount, 1);
1132 return item;
1133}
1134
1135void dt_dev_free_history_item(gpointer data)
1136{
1138 if(IS_NULL_PTR(item)) return; // nothing to free
1139
1140 // Despite the name (kept so every existing g_list_free_full/GDestroyNotify call site gets
1141 // refcount-safety for free), this is now dev_history's unref: dev->history and a form cloned
1142 // by dt_dev_history_cow_touch each hold their own reference. Only the last one out actually
1143 // frees the buffers.
1144 if(dt_atomic_sub_int(&item->refcount, 1) != 1) return;
1145
1146 dt_free(item->params);
1147 dt_free(item->blend_params);
1148 g_list_free_full(item->forms, (void (*)(void *))dt_masks_form_unref);
1149 item->forms = NULL;
1150 dt_free(item);
1151}
1152
1154{
1155 if(IS_NULL_PTR(item)) return NULL;
1156 dt_atomic_add_int(&item->refcount, 1);
1157 return item;
1158}
1159
1160// Same per-field duplication as dt_history_duplicate(), factored out for cow-touch's
1161// single-item clone: fresh struct, own params/blend_params buffers, forms shared by reference
1162// (already refcounted, cloned on write by dt_masks_cow_touch -- not on this snapshot).
1164{
1167 dt_atomic_set_int(&new_item->refcount, 1); // fresh object: reset, don't inherit old's refcount
1168
1169 new_item->params = NULL;
1170 if(old->params && old->params_size > 0)
1171 {
1172 new_item->params = malloc(old->params_size);
1173 memcpy(new_item->params, old->params, old->params_size);
1174 }
1175
1176 new_item->blend_params = NULL;
1177 if(old->blend_params && old->blendop_params_size > 0)
1178 {
1179 new_item->blend_params = malloc(old->blendop_params_size);
1180 memcpy(new_item->blend_params, old->blend_params, old->blendop_params_size);
1181 }
1182
1183 new_item->forms = NULL;
1184 if(old->forms)
1185 {
1186 new_item->forms = g_list_copy(old->forms);
1189 }
1190
1191 return new_item;
1192}
1193
1195{
1196 if(IS_NULL_PTR(dev) || IS_NULL_PTR(hist)) return hist;
1197 if(dt_atomic_get_int(&hist->refcount) <= 1) return hist;
1198
1199 // dt_pthread_rwlock_wrlock is same-thread-recursive (dtpthread.h), so this is safe whether
1200 // or not the caller already holds history_mutex as writer (dt_dev_add_history_item_real
1201 // does; other callers of dt_dev_add_history_item_ext may not).
1203
1204 GList *node = g_list_find(dev->history, hist);
1205 if(IS_NULL_PTR(node))
1206 {
1207 // Not (yet) a member of dev->history: nothing to splice.
1209 return hist;
1210 }
1211
1213 node->data = clone;
1214
1215 // Each pipe's own last-synced-item marker (used by the synch_top path to bound the resync
1216 // range) may name the item being cloned; re-point it at the clone so an in-place top-entry
1217 // rewrite keeps its bounded resync instead of degrading to a full one.
1218 //
1219 // The pipe HOLDS A REFERENCE on that marker and the resync writes it OUTSIDE history_mutex
1220 // (it runs against a snapshot), so this is a genuine cross-thread exchange, not a
1221 // compare-and-assign under a shared lock. The atomic exchange keeps the refcount honest in
1222 // every interleaving: this side takes a reference on the clone before publishing it, and
1223 // releases exactly the reference it displaced. If the worker publishes its own item between
1224 // the compare and the exchange, the slot ends up naming the clone, the worker's item is
1225 // released here, and the worst case is one full resync next frame -- never a leak or a
1226 // double release.
1227 dt_dev_pixelpipe_t *const pipes[] = { dev->pipe, dev->preview_pipe };
1228 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
1229 {
1230 if(IS_NULL_PTR(pipes[i])) continue;
1231 if(dt_atomic_get_ptr(&pipes[i]->last_history_item) != hist) continue;
1234 = (dt_dev_history_item_t *)dt_atomic_exch_ptr(&pipes[i]->last_history_item, clone);
1236 }
1237
1239
1240 dt_dev_free_history_item(hist); // releases dev->history's old reference (unref semantics)
1241
1242 return clone;
1243}
1244
1246 REQUIRES_SHARED(dev->history_mutex)
1247{
1248 if(IS_NULL_PTR(snapshot)) return;
1249 snapshot->items = NULL;
1250 snapshot->history_end = 0;
1251 snapshot->history_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
1252 if(IS_NULL_PTR(dev)) return;
1253
1254 // One reference per element, taken while the list cannot change under us. The copy is of
1255 // the list cells only: the items are shared, and the writer's copy-on-write gate is what
1256 // keeps them immutable for as long as this snapshot holds them.
1257 snapshot->items = g_list_copy(dev->history);
1258 for(GList *node = snapshot->items; node; node = g_list_next(node))
1260
1261 snapshot->history_end = dt_dev_get_history_end_ext(dev);
1262 snapshot->history_hash = dt_dev_get_history_hash(dev);
1263}
1264
1266{
1267 if(IS_NULL_PTR(snapshot)) return;
1269 snapshot->items = NULL;
1270 snapshot->history_end = 0;
1271 snapshot->history_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
1272}
1273
1275{
1276 if(IS_NULL_PTR(dev->history)) return;
1277 // Canonical history is being cleared (image unload, compress rebuild, ...):
1278 // mark every item deleted in the supervisor before freeing the structs.
1280 for(GList *l = dev->history; l; l = g_list_next(l))
1281 {
1282 const dt_dev_history_item_t *h = (const dt_dev_history_item_t *)l->data;
1283 if(h && h->module)
1284 dt_supervisor_history(DT_SV_DELETE, h->hash, h->module->op, h->module->multi_priority,
1285 h->module->multi_name, h->module->iop_order, h->num,
1286 dev->image_storage.id, h->enabled, h->module, h->params, h->blend_params, h->forms);
1287 }
1289 dev->history = NULL;
1290}
1291
1292gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
1293{
1294 // Recreate the whole history from scratch.
1295 // Backend only: GUI updates and pixelpipe rebuilds need to be triggered by callers.
1298 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
1302 return first_run;
1303}
1304
1305
1320{
1322 {
1323 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1325
1326 // Reset the enabled state to the module default too. Neither dt_iop_load_default_params()
1327 // nor dt_iop_reload_defaults() touch it, so without this a module keeps whatever enabled
1328 // state the previous history point left it in. The history overlay below (_history_to_module)
1329 // only re-enables modules that have an entry within the current slice; a module whose only
1330 // history entry lies *after* history_end would otherwise stay stale-enabled while parked at
1331 // INT_MAX, becoming an enabled ghost node after gamma that the pixelpipe format pass disables
1332 // for "unexpected input buffer format" (issue #961). This restores the reset that was dropped
1333 // when the reload path was refactored (commit 892dad90a9).
1334 module->enabled = module->default_enabled;
1335
1336 if(module->multi_priority == 0)
1337 module->iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, module->op, module->multi_priority);
1338 else
1339 module->iop_order = INT_MAX;
1340
1341 // Last chance & desperate attempt at enabling/disabling critical modules
1342 // when history is garbled - This might prevent segfaults on invalid data
1343 if(module->force_enable)
1344 module->enabled = (module->force_enable(module, module->enabled) != 0);
1345
1346 // make sure that always-on modules are always on. duh.
1347 if(module->default_enabled == 1 && module->hide_enable_button == 1)
1348 module->enabled = TRUE;
1349
1351 }
1352}
1353
1354// Dump the content of an history entry into its associated module params, blendops, etc.
1365{
1366 module->enabled = (hist->enabled != 0);
1367
1368 // Update IOP order stuff, that applies to all modules regardless of their internals
1369 module->iop_order = hist->iop_order;
1371
1372 // Copy instance name
1373 g_strlcpy(module->multi_name, hist->multi_name, sizeof(module->multi_name));
1374
1375 // Copy params from history entry to module internals
1376 memcpy(module->params, hist->params, module->params_size);
1378
1379 // Get the module hash. An item without its own forms snapshot must be hashed against the
1380 // snapshot accumulated up to its position in the replay (current_forms) — NOT against the
1381 // live dev->forms of the state being left: dt_iop_compute_blendop_hash()'s NULL fallback
1382 // reads the live list, and during replay that list is only replaced with the target
1383 // snapshot after this loop, so the restored hash would be tied to the wrong mask content.
1385}
1386
1387
1389{
1390 dt_print(DT_DEBUG_HISTORY, "[dt_dev_pop_history_items_ext] loading history entries into modules...\n");
1391
1392 // Ensure `dev->image_storage` is up-to-date before modules reload their defaults.
1393 // This avoids using incomplete RAW metadata (WB coeffs, matrices) on newly-inited images.
1394 dt_dev_ensure_image_storage(dev, dev->image_storage.id);
1395
1396 // Reset every module to its default params first; the history overlay below then overwrites the
1397 // ones that have entries. Per-image defaults were already computed at history init / instance
1398 // creation, so we do NOT recompute them here (no reload_defaults: GUI-unsafe, and it re-derived
1399 // cross-module defaults against a pipeline-populated proxy). See _dt_dev_modules_reload_defaults.
1401
1402 const int history_end = dt_dev_get_history_end_ext(dev);
1403
1404 // go through history up to history_end and set modules params
1405 GList *history = g_list_first(dev->history);
1406 GList *forms = NULL;
1407 for(int i = 0; i < history_end && history; i++)
1408 {
1409 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1410 dt_iop_module_t *module = hist->module;
1411
1412 // Update the reference to the form snapshot that doesn't belong
1413 // conceptually to history items. Advance it before applying the item so the
1414 // item's hash is computed against the masks state at its own position.
1415 if(hist->forms) forms = hist->forms;
1416
1417 if(module) _history_to_module(hist, module, forms);
1418
1419 history = g_list_next(history);
1420 }
1421
1422 // Nuke dev->forms and replace it with the last hist->forms in history.
1424
1425 // History (metadata + presets) is now fully applied to module params. Let modules publish any
1426 // dev->proxy state derived from their EFFECTIVE params (e.g. temperature's WB coeffs, consumed by
1427 // channelmixerrgb) on this main/history thread, BEFORE the pipeline resync below runs. dev->proxy
1428 // is a GUI/main-thread inter-module channel (pipelines must not touch it); this is the single
1429 // main-thread publish point for bulk history loads.
1431 {
1432 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1433 if(module->commit_proxy) module->commit_proxy(module);
1434 }
1435
1436 dt_ioppr_resync_pipeline(dev, 0, "dt_dev_pop_history_items_ext end", TRUE);
1437
1438 // Reloading defaults might have changed the global history hash
1440
1441 // Update darkroom sizes in case clipping & distortion changed.
1442 // This is now handled by the wrapper after releasing the history lock.
1443}
1444
1446{
1451 // Update darkroom sizes after releasing the history lock to avoid deadlocks.
1454}
1455
1456/* dt_dev_history_gui_update() lives in dev_history_gui.c: it attaches missing GTK
1457 * expanders and refreshes every module widget after a backend history reload. */
1458
1460{
1461 if(!dev->gui_attached) return;
1462
1463 // Every caller replaces the history wholesale -- undo, redo, a jump in the history list -- and
1464 // going back is the likely next step: keep the outputs this replaces.
1467
1468 if(rebuild)
1470 else
1472}
1473
1475{
1476 dt_develop_t *const dev = dt_dev_get_global();
1477 return !IS_NULL_PTR(dev) && g_list_find(imgs, GINT_TO_POINTER(dev->image_storage.id));
1478}
1479
1481{
1482 if(IS_NULL_PTR(dev)) return;
1483
1486 // Re-derive dev->roi.processed_{width,height} (and the natural scale) from the new
1487 // history, through the geometry service's size fold. Without this, a history change that alters the
1488 // final image geometry (e.g. removing a crop/ashift step) still renders at the old,
1489 // stale ROI -- same fix as _history_apply_history_end() in libs/history.c.
1493}
1494
1500static void _cleanup_history(const int32_t imgid)
1501{
1503}
1504
1506{
1507 // Count all the mask forms used x history entries, up to a certain threshold.
1508 // Stop counting when the threshold is reached, for performance.
1509 guint states = 0;
1510 for(GList *history = g_list_first(dev_history); history; history = g_list_next(history))
1511 {
1513 states += g_list_length(hist_item->forms);
1514 if(states > threshold) break;
1515 }
1516 return states;
1517}
1518
1519void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
1520{
1521 if(IS_NULL_PTR(dev) || imgid <= 0) return;
1522
1523 if(dev->gui_attached)
1524 {
1525 // Every caller reaches this from an unlocked context (the write job unlocks immediately
1526 // before calling; the GUI and IOP call sites never hold it), so the walk of dev->history
1527 // takes the read lock here rather than demanding it from callers. Released before
1528 // dt_image_history_changed() below, which does mipmap and thumbnail work far too
1529 // expensive to hold history_mutex across.
1531 const guint states = dt_dev_mask_history_overload(dev->history, 250);
1533 if(states > 250)
1534 dt_history_toast(_("Image #%i history is storing %d mask states. n"
1535 "Consider compressing history and removing unused masks to keep reads/writes manageable."),
1536 imgid, states);
1537 }
1538
1539 // Reload metadata, drop the stale mipmap and refresh the lighttable thumbnail.
1540 // refresh_filmstrip = FALSE: in darkroom the filmstrip is best-effort, spawning another export
1541 // thread would slow down the current one and we want resources allocated to the realtime main
1542 // preview, not diverted to cosmetic GUI refreshes.
1544}
1545
1546
1547// helper used to synch a single history item with db
1548int dt_dev_write_history_item(const int32_t imgid, dt_dev_history_item_t *h, int32_t num)
1549{
1550 if(IS_NULL_PTR(h)) return 1;
1551
1552 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",
1553 h->op_name, h->multi_name, h->enabled, h->iop_order, imgid);
1554
1555 const char *operation = h->module ? h->module->op : h->op_name;
1556 const int params_size = h->module ? h->module->params_size : h->params_size;
1557 const int module_version = h->module ? h->module->version() : h->module_version;
1558 const int blendop_params_size
1559 = h->blend_params ? (h->blendop_params_size > 0 ? h->blendop_params_size : (int)sizeof(dt_develop_blend_params_t)) : 0;
1560 const int blendop_version
1562
1563 dt_history_repository_write_item(imgid, num, operation, h->params, params_size, module_version, h->enabled != 0,
1564 h->blend_params, blendop_params_size, blendop_version, h->multi_priority, h->multi_name);
1565
1566 // write masks (if any)
1567 if(h->forms)
1568 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);
1569
1570 for(GList *forms = g_list_first(h->forms); forms; forms = g_list_next(forms))
1571 {
1572 dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
1573 if (form)
1574 dt_masks_write_masks_history_item(imgid, num, form);
1575 }
1576
1577 return 0;
1578}
1579
1581{
1582 // No-op: SQL statement caching/cleanup for history lives in common/history.c (dt_history_repository_cleanup()).
1583}
1584
1585
1586
1587/* Everything the DB rewrite needs, frozen at one moment under history_mutex, so the rewrite
1588 * itself can run with the lock RELEASED. A dt_dev_history_snapshot_t carries the items,
1589 * history_end and the hash; the iop-order list is the one other thing the rewrite reads from
1590 * dev, and it is deep-copied because it is a plain GList of entries with no refcount. */
1596
1598 REQUIRES_SHARED(dev->history_mutex)
1599{
1600 // Re-derive the hash from the list and publish it, as the rewrite always has: the atomic
1601 // is normally maintained by dt_dev_set_history_end_ext(), this is the belt to its braces.
1603 dt_dev_history_snapshot_take(dev, &state->snapshot);
1604 state->iop_order_list = dt_ioppr_iop_order_copy_deep(dev->iop_order_list);
1605}
1606
1608{
1610 g_list_free_full(state->iop_order_list, dt_free_gpointer);
1611 state->iop_order_list = NULL;
1612}
1613
1614/* The rewrite proper. Needs NO lock: every item is kept alive by the snapshot's references and
1615 * kept unmodified by the writer's copy-on-write gate, and the iop-order list is our own copy.
1616 * dt_dev_write_history_item() reads h->module->op/params_size/version() through the item's
1617 * module pointer, which is an iop instance that outlives every history item naming it. */
1618static void _write_history_from_state(const _history_write_state_t *state, const int32_t imgid)
1619{
1621 if(IS_NULL_PTR(cache_img)) return;
1622
1623 dt_print(DT_DEBUG_HISTORY, "[dt_dev_write_history_ext] writing history for image %i...\n", imgid);
1624
1625 _cleanup_history(imgid);
1626
1627 // write history entries
1628 int i = 0;
1629 for(GList *history = g_list_first(state->snapshot.items); history; history = g_list_next(history))
1630 {
1631 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1632 dt_dev_write_history_item(imgid, hist, i);
1633 i++;
1634 }
1635
1636 dt_history_repository_set_end(imgid, state->snapshot.history_end);
1637
1638 // write the iop-order-list this history was captured with
1639 dt_ioppr_write_iop_order_list(state->iop_order_list, imgid);
1640
1641 cache_img->history_hash = state->snapshot.history_hash;
1642
1644}
1645
1646/* Callers of this one already hold history_mutex (most as writers, mid-commit) and expect the
1647 * write to have happened when it returns, so the state is taken and written under their lock.
1648 * The two paths that took the lock ONLY to write -- the async job and the sync fallback in
1649 * dt_dev_write_history() -- do not come through here; they hold it just long enough to take the
1650 * state. */
1658
1659// Schedule history write as a background job to avoid blocking the GUI.
1660// If scheduling fails, fall back to synchronous write to preserve behaviour.
1662{
1664 if(IS_NULL_PTR(d)) return 1;
1665 const int32_t imgid = d->image_storage.id;
1666
1667 // This job used to hold history_mutex as reader for the whole rewrite -- every row of
1668 // history and masks_history deleted and re-inserted, tens to hundreds of ms -- and it runs
1669 // after every commit, so the GUI thread's NEXT commit (which needs the writer side) queued
1670 // behind it: the wait dt_dev_history_commit_item_now() logs as "blocked acquiring
1671 // history_mutex". Same disease as the pipe resync, same cure: freeze the state under the
1672 // lock, release, write from the frozen state.
1674 const double _lock_start = dt_get_wtime();
1675 dt_pthread_rwlock_rdlock(&d->history_mutex);
1677 // Clear the coalescing flag HERE, under the lock, at the moment the state is frozen -- not
1678 // after the write. dt_dev_write_history() skips queueing while this flag is set, on the
1679 // promise that the pending write will still pick the commit up. That promise now holds only
1680 // for commits landing BEFORE this point: anything committed after the unlock is not in our
1681 // snapshot, so it must find the flag clear and queue its own write. Clearing after the
1682 // rewrite would silently drop every commit made while the rewrite ran.
1683 dt_atomic_set_int(&d->history_write_pending, 0);
1684 dt_pthread_rwlock_unlock(&d->history_mutex);
1685 const double _lock_ms = (dt_get_wtime() - _lock_start) * 1000.0;
1686 if(_lock_ms > 1.0)
1688 "[_dt_dev_write_history_job_run] tid %lu held history_mutex for %.2f ms to snapshot image %i\n",
1689 (unsigned long)pthread_self(), _lock_ms, imgid);
1690
1691 const double _write_start = dt_get_wtime();
1693 const double _write_ms = (dt_get_wtime() - _write_start) * 1000.0;
1694 if(_write_ms > 1.0)
1696 "[_dt_dev_write_history_job_run] tid %lu full history+masks rewrite for image %i took %.2f ms, lock-free\n",
1697 (unsigned long)pthread_self(), imgid, _write_ms);
1699
1701 return 0;
1702}
1703
1704// Write TO XMP, so from the dev perspective, it's a read
1706{
1707 if(!async)
1708 {
1709 // Same shape as the job: the lock is for freezing the state, not for writing it.
1717 return;
1718 }
1719
1720 // Coalesce: a write already QUEUED for this dev has not frozen its state yet, so it will
1721 // pick up this commit when it runs. A write already RUNNING froze its state when it took
1722 // the lock and cleared this flag at that same moment (see _dt_dev_write_history_job_run),
1723 // so a commit landing after that finds the flag clear and queues its own write here. Either
1724 // way nothing is lost; queueing while the flag is set would only repeat a rewrite of state
1725 // that is already on its way to disk.
1726 if(dt_atomic_exch_int(&dev->history_write_pending, 1) != 0) return;
1727
1729 dev->image_storage.id);
1731
1733 {
1734 // scheduling failed: dispose job, clear the flag we just set, and run synchronously
1738 }
1739}
1740
1748static gboolean _dev_auto_apply_presets(dt_develop_t *dev, int32_t imgid)
1749{
1750 dt_image_t *image = &dev->image_storage;
1752 const char *workflow_preset = has_matrix ? _("scene-referred default") : "\t\n";
1753
1754 int iformat = 0;
1755 if(dt_image_needs_rawprepare(image))
1756 iformat |= FOR_RAW;
1757 else
1758 iformat |= FOR_LDR;
1759
1760 if(dt_image_is_hdr(image))
1761 iformat |= FOR_HDR;
1762
1763 int excluded = 0;
1764 if(dt_image_monochrome_flags(image))
1765 excluded |= FOR_NOT_MONO;
1766 else
1767 excluded |= FOR_NOT_COLOR;
1768
1769 int legacy_params = 0;
1770 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = TRUE };
1772
1773 // now we want to auto-apply the iop-order list if one corresponds and none are
1774 // still applied. Note that we can already have an iop-order list set when
1775 // copying an history or applying a style to a not yet developed image.
1776
1777 if(!dt_ioppr_has_iop_order_list(imgid))
1778 {
1779 void *params = NULL;
1780 int32_t params_len = 0;
1781 if(dt_history_repository_get_autoapply_ioporder_params(imgid, image, iformat, excluded, &params, &params_len))
1782 {
1783 GList *iop_list = dt_ioppr_deserialize_iop_order_list(params, params_len);
1784 dt_ioppr_write_iop_order_list(iop_list, imgid);
1786 iop_list = NULL;
1788 dt_free(params);
1789 }
1790 else
1791 {
1792 // No auto-applied order exists for this image. Persist the built-in order
1793 // matching the input class; the non-RAW order is named ANSEL_JPG but also
1794 // covers rendered formats such as TIFF/PNG/HDR.
1799 dt_ioppr_write_iop_order_list(iop_list, imgid);
1801 iop_list = NULL;
1803 }
1804 }
1805
1806 // Notify our private image copy that auto-presets got applied
1808
1809 return TRUE;
1810}
1811
1822static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited) REQUIRES(dev->history_mutex)
1823{
1824 // Module already in history: don't prepend extra entries.
1825 //
1826 // Two independent sources can already cover this module and both must be checked:
1827 // - dev->history in memory: dt_dev_replace_history_on_image() (image duplication, history
1828 // "replace" paste) loads dev->history from a *source* image, then points
1829 // dev->image_storage at a freshly created *destination* image whose DB history is still
1830 // empty (nothing written yet). The DB check alone always reports "missing" there, so every
1831 // default_enabled module (temperature, colorin, colorout, demosaic...) got a fresh
1832 // default-params entry silently appended after the correctly copied one, clobbering the
1833 // user's settings in the copy -- duplicating an image did not reproduce its development
1834 // identically.
1835 // - the DB row for dev->image_storage.id: dt_dev_read_history_ext() (normal darkroom/export
1836 // history load) calls this function on a freshly emptied dev->history, *before* the real
1837 // history rows are read from DB into memory. The in-memory check alone would always report
1838 // "missing" there too, causing every already-edited image to get spurious duplicate default
1839 // entries prepended ahead of its real history on every single load.
1841 || dt_history_repository_module_exists(dev->image_storage.id, module->op))
1842 return;
1843
1844 // Module has no user params: no history: don't prepend either
1845 if((module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1846 && (module->default_enabled || (module->force_enable && module->force_enable(module, FALSE))))
1847 {
1848 module->enabled = TRUE;
1849 return;
1850 }
1851
1852 dt_image_t *image = &dev->image_storage;
1853
1854 // Prior to Darktable 3.0, modules enabled by default which still had
1855 // default params (no user change) were not inserted into history/DB.
1856 // We need to insert them here with default params.
1857 // But defaults have changed since then for some modules, so we need to ensure
1858 // we insert them with OLD defaults.
1859 if(module->default_enabled || (module->force_enable && module->force_enable(module, FALSE)))
1860 {
1861 module->enabled = TRUE;
1863 const gboolean is_raw = dt_image_is_raw(image);
1864
1865 if(!strcmp(module->op, "temperature")
1866 && (image->change_timestamp == -1) // change_timestamp is not defined for old pics
1867 && is_raw && is_inited && has_matrix)
1868 {
1869 dt_print(DT_DEBUG_HISTORY, "[history] Image history seems older than Darktable 3.0, we will insert white balance.\n");
1870
1871 // Temp revert to legacy defaults
1872 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "legacy");
1874
1876
1877 // Go back to current defaults
1878 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1880 }
1881 else
1882 {
1884 }
1885 }
1886 else if(module->workflow_enabled && !is_inited)
1887 {
1888 module->enabled = TRUE;
1890 }
1891
1892 if(module->enabled)
1893 dt_print(DT_DEBUG_HISTORY, "[history] %s was inserted into history by default (enabled %i)\n", module->op, module->enabled);
1894}
1895
1896// Returns TRUE if this is a freshly-inited history on which we just applied auto presets and defaults,
1897// FALSE if we had an earlier history
1898gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets)
1899 REQUIRES(dev->history_mutex)
1900{
1901 const gboolean is_inited = (dev->image_storage.flags & DT_IMAGE_AUTO_PRESETS_APPLIED);
1902
1903 // Make sure this is set
1904 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1905
1906 // make sure all modules default params are loaded to init history
1907 for(GList *iop = g_list_first(dev->iop); iop; iop = g_list_next(iop))
1908 {
1909 dt_iop_module_t *module = (dt_iop_module_t *)(iop->data);
1912 }
1913
1914 // On virgin history image, apply auto stuff (ours and user's)
1916 dt_print(DT_DEBUG_HISTORY, "[history] temporary history initialised with default params and presets\n");
1917
1918 return !is_inited;
1919}
1920
1922 const gboolean reload_defaults, const char *msg)
1923{
1924 if(dest_imgid <= 0) return 1;
1925
1927
1928 // Same shape as dt_dev_reload_history_items(): the whole rebuild runs under the write lock,
1929 // released before dt_dev_write_history(), which takes the read lock itself.
1931
1932 if(reload_defaults)
1933 {
1936 }
1937
1940
1941 dt_dev_write_history(dev_src, FALSE);
1942
1943 return 0;
1944}
1945
1946// populate hist->module
1956{
1957 dt_iop_module_t *match = NULL;
1958
1960 {
1961 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1962 if(!strcmp(module->op, hist->op_name))
1963 {
1964 if(module->multi_priority == hist->multi_priority)
1965 {
1966 // Found exact match at required priority: we are done
1967 hist->module = module;
1968 break;
1969 }
1970 else if(hist->multi_priority > 0)
1971 {
1972 // Found the right kind of module but the wrong instance.
1973 // Current history entry is targeting an instance that may exist later in the pipe, so keep looping/looking.
1974 match = module;
1975 }
1976 }
1977 }
1978
1979 if(!hist->module && match)
1980 {
1981 // We found a module having the required name but not the required instance number:
1982 // add a new instance of this module by using its ->so property
1984 if(!dt_iop_load_module(new_module, match->so, dev))
1985 {
1986 dev->iop = g_list_append(dev->iop, new_module);
1987 // Just init, it will get rewritten later by resync IOP order methods:
1988 new_module->instance = match->instance;
1989 hist->module = new_module;
1990 }
1991 }
1992 // else we found an already-existing instance and it's in hist->module already
1993
1994 if(hist->module) hist->module->enabled = (hist->enabled != 0);
1995}
1996
1997
2009static void _sync_blendop_params(dt_dev_history_item_t *hist, const void *blendop_params, const int bl_length,
2010 const int blendop_version, int *legacy_params)
2011{
2012 const gboolean is_valid_blendop_version = (blendop_version == dt_develop_blend_version());
2013 const gboolean is_valid_blendop_size = (bl_length == sizeof(dt_develop_blend_params_t));
2014
2018
2020 {
2021 memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
2022 }
2023 else if(blendop_params
2024 && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params,
2026 == 0)
2027 {
2029 }
2030 else
2031 {
2032 memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
2033 }
2034}
2035
2049static int _sync_params(dt_dev_history_item_t *hist, const void *module_params, const int param_length,
2050 const int modversion, int *legacy_params, const char *preset_name)
2051{
2052 hist->params_size = hist->module->params_size;
2053 hist->module_version = hist->module->version();
2054 const gboolean is_valid_module_version = (modversion == hist->module->version());
2055 const gboolean is_valid_params_size = (param_length == hist->module->params_size);
2056
2057 hist->params = malloc(hist->module->params_size);
2059 {
2060 memcpy(hist->params, module_params, hist->module->params_size);
2061 }
2062 else
2063 {
2064 if(!hist->module->legacy_params
2065 || hist->module->legacy_params(hist->module, module_params, labs(modversion),
2066 hist->params, labs(hist->module->version())))
2067 {
2068 gchar *preset = (preset_name) ? g_strdup_printf(_("from preset %s"), preset_name)
2069 : g_strdup("");
2070
2071 fprintf(stderr, "[dev_read_history] module `%s' %s version mismatch: history is %d, dt %d.\n", hist->module->op,
2072 preset, modversion, hist->module->version());
2073
2074 dt_history_message(_("module `%s' %s version mismatch: %d != %d"), hist->module->op,
2075 preset, hist->module->version(), modversion);
2076
2077 dt_free(preset);
2078 return 1;
2079 }
2080 else
2081 {
2082 // NOTE: spots version was bumped from 1 to 2 in 2013.
2083 // This handles edits made prior to Darktable 1.4.
2084 // Then spots was deprecated in 2021 in favour of retouch.
2085 // How many edits out there still need the legacy conversion in 2025 ?
2086 if(!strcmp(hist->module->op, "spots") && modversion == 1)
2087 {
2088 // quick and dirty hack to handle spot removal legacy_params
2089 memcpy(hist->blend_params, hist->module->blend_params, sizeof(dt_develop_blend_params_t));
2090 }
2092 }
2093
2094 /*
2095 * Fix for flip iop: previously it was not always needed, but it might be
2096 * in history stack as "orientation (off)", but now we always want it
2097 * by default, so if it is disabled, enable it, and replace params with
2098 * default_params. if user want to, he can disable it.
2099 * NOTE: Flip version was bumped from 1 to 2 in 2014.
2100 * This handles edits made prior to Darktable 1.6.
2101 * How many edits out there still need the legacy conversion in 2025 ?
2102 */
2103 if(!strcmp(hist->module->op, "flip") && hist->enabled == 0 && labs(modversion) == 1)
2104 {
2105 memcpy(hist->params, hist->module->default_params, hist->module->params_size);
2106 hist->enabled = TRUE;
2107 }
2108 }
2109
2110 return 0;
2111}
2112
2113// WARNING: this does not set hist->forms
2137static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
2138 const int modversion, const char *operation, const void *module_params,
2139 const int param_length, const gboolean enabled, const void *blendop_params,
2140 const int bl_length, const int blendop_version, const int multi_priority,
2141 const char *multi_name, const char *preset_name, int *legacy_params,
2142 const gboolean presets) REQUIRES(dev->history_mutex)
2143{
2144 // Sanity checks
2145 const gboolean is_valid_id = (id == imgid);
2146 const gboolean has_operation = (!IS_NULL_PTR(operation));
2147
2148 if(!(has_operation && is_valid_id))
2149 {
2150 fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n",
2151 dev->image_storage.filename);
2152 return;
2153 }
2154
2160 if(!dt_iop_get_module_from_list(dev->iop, operation)) return;
2161
2162 int iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, operation, multi_priority);
2163
2164 // Init a bare minimal history entry
2166 hist->module = NULL;
2167 hist->num = num;
2168 hist->iop_order = iop_order;
2169 hist->multi_priority = multi_priority;
2170 hist->enabled = (enabled != 0); // cast to gboolean
2171 g_strlcpy(hist->op_name, operation, sizeof(hist->op_name));
2172 g_strlcpy(hist->multi_name, multi_name ? multi_name : "", sizeof(hist->multi_name));
2173
2174 // Find a .so file that matches our history entry, aka a module to run the params stored in DB
2175 _find_so_for_history_entry(dev, hist);
2176
2177 if(!hist->module)
2178 {
2179 // Keep the serialized history entry even though no live module can consume it.
2180 hist->params_size = MAX(param_length, 0);
2181 hist->module_version = modversion;
2182 hist->blendop_params_size = MAX(bl_length, 0);
2183 hist->blendop_version = blendop_version;
2185 {
2186 hist->params = malloc(param_length);
2188 }
2189 if(!IS_NULL_PTR(blendop_params) && bl_length > 0)
2190 {
2191 hist->blend_params = malloc(bl_length);
2192 memcpy(hist->blend_params, blendop_params, bl_length);
2193 }
2194
2195 fprintf(
2196 stderr,
2197 "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n",
2198 operation, dev->image_storage.filename);
2199 dev->history = g_list_append(dev->history, hist);
2200 return;
2201 }
2202
2203 // Update IOP order stuff, that applies to all modules regardless of their internals
2204 // Needed now to de-entangle multi-instances
2205 hist->module->iop_order = hist->iop_order;
2206 dt_iop_update_multi_priority(hist->module, hist->multi_priority);
2207
2208 // When the stored module_order uses a non-CUSTOM built-in (one entry per module),
2209 // extra instances (multi_priority > 0) are absent from iop_order_list and get
2210 // iop_order = INT_MAX above. Insert a placeholder so the module gets a valid
2211 // sequential position instead of drifting through the pipeline at INT_MAX.
2212 if(iop_order == INT_MAX && hist->multi_priority > 0)
2213 {
2215 g_strlcpy(new_entry->operation, operation, sizeof(new_entry->operation));
2216 g_strlcpy(new_entry->name, hist->multi_name, sizeof(new_entry->name));
2217 new_entry->instance = hist->multi_priority;
2218 new_entry->o.iop_order = 0;
2219
2220 // Insert after the last existing entry for this operation so extra instances
2221 // sit after the base, matching the order from dt_dev_module_duplicate.
2222 GList *place = NULL;
2223 for(GList *l = dev->iop_order_list; l; l = g_list_next(l))
2224 {
2225 const dt_iop_order_entry_t *e = (dt_iop_order_entry_t *)l->data;
2226 if(!strcmp(e->operation, operation)) place = l;
2227 }
2228 dev->iop_order_list = g_list_insert_before(dev->iop_order_list,
2230
2231 iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, operation, hist->multi_priority);
2232 hist->iop_order = iop_order;
2233 hist->module->iop_order = iop_order;
2234 }
2235
2236 // module has no user params and won't bother us in GUI - exit early, we are done
2237 if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
2238 {
2239 // Since it's the last we hear from this module as far as history is concerned,
2240 // compute its hash here.
2241 dt_iop_compute_module_hash(hist->module, NULL);
2242
2243 // Done. We don't add to history
2244 dt_free(hist);
2245 return;
2246 }
2247
2248 // Copy module params if valid version, else try to convert legacy params
2249 if(_sync_params(hist, module_params, param_length, modversion, legacy_params, preset_name))
2250 {
2251 dt_free(hist);
2252 return;
2253 }
2254
2255 // So far, on error we haven't allocated any buffer, so we just freed the hist structure
2256
2257 // Last chance & desperate attempt at enabling/disabling critical modules
2258 // when history is garbled - This might prevent segfaults on invalid data
2259 if(hist->module->force_enable)
2260 hist->enabled = (hist->module->force_enable(hist->module, hist->enabled) != 0);
2261
2262 // make sure that always-on modules are always on. duh.
2263 if(hist->module->default_enabled == TRUE && hist->module->hide_enable_button == TRUE)
2264 hist->enabled = TRUE;
2265
2266 // Copy blending params if valid, else try to convert legacy params
2267 _sync_blendop_params(hist, blendop_params, bl_length, blendop_version, legacy_params);
2268
2269 if(presets && !IS_NULL_PTR(hist->blend_params) && !IS_NULL_PTR(hist->module)
2270 && !IS_NULL_PTR(hist->module->default_blendop_params))
2271 {
2273 dt_develop_blend_params_t default_blend = *hist->module->default_blendop_params;
2278 }
2279
2280 dev->history = g_list_append(dev->history, hist);
2281
2282 dt_print(DT_DEBUG_HISTORY, "[history entry] read %s at pipe position %i (enabled %i) from %s %s\n", hist->op_name,
2283 hist->iop_order, hist->enabled, (presets) ? "preset" : "database", (presets) ? preset_name : "");
2284}
2285
2286
2287gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid) REQUIRES(dev->history_mutex)
2288{
2289 if(imgid == UNKNOWN_IMAGE) return FALSE;
2290
2291 // This should be inited already when creating a pipeline or entering darkroom
2292 // but some pathes don't handle it, so make sure we have modules loaded.
2293 if(IS_NULL_PTR(dev->iop))
2294 dev->iop = dt_dev_load_modules(dev);
2295
2296 // Ensure raw metadata (WB coeffs, matrices, etc.) is available for modules that
2297 // query it while (re)loading defaults (e.g. temperature/colorin).
2298 // This is redundant with `_dt_dev_load_raw()` called from `dt_dev_load_image()`,
2299 // but some call sites reload history without guaranteeing a prior FULL open.
2300 if(dt_dev_ensure_image_storage(dev, imgid))
2301 return FALSE;
2302
2303 // Start fresh
2305 int legacy_params = 0;
2307
2308 // Find the new history end from DB now, if defined.
2309 // Note: dt_dev_set_history_end_ext sanitizes the value with the actual history size.
2310 // It needs to run after dev->history is fully populated.
2311 int32_t history_end = dt_history_repository_get_end(imgid);
2312
2313 // Find out if we already have an history, and how many items
2314 const int32_t db_items = dt_history_repository_get_next_num(imgid);
2315
2316 // Load all the modules that may be required by the image format,
2317 // plus auto-presets if it's a new edit
2318 gboolean first_run = dt_dev_init_default_history(dev, imgid, (db_items == 0));
2319
2320 // Protect history DB reads with a cache read lock.
2321 // Release it before applying history to modules to avoid deadlocks.
2323 if(IS_NULL_PTR(read_lock_img)) return FALSE;
2324
2325 // Load DB history into dev->history
2326 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = FALSE };
2328 const int32_t history_length = g_list_length(dev->history);
2329
2331 {
2332 // We have now more history entries than when reading DB,
2333 // meaning the sanitization step added required default modules.
2334 // We need to shift history end by the same amount to still point to the same entry.
2335 // This is valid whether history_end was 0 or not
2336 history_end += history_length - db_items;
2337 }
2338
2339 // Set a provisional history_end so mask history can resolve current forms.
2340 // We will recompute hashes once all history items are committed below.
2341 dt_dev_set_history_end_ext(dev, history_end);
2342
2343 // Sanitize and flatten module order
2344 dt_ioppr_resync_pipeline(dev, imgid, "dt_dev_read_history_no_image end", FALSE);
2345
2346 // After resync, iop_order_list was renumbered with correct sequential values.
2347 // Fix hist->iop_order for entries recovered from a missing order entry: they
2348 // got a placeholder value of 0 above; sync them from the now-correct module order.
2349 for(GList *h = dev->history; h; h = g_list_next(h))
2350 {
2352 if(hist->module && hist->iop_order < 1 && hist->module->iop_order > 0)
2353 hist->iop_order = hist->module->iop_order;
2354 }
2355
2356 // Update "masks history"
2357 // This design is stupid because `dt_dev_history_item_t *hist->forms` is not read
2358 // from the DB history items (`main.history`), but from the `main.masks_history`,
2359 // and later on, `dev->forms` is restored at the `history_end` index from dumping
2360 // the content of the `dt_dev_history_item_t *hist->forms` snapshot.
2361 // This only means that `hist->forms` doesn't belong to the `dt_dev_history_item_t *`
2362 // but should live in its own branch. See `dt_dev_pop_history_items_ext()`
2363 dt_masks_read_masks_history(dev, imgid);
2364
2367
2368 // Now we have fully-populated history items:
2369 // Commit params to modules and publish the masks on the raster stack for other modules to find
2371 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
2372 {
2373 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
2374 if(!hist)
2375 {
2376 fprintf(stderr, "[dt_dev_read_history_ext] we have no history item. This is not normal.\n");
2377 continue;
2378 }
2379 else if(!hist->module)
2380 {
2381 dt_print(DT_DEBUG_HISTORY, "[history] keeping archival history item %s (%s) without live module binding\n",
2382 hist->op_name, hist->multi_name);
2383 continue;
2384 }
2385
2386 // Same accumulation rule as dt_dev_pop_history_items_ext(): an item without its own forms
2387 // snapshot is hashed against the last snapshot recorded at or before its position.
2388 if(hist->forms) accumulated_forms = hist->forms;
2389
2390 dt_iop_module_t *module = hist->module;
2392 hist->hash = hist->module->hash;
2393
2394 // Register the freshly-read history item now (its hash is final), so the
2395 // node<->history resynchronization can resolve `params` to a real entry.
2397 dt_supervisor_history(DT_SV_CREATE, hist->hash, module->op, module->multi_priority,
2398 module->multi_name, module->iop_order, g_list_index(dev->history, hist),
2399 imgid, hist->enabled, module, hist->params, hist->blend_params, hist->forms);
2400
2401 dt_print(DT_DEBUG_HISTORY, "[history] successfully loaded module %s history (enabled: %i)\n", hist->module->op, hist->enabled);
2402 }
2403
2406 // The call above seeds dev->forms_hash from a freshly-zeroed dev, which always compares
2407 // as "changed" the first time -- that's establishing the baseline against what was just
2408 // loaded from disk, not a pending edit. Clear it so the first mask interaction (even a
2409 // no-op selection click) doesn't trigger a spurious history commit + full DB rewrite.
2410 dev->forms_changed = FALSE;
2411
2412 dt_dev_set_history_end_ext(dev, history_end);
2413 // Note: dt_dev_set_history_end already updates dev->history_hash
2414
2415 dt_print(DT_DEBUG_HISTORY, "[history] dt_dev_read_history_ext completed\n");
2416 return first_run;
2417}
2418
2419
2421{
2422 for(; list; list = g_list_next(list))
2423 {
2425 if (hitem->module == module)
2426 {
2427 hitem->module = NULL;
2428 }
2429 }
2430}
2431
2436
2447
2455{
2456 return module->default_enabled || (module->force_enable && module->force_enable(module, module->enabled));
2457}
2458
2466{
2467 return module->has_defaults ? module->has_defaults(module) : TRUE;
2468}
2469
2477{
2478 if(!module->blend_params || !module->default_blendop_params) return TRUE;
2479
2480 return memcmp(module->blend_params, module->default_blendop_params,
2481 sizeof(dt_develop_blend_params_t)) == 0;
2482}
2483
2494
2496
2503static void _dev_history_add_filtered(dt_develop_t *dev, dt_iop_module_filter_t filter) REQUIRES(dev->history_mutex)
2504{
2505 for(GList *item = g_list_first(dev->iop); item; item = g_list_next(item))
2506 {
2507 dt_iop_module_t *module = (dt_iop_module_t *)(item->data);
2508 if(!_module_leaves_no_history(module) && filter(module))
2510 }
2511}
2512
2517{
2518 return module->enabled && _module_is_default_or_forced_enabled(module);
2519}
2520
2525{
2526 return module->enabled && !_module_is_default_or_forced_enabled(module)
2527 && _module_params_are_default(module);
2528}
2529
2534{
2535 return module->enabled && !_module_is_default_or_forced_enabled(module)
2536 && !_module_params_are_default(module);
2537}
2538
2543{
2544 return !module->enabled
2545 && (module->default_enabled || module->workflow_enabled || _module_has_nondefault_internal_params(module));
2546}
2547
2557{
2558 // Rebuild the history list under lock, but run expensive cross-subsystem
2559 // operations (history->modules sync, optional DB write) after releasing it.
2560 // This keeps history_mutex hold time short and avoids lock-order contention
2561 // with GUI/pipeline users touching masks/pixelpipe.
2563
2564 // Cleanup old history
2566
2567 // Rebuild an history from current pipeline.
2568 // First: modules enabled by default or forced enabled for technical reasons
2570
2571 // Second: modules enabled by user
2572 // 2.1 : start with modules that still have default params,
2574
2575 // 2.2 : then modules that are set to non-default
2577
2578 // Third: disabled modules that have history because they were enabled by default or
2579 // because their internal params (including blendops) differ from defaults. Maybe users
2580 // want to re-enable them later, or it's modules enabled by default that were manually disabled.
2581 // Put them the end of the history, so user can truncate it after the last enabled item
2582 // to get rid of disabled history if needed.
2584
2587
2594 if(write_history)
2595 {
2599 }
2600}
2601
2606
2611
2612void dt_dev_history_truncate(dt_develop_t *dev, const int32_t imgid)
2613{
2615
2616 // Remove tail entries (num >= end).
2617 // history_end is a cursor expressed in "number of applied items" terms:
2618 // - keep items [0..history_end-1]
2619 // - remove items [history_end..]
2621 while(link)
2622 {
2623 GList *next = g_list_next(link);
2624 const dt_dev_history_item_t *h = (const dt_dev_history_item_t *)link->data;
2625 if(dt_supervisor_active() && h && h->module)
2626 dt_supervisor_history(DT_SV_DELETE, h->hash, h->module->op, h->module->multi_priority,
2627 h->module->multi_name, h->module->iop_order, h->num,
2628 dev->image_storage.id, h->enabled, h->module, h->params, h->blend_params, h->forms);
2629 dt_dev_free_history_item(link->data);
2630 dev->history = g_list_delete_link(dev->history, link);
2631 link = next;
2632 }
2633
2635
2636 // Re-apply history and resync iop order from the truncated stack.
2644 dt_dev_write_history_ext(dev, imgid);
2646}
2647
2649{
2650 if(dt_dev_get_history_end_ext(dev) == g_list_length(dev->history))
2652 else
2653 dt_dev_history_truncate(dev, dev->image_storage.id);
2654}
2655
2656
2667{
2668 GList *iop_list = *_iop_list;
2669 int deleted_module_found = 0;
2670
2671 // we will check on dev->iop if there's a module that is not in history
2672 GList *modules = iop_list;
2673 while(modules)
2674 {
2676 if(IS_NULL_PTR(mod)) continue;
2677
2678 int delete_module = 0;
2679
2680 // base modules are a special case
2681 // most base modules won't be in history and must not be deleted
2682 // but the user may have deleted a base instance of a multi-instance module
2683 // and then undo and redo, so we will end up with two entries in dev->iop
2684 // with multi_priority == 0, this can't happen and the extra one must be deleted
2685 // dev->iop is sorted by (priority, multi_priority DESC), so if the next one is
2686 // a base instance too, one must be deleted
2687 if(mod->multi_priority == 0)
2688 {
2690 if(modules_next)
2691 {
2693 if(strcmp(mod_next->op, mod->op) == 0 && mod_next->multi_priority == 0)
2694 {
2695 // is the same one, check which one must be deleted
2698
2699 // current is in history and next is not, delete next
2701 {
2702 mod = mod_next;
2704 delete_module = 1;
2705 }
2706 // current is not in history and next is, delete current
2708 {
2709 delete_module = 1;
2710 }
2711 else
2712 {
2714 fprintf(
2715 stderr,
2716 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) both in history\n",
2717 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2718 mod_next->multi_priority);
2719 else
2720 fprintf(
2721 stderr,
2722 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) none in history\n",
2723 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2724 mod_next->multi_priority);
2725 }
2726 }
2727 }
2728 }
2729 // this is a regular multi-instance and must be in history
2730 else
2731 {
2733 }
2734
2735 // if module is not in history we delete it
2736 if(delete_module && mod)
2737 {
2739
2740 iop_list = g_list_delete_link(iop_list, modules);
2741
2742 // remove the module reference from all snapshots
2744
2745 // don't delete the module, a pipe may still need it
2746 dev->alliop = g_list_append(dev->alliop, mod);
2747
2748 // Its widgets used to be destroyed RIGHT HERE, under history_mutex as writer --
2749 // GTK teardown inside the engine lock. The caller collects the removed instances
2750 // and destroys their widgets after the lock is released; the module is already
2751 // unlinked from the iop list and parked in alliop, so nothing re-touches it in
2752 // the meantime.
2755
2756 // and reset the list
2757 modules = iop_list;
2758 continue;
2759 }
2760
2762 }
2763 if(deleted_module_found) iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2764
2765 *_iop_list = iop_list;
2766
2767 return deleted_module_found;
2768}
2769
2777{
2778 int changed = 0;
2779 for(const GList *history = history_list; history; history = g_list_next(history))
2780 {
2782
2783 // if multi_priority is different in history and dev->iop
2784 // we keep the history version
2785 if(hitem->module && hitem->module->multi_priority != hitem->multi_priority)
2786 {
2787 dt_iop_update_multi_priority(hitem->module, hitem->multi_priority);
2788 changed = 1;
2789 }
2790 }
2791 return changed;
2792}
2793
2801static void _reset_module_instance(GList *hist, dt_iop_module_t *module, int multi_priority)
2802{
2803 for(; hist; hist = g_list_next(hist))
2804 {
2806
2807 if(IS_NULL_PTR(hit->module) && strcmp(hit->op_name, module->op) == 0 && hit->multi_priority == multi_priority)
2808 {
2809 hit->module = module;
2810 }
2811 }
2812}
2813
2821static void _undo_items_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data)
2822{
2823 struct _cb_data *udata = (struct _cb_data *)user_data;
2825 _reset_module_instance(hdata->after_snapshot, udata->module, udata->multi_priority);
2826}
2827
2839{
2840 GList *iop_list = *_iop_list;
2841 int changed = 0;
2842 gboolean done = FALSE;
2843
2844 GList *l = history_list;
2845 while(l)
2846 {
2847 GList *next = g_list_next(l);
2849
2850 // this fixes the duplicate module when undo: hitem->multi_priority = 0;
2851 if(IS_NULL_PTR(hitem->module))
2852 {
2853 changed = 1;
2854
2855 const dt_iop_module_t *base_module = dt_iop_get_module_from_list(iop_list, hitem->op_name);
2857 {
2858 fprintf(stderr, "[_create_deleted_modules] can't find base module for %s\n", hitem->op_name);
2859 return changed;
2860 }
2861
2862 // from there we create a new module for this base instance. The goal is to do a very minimal setup of the
2863 // new module to be able to write the history items. From there we reload the whole history back and this
2864 // will recreate the proper module instances.
2865 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
2867 {
2868 return changed;
2869 }
2870 module->instance = base_module->instance;
2871
2872 // adjust the multi_name of the new module
2873 g_strlcpy(module->multi_name, hitem->multi_name, sizeof(module->multi_name));
2874 dt_iop_update_multi_priority(module, hitem->multi_priority);
2875 module->iop_order = hitem->iop_order;
2876
2877 // we insert this module into dev->iop
2878 iop_list = g_list_insert_sorted(iop_list, module, dt_sort_iop_by_order);
2879
2880 // if not already done, set the module to all others same instance
2881 if(!done)
2882 {
2884
2885 // and do that also in the undo/redo lists
2886 struct _cb_data udata = { module, hitem->multi_priority };
2888 done = TRUE;
2889 }
2890
2891 hitem->module = module;
2892 }
2893 l = next;
2894 }
2895
2896 *_iop_list = iop_list;
2897
2898 return changed;
2899}
2900
2901
2902// returns 1 if the topology of the pipe has changed, aka it needs a full rebuild
2903// 0 means only internal parameters of pipe nodes have change, so it's a mere resync
2906{
2907 GList *iop_list = *iop;
2908
2909 // topology has changed?
2910 int pipe_remove = 0;
2911
2912 // we have to check if multi_priority has changed since history was saved
2913 // we will adjust it here
2914 if(_rebuild_multi_priority(history))
2915 {
2916 pipe_remove = 1;
2917 iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2918 }
2919
2920 // check if this undo a delete module and re-create it
2921 if(_create_deleted_modules(&iop_list, history))
2922 pipe_remove = 1;
2923
2924 // check if this is a redo of a delete module or an undo of an add module
2925 if(_check_deleted_instances(dev, &iop_list, history, removed_gui_modules))
2926 pipe_remove = 1;
2927
2928 *iop = iop_list;
2929
2930 // if topology has changed, we need to reorder modules in GUI
2932
2933 return pipe_remove;
2934}
2935
2936
2937/* ---------------------------------------------------------------------------------------------------
2938 * Out-of-history transient param channel (see dev_history.h).
2939 *
2940 * Thread-safety: the slot is guarded by `dev->transient_params_mutex`. Writers (GUI/worker thread)
2941 * `_set`/`_clear`; the pipeline reader (`_get`) copies out under the same lock and never dereferences
2942 * the publishing module. The published payload is a plain byte copy of the module params struct, so the
2943 * pipeline never touches GUI-owned `module->params`.
2944 * ------------------------------------------------------------------------------------------------- */
2945
2947 const void *blend_params, const size_t blend_size)
2948{
2949 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev) || IS_NULL_PTR(params) || params_size == 0) return;
2950 dt_develop_t *dev = module->dev;
2951
2953
2954 if(dev->transient_params.params_size != (int32_t)params_size)
2955 {
2959 }
2962
2963 if(!IS_NULL_PTR(blend_params) && blend_size > 0)
2964 {
2965 if(dev->transient_params.blend_size != (int32_t)blend_size)
2966 {
2968 dev->transient_params.blend_params = g_malloc0(blend_size);
2969 dev->transient_params.blend_size = (int32_t)blend_size;
2970 }
2972 memcpy(dev->transient_params.blend_params, blend_params, blend_size);
2973 }
2974 else
2975 {
2978 }
2979
2980 dev->transient_params.module = module;
2981 dev->transient_params.serial++;
2982
2984
2985 /* Intentionally does NOT trigger a pipe recompute. The caller flags the pipe the way that fits its
2986 * use case: a realtime module (drawlayer) raises DT_DEV_PIPE_TOP_CHANGED on the main pipe for a fast
2987 * focused-piece resync, while a geometry-changing edit (crop/ashift) drives a full
2988 * dt_dev_pixelpipe_resync_history_all() so every pipe (main, preview, virtual) replans ROI/formats.
2989 * Auto-triggering here too would route those edits through the partial focused-piece resync as well
2990 * and race the full one (garbled geometry on warm re-edits). */
2991}
2992
2994{
2995 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev)) return;
2996 dt_develop_t *dev = module->dev;
2997
2999 if(dev->transient_params.module == module)
3000 {
3005 dev->transient_params.module = NULL;
3006 dev->transient_params.serial++;
3007 }
3009
3010 /* As with _set, the caller is responsible for re-triggering the pipe (resync / history commit) so it
3011 * re-commits the focused module's piece from history instead of the dropped transient snapshot. */
3012}
3013
3015 void *out_params, const size_t out_params_size,
3016 void *out_blend, const size_t out_blend_size, gboolean *out_has_blend,
3018{
3022
3023 gboolean ok = FALSE;
3025 if(dev->transient_params.module == module && !IS_NULL_PTR(dev->transient_params.params)
3026 && dev->transient_params.params_size == (int32_t)out_params_size)
3027 {
3029 /* Fetched under the SAME lock as the blob it describes. Read separately, a publish landing
3030 * in between would pair a serial with params it does not describe -- and since the serial is
3031 * what advances the piece's cache identity, that pairing is a silently missed render. */
3033 ok = TRUE;
3034
3036 && dev->transient_params.blend_size == (int32_t)out_blend_size)
3037 {
3040 }
3041 }
3043 return ok;
3044}
3045
3047{
3048 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module)) return FALSE;
3050 const gboolean active = (dev->transient_params.module == module);
3052 return active;
3053}
void reload_defaults(dt_iop_module_t *module)
Definition ashift.c:5828
#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)
static void * dt_atomic_exch_ptr(dt_atomic_ptr *var, void *value)
Definition atomic.h:80
static void * dt_atomic_get_ptr(const dt_atomic_ptr *var)
Definition atomic.h:76
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:571
#define m
Definition basecurve.c:283
int dt_develop_blend_version(void)
Definition blend.c:1713
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:1777
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
const float threshold
void dt_conf_set_string(const char *name, const char *val)
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)
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:651
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
static void _remove_history_leaks(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
Remove history items past history_end when allowed.
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.
void dt_dev_history_set_commit_gui_handler(dt_dev_history_commit_gui_handler_t handler)
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, uint64_t *out_serial)
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_compress_or_truncate(dt_develop_t *dev) REQUIRES_SHARED(dev -> history_mutex)
Compress history if history_end is at top, otherwise truncate.
static void _history_to_module(const dt_dev_history_item_t *const hist, dt_iop_module_t *module, GList *current_forms)
Apply a history item to a module instance.
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.
static int _check_deleted_instances(dt_develop_t *dev, GList **_iop_list, GList *history_list, GList **removed_gui_modules)
Detect and handle module instances that exist in iop list but not in history.
static void _dev_history_add_filtered(dt_develop_t *dev, dt_iop_module_filter_t filter) REQUIRES(dev -> history_mutex)
Append history items for modules that pass a filter.
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.
void dt_dev_pop_history_items_ext(dt_develop_t *dev) REQUIRES_SHARED(dev -> history_mutex)
Apply history items to module params up to dev->history_end.
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).
void dt_dev_history_snapshot_release(dt_dev_history_snapshot_t *snapshot)
Release every reference a snapshot holds and reset it. Needs no lock.
static dt_dev_history_item_t * _search_history_by_op(dt_develop_t *dev, const dt_iop_module_t *module) REQUIRES_SHARED(dev -> history_mutex)
Find the first history item matching a module operation name.
void dt_dev_history_set_undo_restore_gui_handler(dt_dev_history_undo_restore_gui_handler_t handler)
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.
static gboolean _module_params_are_default(dt_iop_module_t *module)
Check if module params match defaults.
gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid) REQUIRES(dev -> history_mutex)
Read history and masks from DB and populate dev->history.
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 void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs) NO_THREAD_SAFETY_ANALYSIS
Apply an undo/redo history snapshot to a develop context.
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) REQUIRES_SHARED(dev_dest -> history_mutex)
Resolve or create the destination module instance for history merge.
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.
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.
void dt_dev_history_undo_end_record_locked(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
Finish an undo record with history_mutex already locked.
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.
static void _history_write_state_release(_history_write_state_t *state)
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.
int dt_history_merge_module_into_history(dt_develop_t *dev_dest, dt_develop_t *dev_src, dt_iop_module_t *mod_src) REQUIRES(dev_dest -> history_mutex)
Merge a single module instance into a destination 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)
int dt_dev_history_refresh_nodes_ext(dt_develop_t *dev, GList **iop, GList *history, GList **removed_gui_modules)
Refresh GUI module nodes to match history state.
void dt_dev_history_free_history(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
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_dev_add_history_item_ext(dt_develop_t *dev, struct dt_iop_module_t *module, gboolean enable, gboolean force_new_item) REQUIRES(dev -> history_mutex)
Append or update a history item for a module.
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....
uint64_t dt_dev_history_compute_hash(dt_develop_t *dev) REQUIRES_SHARED(dev -> history_mutex)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
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().
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.
static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited) REQUIRES(dev -> history_mutex)
Insert default modules into history when needed.
void dt_dev_history_snapshot_take(dt_develop_t *dev, dt_dev_history_snapshot_t *snapshot) REQUIRES_SHARED(dev -> history_mutex)
Take a snapshot of dev->history. Caller holds history_mutex (reader suffices).
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)
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.
static dt_dev_history_undo_restore_gui_handler_t _undo_restore_gui_handler
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.
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.
void dt_dev_history_undo_end_record(dt_develop_t *dev)
Finish an undo record for history changes.
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_undo_start_record(dt_develop_t *dev)
Start an undo record for history changes.
static dt_dev_history_commit_gui_handler_t _commit_gui_handler
void dt_dev_history_commit_item_now(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable)
Commit one history item immediately – record undo, take history_mutex as writer, write through,...
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_history_undo_start_record_locked(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
Start an undo record with history_mutex already locked.
static void _write_history_from_state(const _history_write_state_t *state, const int32_t imgid)
void dt_dev_invalidate_history_module(GList *list, dt_iop_module_t *module)
Remove a module pointer from a history list.
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.
static void _history_write_state_take(dt_develop_t *dev, _history_write_state_t *state) REQUIRES_SHARED(dev -> history_mutex)
static gboolean _compress_enabled_user_default_params(dt_iop_module_t *module)
Filter: enabled modules with default params (user enabled).
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_write_history_ext(dt_develop_t *dev, const int32_t imgid) REQUIRES_SHARED(dev -> history_mutex)
Write dev->history to DB and XMP for a given image id.
void(* dt_dev_history_commit_gui_handler_t)(struct dt_develop_t *dev, struct dt_iop_module_t *module)
Post-commit presentation work: viewport-size refresh for geometry-changing modules,...
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:1925
void dt_dev_history_gui_update(struct dt_develop_t *dev)
Apply history-loaded params to module GUIs.
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1919
void(* dt_dev_history_undo_restore_gui_handler_t)(struct dt_develop_t *dev, int mask_edit_mode, int request_mask_display)
Restores the presentation half of an undo record: the blending panel's mask toggles for the focused m...
#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:1868
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1360
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:353
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:237
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:143
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1874
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:1941
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1885
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:976
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1365
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:494
dt_dev_pixelpipe_display_mask_t
Definition develop.h:121
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:122
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
Definition develop.h:499
gboolean enabled
–doc was passed on the command line
static int rwlock NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:340
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock) ACQUIRE(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:299
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:217
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:267
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
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:293
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition hash.h:55
static gboolean enable(const dt_image_t *image)
Definition highlights.c:879
void dt_history_toast(const char *format,...)
void dt_history_message(const char *format,...)
void dt_history_changed(const dt_history_change_t what)
Raise it. Internal to this code.
Everything the history, styles and presets code says to the outside world: messages for the user,...
@ DT_HISTORY_CHANGE_DEVELOP
@ FOR_RAW
@ FOR_NOT_MONO
@ FOR_LDR
@ FOR_HDR
@ FOR_NOT_COLOR
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
void dt_history_repository_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_repository_row_cb cb, void *user_data)
gboolean dt_history_repository_delete_dev_history(const int32_t imgid)
gboolean dt_history_repository_write_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_repository_module_exists(const int32_t imgid, const char *operation)
gboolean dt_history_repository_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)
gboolean dt_history_repository_set_end(const int32_t imgid, const int32_t history_end)
int32_t dt_history_repository_get_end(const int32_t imgid)
int32_t dt_history_repository_get_next_num(const int32_t imgid)
void dt_history_repository_foreach_row(const int32_t imgid, dt_history_repository_row_cb cb, void *user_data)
#define UNKNOWN_IMAGE
Definition image.h:78
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:132
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:130
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:50
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:48
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
Definition imageop.c:1713
gboolean dt_iop_module_needs_mask_history_ext(const dt_iop_module_t *module, gboolean *raster, gboolean *drawn, gboolean *parametric)
Definition imageop.c:1205
void dt_iop_load_default_params(dt_iop_module_t *module)
Definition imageop.c:145
void dt_iop_compute_module_hash(dt_iop_module_t *module, GList *masks)
Definition imageop.c:1461
void dt_iop_reload_defaults(dt_iop_module_t *module)
Definition imageop.c:792
void dt_iop_commit_blend_params(dt_iop_module_t *module, const dt_develop_blend_params_t *blendop_params)
Definition imageop.c:1234
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1102
gboolean dt_iop_module_needs_mask_history(const dt_iop_module_t *module)
Definition imageop.c:1228
gboolean dt_iop_check_modules_equal(dt_iop_module_t *mod_1, dt_iop_module_t *mod_2)
Definition imageop.c:1382
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:1855
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:1817
@ IOP_FLAGS_HIDDEN
Definition imageop.h:189
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:196
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:193
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:1095
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:1066
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:1976
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:1970
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:1035
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:913
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:1309
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:1241
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:2606
dt_iop_order_t
Definition iop_order.h:149
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:154
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:155
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:138
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:419
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:115
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:156
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:57
hash
Definition layers.h:90
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_HISTORY
Definition logging.h:75
@ DT_DEBUG_VERBOSE
Definition logging.h:78
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#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 macros.h:96
void dt_masks_read_masks_history(dt_develop_t *dev, const int32_t imgid)
Definition masks.c:1118
void dt_masks_write_masks_history_item(const int32_t imgid, const int num, dt_masks_form_t *form)
Definition masks.c:1131
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
Definition masks.c:925
dt_masks_edit_mode_t dt_masks_get_edit_mode(struct dt_iop_module_t *module)
Definition masks.c:1169
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
Definition masks_gui.c:5809
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_masks_edit_mode_t
@ DT_MASKS_EDIT_OFF
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
const char * name
Definition pdf.h:90
#define DT_PIXELPIPE_CACHE_HASH_INVALID
static void dt_dev_pixelpipe_or_changed(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_change_t flags)
@ DT_DEV_PIPE_SWITCHED
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_module_t *int multi_priority
dt_dev_history_snapshot_t snapshot
dt_iop_params_t * params
Definition dev_history.h:54
dt_atomic_int refcount
Definition dev_history.h:73
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:57
struct dt_iop_module_t *gboolean enabled
Definition dev_history.h:52
A reference-sharing snapshot of dev->history, for readers that outlive a lock.
dt_atomic_int shutdown
int32_t gui_attached
Definition develop.h:167
GList * iop_order_list
Definition develop.h:275
dt_image_t image_storage
Definition develop.h:225
GList * iop
Definition develop.h:269
struct dt_develop_t::@12 transient_params
struct dt_iop_module_t * gui_module
Definition develop.h:170
dt_pthread_rwlock_t history_mutex
Definition develop.h:247
uint64_t serial
Definition develop.h:299
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:214
GList * history
Definition develop.h:259
struct dt_iop_module_t *void * params
Definition develop.h:294
GList * alliop
Definition develop.h:271
dt_atomic_int history_write_pending
Definition develop.h:265
dt_pthread_mutex_t transient_params_mutex
Definition develop.h:301
int32_t blend_size
Definition develop.h:298
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:214
GList * forms
Definition develop.h:304
void * blend_params
Definition develop.h:297
int32_t params_size
Definition develop.h:296
int32_t flags
Definition image.h:401
GTimeSpan change_timestamp
Definition image.h:415
int32_t id
Definition image.h:401
int32_t instance
Definition imageop.h:266
dt_iop_module_so_t * so
Definition imageop.h:376
int request_mask_display
Definition imageop.h:276
dt_iop_params_t * params
Definition imageop.h:333
Definition iop_order.h:160
char operation[20]
Definition iop_order.h:166
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:79
@ DT_SV_CREATE
Definition supervisor.h:78
@ DT_SV_DELETE
Definition supervisor.h:81
static gboolean dt_supervisor_active(void)
Definition supervisor.h:95
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
static double dt_get_wtime(void)
Definition times.h:43
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:394
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:40
@ DT_UNDO_HISTORY
Definition undo.h:43
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:611
void * dt_undo_data_t
Definition undo.h:70
dt_undo_action_t
Definition undo.h:65
@ DT_ACTION_UNDO
Definition undo.h:66
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()