Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dev_history.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2015, 2018 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011 Bruce Guenter.
6 Copyright (C) 2010-2012 Henrik Andersson.
7 Copyright (C) 2011 Karl Mikaelsson.
8 Copyright (C) 2011 Mikko Ruohola.
9 Copyright (C) 2011 Omari Stephens.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2019 Tobias Ellinghaus.
13 Copyright (C) 2012-2014, 2016, 2020-2021 Aldric Renaudin.
14 Copyright (C) 2012 Antony Dovgal.
15 Copyright (C) 2012 Moritz Lipp.
16 Copyright (C) 2012 Richard Wonka.
17 Copyright (C) 2012-2014, 2016-2017 Ulrich Pegelow.
18 Copyright (C) 2013-2022 Pascal Obry.
19 Copyright (C) 2014, 2020 Dan Torop.
20 Copyright (C) 2014 parafin.
21 Copyright (C) 2014-2015 Pedro Côrte-Real.
22 Copyright (C) 2014-2017 Roman Lebedev.
23 Copyright (C) 2016 Alexander V. Smal.
24 Copyright (C) 2017, 2021 luzpaz.
25 Copyright (C) 2018-2019 Edgardo Hoszowski.
26 Copyright (C) 2019 Alexander Blinne.
27 Copyright (C) 2019-2020, 2022-2026 Aurélien PIERRE.
28 Copyright (C) 2019-2021 Diederik Ter Rahe.
29 Copyright (C) 2019-2022 Hanno Schwalm.
30 Copyright (C) 2019 Heiko Bauke.
31 Copyright (C) 2019-2020 Philippe Weyland.
32 Copyright (C) 2020-2021 Chris Elston.
33 Copyright (C) 2020 GrahamByrnes.
34 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
35 Copyright (C) 2020 Hubert Kowalski.
36 Copyright (C) 2020 JP Verrue.
37 Copyright (C) 2020-2021 Ralf Brown.
38 Copyright (C) 2021 paolodepetrillo.
39 Copyright (C) 2021 Sakari Kapanen.
40 Copyright (C) 2022 Martin Bařinka.
41 Copyright (C) 2023 Alynx Zhou.
42 Copyright (C) 2023 lologor.
43 Copyright (C) 2023 Luca Zulberti.
44 Copyright (C) 2023 Ricky Moon.
45 Copyright (C) 2025-2026 Guillaume Stutin.
46 Copyright (C) 2025 Miguel Moquillon.
47
48 darktable is free software: you can redistribute it and/or modify
49 it under the terms of the GNU General Public License as published by
50 the Free Software Foundation, either version 3 of the License, or
51 (at your option) any later version.
52
53 darktable is distributed in the hope that it will be useful,
54 but WITHOUT ANY WARRANTY; without even the implied warranty of
55 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56 GNU General Public License for more details.
57
58 You should have received a copy of the GNU General Public License
59 along with darktable. If not, see <http://www.gnu.org/licenses/>.
60*/
61#include "common/darktable.h"
62#include "common/history.h"
63#include "common/undo.h"
65#include "common/image_cache.h"
67#include "common/iop_order.h"
68#include "develop/dev_history.h"
69#include "develop/blend.h"
70#include "develop/develop.h"
71#include "develop/imageop.h"
72#include "develop/masks.h"
73
74#include "gui/presets.h"
75
76#include <inttypes.h>
77
78#include <glib.h>
79
80static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
81 const int modversion, const char *operation, const void *module_params,
82 const int param_length, const gboolean enabled, const void *blendop_params,
83 const int bl_length, const int blendop_version, const int multi_priority,
84 const char *multi_name, const char *preset_name, int *legacy_params,
85 const gboolean presets);
86
94
95gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets);
96
117static void _dev_history_db_row_cb(void *user_data, const int32_t id, const int num, const int modversion,
118 const char *operation, const void *module_params, const int param_length,
119 const gboolean enabled, const void *blendop_params, const int bl_length,
120 const int blendop_version, const int multi_priority, const char *multi_name,
121 const char *preset_name)
122{
124 _process_history_db_entry(ctx->dev, ctx->imgid, id, num, modversion, operation, module_params, param_length, enabled,
125 blendop_params, bl_length, blendop_version, multi_priority, multi_name, preset_name,
126 ctx->legacy_params, ctx->presets);
127}
128
129// returns the first history item with hist->module == module
131{
132 dt_dev_history_item_t *hist_mod = NULL;
133 for(GList *history = g_list_first(history_list); history; history = g_list_next(history))
134 {
135 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
136
137 if(hist->module == module)
138 {
139 hist_mod = hist;
140 break;
141 }
142 }
143 return hist_mod;
144}
145
147{
148 dt_dev_history_item_t *hist_mod = NULL;
149 for(GList *history = g_list_nth(history_list, history_end -1); history; history = g_list_previous(history))
150 {
151 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
152
153 if(hist->module == module)
154 {
155 hist_mod = hist;
156 break;
157 }
158 }
159 return hist_mod;
160}
161
163 const gboolean enabled, const void *params, const int32_t params_size,
164 const dt_develop_blend_params_t *blend_params, GList *forms)
165{
166 if(!hist || IS_NULL_PTR(module)) return FALSE;
167
168 if(!hist->params)
169 {
170 hist->params = g_malloc0(module->params_size);
171 if(IS_NULL_PTR(hist->params)) return FALSE;
172 }
173
174 if(!hist->blend_params)
175 {
176 hist->blend_params = g_malloc0(sizeof(dt_develop_blend_params_t));
177 if(IS_NULL_PTR(hist->blend_params)) return FALSE;
178 }
179
180 if(hist->forms)
181 {
182 g_list_free_full(hist->forms, (void (*)(void *))dt_masks_free_form);
183 hist->forms = NULL;
184 }
185 hist->forms = forms;
186
187 module->enabled = enabled;
188 hist->enabled = enabled;
189 hist->module = module;
190 hist->params_size = module->params_size;
191 hist->module_version = module->version();
194 hist->iop_order = module->iop_order;
195 hist->multi_priority = module->multi_priority;
196 g_strlcpy(hist->op_name, module->op, sizeof(hist->op_name));
197 g_strlcpy(hist->multi_name, module->multi_name, sizeof(hist->multi_name));
198
199 const void *src_params = params ? params : module->params;
200 const int32_t src_size = params ? params_size : module->params_size;
201 const int32_t sz = MIN(module->params_size, src_size);
202 if(!IS_NULL_PTR(src_params) && hist->params && sz > 0) memcpy(hist->params, src_params, sz);
203
204 const dt_develop_blend_params_t *src_blend = blend_params ? blend_params : module->blend_params;
205 if(src_blend && hist->blend_params) memcpy(hist->blend_params, src_blend, sizeof(dt_develop_blend_params_t));
206
207 // Apply to module to keep the hash in sync.
208 if(hist->params && module->params && module->params_size > 0)
209 memcpy(module->params, hist->params, module->params_size);
211
212 dt_iop_compute_module_hash(module, hist->forms);
213 hist->hash = module->hash;
214
215 return TRUE;
216}
217
219{
220 int max_priority = 0;
221 for(const GList *l = g_list_first(dev->iop); l; l = g_list_next(l))
222 {
223 const dt_iop_module_t *m = (const dt_iop_module_t *)l->data;
224 if(strcmp(m->op, op)) continue;
225 max_priority = MAX(max_priority, m->multi_priority);
226 }
227 return max_priority + 1;
228}
229
230dt_iop_module_t *dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
231 const int multi_priority)
232{
233 const char *name = (multi_name && *multi_name) ? multi_name : NULL;
234 dt_iop_module_t *module = dt_iop_get_module_by_instance_name(dev->iop, op, name);
235 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
236 module = dt_iop_get_module_by_op_priority(dev->iop, op, multi_priority);
237 if(IS_NULL_PTR(module) && (IS_NULL_PTR(name) || *name == '\0'))
238 module = dt_iop_get_module_by_op_priority(dev->iop, op, 0);
239 return module;
240}
241
242dt_iop_module_t *dt_dev_create_module_instance(dt_develop_t *dev, const char *op, const char *multi_name,
243 const int multi_priority, gboolean use_next_priority)
244{
246 if(IS_NULL_PTR(base)) base = dt_iop_get_module_by_op_priority(dev->iop, op, -1);
247 if(IS_NULL_PTR(base)) return NULL;
248
249 if((base->flags() & IOP_FLAGS_ONE_INSTANCE) == IOP_FLAGS_ONE_INSTANCE)
250 return base;
251
252 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
253 if(IS_NULL_PTR(module)) return NULL;
254
255 if(dt_iop_load_module(module, base->so, dev))
256 {
257 fprintf(stderr, "[dt_dev_create_module_instance] can't load module %s\n", op);
258 dt_free(module);
259 return NULL;
260 }
261
262 module->instance = base->instance;
263 module->enabled = FALSE;
264 module->multi_priority = use_next_priority ? dt_dev_next_multi_priority_for_op(dev, op) : multi_priority;
265 g_strlcpy(module->multi_name, multi_name ? multi_name : "", sizeof(module->multi_name));
266
267 dev->iop = g_list_append(dev->iop, module);
268 return module;
269}
270
272 const dt_iop_module_t *mod_src)
273{
274 mod_dest->enabled = mod_src->enabled;
275
276 const int32_t sz_dest = mod_dest->params_size;
277 const int32_t sz_src = mod_src->params_size;
278
279 // If both param sizes don't match, we have a serious problem
280 assert(sz_dest == sz_src);
281 if(sz_dest != sz_src) return 1;
282
283 const int32_t sz = MIN(sz_dest, sz_src);
284 if(sz > 0) memcpy(mod_dest->params, mod_src->params, sz);
285
286 if(mod_src->blend_params) dt_iop_commit_blend_params(mod_dest, mod_src->blend_params);
287
288 if(dev_src && dt_masks_copy_used_forms_for_module(dev_dest, dev_src, mod_src)) return 1;
289 return 0;
290}
291
293 const dt_dev_history_item_t *hist_src, dt_iop_module_t *mod_dest,
294 dt_dev_history_item_t **out_hist)
295{
296 if(IS_NULL_PTR(hist_src) || IS_NULL_PTR(hist_src->module) || IS_NULL_PTR(mod_dest) || IS_NULL_PTR(out_hist))
297 {
299 "[dt_dev_history_item_from_source_history_item] invalid input: hist=%s hist_module=%s dest=%s out=%s\n",
300 hist_src ? "yes" : "no", hist_src && hist_src->module ? "yes" : "no",
301 mod_dest ? "yes" : "no", out_hist ? "yes" : "no");
302 return 1;
303 }
304
306 if(IS_NULL_PTR(hist))
307 {
309 "[dt_dev_history_item_from_source_history_item] allocation failed: src=%s multi='%s'\n",
310 hist_src->module->op, hist_src->module->multi_name);
311 return 1;
312 }
313
314 if(dt_masks_copy_used_forms_for_module(dev_dest, dev_src, hist_src->module))
315 {
317 "[dt_dev_history_item_from_source_history_item] mask copy failed: src=%s multi='%s'\n",
318 hist_src->module->op, hist_src->module->multi_name);
320 return 1;
321 }
322 GList *forms_snapshot = NULL;
323 if(dt_iop_module_needs_mask_history(hist_src->module))
324 {
325 forms_snapshot = dt_masks_snapshot_current_forms(dev_dest, FALSE);
326 if(IS_NULL_PTR(forms_snapshot))
327 {
329 "[dt_dev_history_item_from_source_history_item] no destination mask forms to snapshot: "
330 "src=%s multi='%s'\n",
331 hist_src->module->op, hist_src->module->multi_name);
332
334 return 1;
335 }
336 }
337
338 if(!dt_dev_history_item_update_from_params(dev_dest, hist, mod_dest, hist_src->enabled, hist_src->params,
339 hist_src->module->params_size, hist_src->blend_params, forms_snapshot))
340 {
342 "[dt_dev_history_item_from_source_history_item] params update failed: src=%s multi='%s' "
343 "dest=%s multi='%s' src_params=%d dest_params=%d\n",
344 hist_src->module->op, hist_src->module->multi_name, mod_dest->op, mod_dest->multi_name,
345 hist_src->module->params_size, mod_dest->params_size);
347 return 1;
348 }
349
350 *out_hist = hist;
351 return 0;
352}
353
354int dt_dev_merge_history_into_image(dt_develop_t *dev_src, int32_t dest_imgid, const GList *mod_list,
355 gboolean merge_iop_order, const dt_history_merge_strategy_t mode,
356 const gboolean paste_instances, const char *source_label,
357 dt_hm_batch_state_t *batch)
358{
359 if(dest_imgid <= 0) return 1;
360 if(IS_NULL_PTR(mod_list)) return 0;
361
362 dt_develop_t dev_dest = { 0 };
363 dt_dev_init(&dev_dest, FALSE);
364 const gboolean first_run = dt_dev_reload_history_items(&dev_dest, dest_imgid);
365
366 if(first_run)
367 {
368 /* Match the persistent state produced by opening the destination in darkroom
369 * after a history deletion: the first-run defaults, auto-presets, image
370 * flags and resulting module order must exist before paste/style merging.
371 */
372 dt_image_t *image = dt_image_cache_get(darktable.image_cache, dest_imgid, 'w');
373 if(!IS_NULL_PTR(image))
374 {
375 *image = dev_dest.image_storage;
377 }
378
379 dt_dev_write_history_ext(&dev_dest, dest_imgid);
380 }
381
382 /* If the destination history was just recreated from an empty stack, keep the
383 * image-format default order we would get by opening the image in darkroom.
384 * Source ordering constraints would otherwise let a pasted JPEG/style order
385 * turn a freshly reset RAW destination into a non-RAW pipeline.
386 */
387 const gboolean use_source_iop_order = merge_iop_order && !first_run;
388 const int ret_val = dt_history_merge(&dev_dest, dev_src, dest_imgid, mod_list, use_source_iop_order, mode,
389 paste_instances, source_label, batch);
390
391 if(ret_val == 0)
392 {
394 dt_dev_write_history(&dev_dest, FALSE);
395 }
396
397 dt_dev_cleanup(&dev_dest);
398 return ret_val;
399}
400
409{
410 dt_dev_history_item_t *hist_mod = NULL;
411 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
412 {
413 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
414 if(!hist || !hist->module) continue;
415
416 if(strcmp(hist->module->op, module->op) == 0)
417 {
418 hist_mod = hist;
419 break;
420 }
421 }
422 return hist_mod;
423}
424
437 const dt_iop_module_t *mod_src,
438 gboolean *created,
439 gboolean *reused_base)
440{
441 *created = FALSE;
442 *reused_base = FALSE;
443
444 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
445 return dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
446
447 dt_iop_module_t *module
448 = dt_dev_get_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority);
449 if(module) return module;
450
451 if(_search_history_by_op(dev_dest, mod_src) == NULL)
452 {
453 module = dt_iop_get_module_by_op_priority(dev_dest->iop, mod_src->op, -1);
454 if(!IS_NULL_PTR(module))
455 {
456 *reused_base = TRUE;
457 return module;
458 }
459 }
460
461 module = dt_dev_create_module_instance(dev_dest, mod_src->op, mod_src->multi_name, mod_src->multi_priority, FALSE);
462 if(module) *created = TRUE;
463
464 return module;
465}
466
467// dev_src is used only to copy masks, if no mask will be copied it can be null
469{
470 gboolean created = FALSE;
471 gboolean reused_base = FALSE;
472 dt_iop_module_t *module = _history_merge_resolve_dest_instance(dev_dest, mod_src, &created, &reused_base);
473 if(IS_NULL_PTR(module)) return 0;
474
475 if(mod_src->flags() & IOP_FLAGS_ONE_INSTANCE)
476 {
477 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",
478 mod_src->name(), mod_src->multi_name);
479 }
480 else if(created)
481 {
482 dt_print(DT_DEBUG_HISTORY, "[dt_history_merge_module_into_history] %s (%s) will be inserted as a new instance in target history\n",
483 mod_src->name(), mod_src->multi_name);
484 }
485 else if(reused_base)
486 {
487 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",
488 mod_src->name(), mod_src->multi_name);
489 }
490
491 g_strlcpy(module->multi_name, mod_src->multi_name, sizeof(module->multi_name));
492
493 if(dt_dev_copy_module_contents(dev_dest, dev_src, module, mod_src)) return 0;
494
495 dt_dev_add_history_item_ext(dev_dest, module, FALSE, FALSE);
496
497 return 1;
498}
507GList *dt_history_duplicate(GList *hist)
508{
509 GList *result = NULL;
510 for(GList *h = g_list_first(hist); h; h = g_list_next(h))
511 {
512 const dt_dev_history_item_t *old = (dt_dev_history_item_t *)(h->data);
514
515 memcpy(new, old, sizeof(dt_dev_history_item_t));
516
517 dt_iop_module_t *module = (old->module) ? old->module : dt_iop_get_module(old->op_name);
518
519 if(old->params && old->params_size > 0)
520 {
521 new->params = malloc(old->params_size);
522 memcpy(new->params, old->params, old->params_size);
523 }
524
525 if(IS_NULL_PTR(module))
526 fprintf(stderr, "[_duplicate_history] can't find base module for %s\n", old->op_name);
527
528 if(old->blend_params && old->blendop_params_size > 0)
529 {
530 new->blend_params = malloc(old->blendop_params_size);
531 memcpy(new->blend_params, old->blend_params, old->blendop_params_size);
532 }
533
534 if(old->forms) new->forms = dt_masks_dup_forms_deep(old->forms, NULL);
535
536 result = g_list_prepend(result, new);
537 }
538
539 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
540}
541
550
552{
553 dt_iop_module_t *module;
555};
556
564static void _history_invalidate_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item)
565{
566 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
567 dt_undo_history_t *hist = (dt_undo_history_t *)item;
568 dt_dev_invalidate_history_module(hist->before_snapshot, module);
569 dt_dev_invalidate_history_module(hist->after_snapshot, module);
570}
571
577
583static void _history_undo_data_free(gpointer data)
584{
585 dt_undo_history_t *hist = (dt_undo_history_t *)data;
586 if(IS_NULL_PTR(hist)) return;
587 g_list_free_full(hist->before_snapshot, dt_dev_free_history_item);
588 hist->before_snapshot = NULL;
589 g_list_free_full(hist->after_snapshot, dt_dev_free_history_item);
590 hist->after_snapshot = NULL;
591 g_list_free_full(hist->before_iop_order_list, dt_free_gpointer);
592 hist->before_iop_order_list = NULL;
593 g_list_free_full(hist->after_iop_order_list, dt_free_gpointer);
594 hist->after_iop_order_list = NULL;
595 dt_free(hist);
596}
597
609static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
610{
611 if(type != DT_UNDO_HISTORY) return;
612
613 dt_develop_t *dev = (dt_develop_t *)user_data;
614 dt_undo_history_t *hist = (dt_undo_history_t *)data;
615 if(IS_NULL_PTR(dev) || IS_NULL_PTR(hist)) return;
616
617 GList *snapshot = (action == DT_ACTION_UNDO) ? hist->before_snapshot : hist->after_snapshot;
618 const int history_end = (action == DT_ACTION_UNDO) ? hist->before_end : hist->after_end;
619 GList *iop_order_list
620 = (action == DT_ACTION_UNDO) ? hist->before_iop_order_list : hist->after_iop_order_list;
621
622 GList *history_temp = dt_history_duplicate(snapshot);
623 GList *iop_order_temp = dt_ioppr_iop_order_copy_deep(iop_order_list);
624
627 dev->history = history_temp;
628 dt_dev_set_history_end_ext(dev, history_end);
629 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
630 dev->iop_order_list = iop_order_temp;
634
636 // TODO: check if we need to rebuild the full pipeline and do it only if needed
638
639 if(dev->gui_module)
640 {
645 if(bd)
646 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->showmask),
648 }
649
650 // Ensure all UI pieces (history treeview, iop order, etc.) resync after undo/redo.
651 // Undo callbacks bypass dt_dev_undo_end_record(), so we need to raise the change signal here.
652 if(darktable.gui && dev->gui_attached && dev == darktable.develop)
654}
655
663
683
691
726
727
736{
737 GList *history = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev));
738 while(history)
739 {
740 // We need to use a while because we are going to dynamically remove entries at the end
741 // of the list, so we can't know the number of iterations
742 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
743 if(IS_NULL_PTR(hist) || IS_NULL_PTR(hist->module))
744 {
746 "[dt_dev_add_history_item_ext] archival history item %s at %i is past history limit (%i) and will be kept\n",
747 hist ? hist->op_name : "(null)", g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
748 history = g_list_next(history);
749 continue;
750 }
751
752 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history item %s at %i is past history limit (%i)\n",
753 hist->module->op, g_list_index(dev->history, hist), dt_dev_get_history_end_ext(dev) - 1);
754
755 // In case user wants to insert new history items before auto-enabled or mandatory modules,
756 // we forbid it, unless we already have at least one lower history entry.
757
758 // Check if an earlier instance of mandatory module exists
759 gboolean earlier_entry = FALSE;
760 if((hist->module->hide_enable_button || hist->module->default_enabled))
761 {
762 for(GList *prior_history = g_list_previous(history); prior_history;
763 prior_history = g_list_previous(prior_history))
764 {
765 dt_dev_history_item_t *prior_hist = (dt_dev_history_item_t *)(prior_history->data);
766 if(!prior_hist || !prior_hist->module) continue;
767 if(prior_hist->module->so == hist->module->so)
768 {
769 earlier_entry = TRUE;
770 break;
771 }
772 }
773 }
774
775 // In case we delete the current link, we need to update the incrementer now
776 // to not loose the reference
777 GList *link = history;
778 history = g_list_next(history);
779
780 // Finally: attempt removing the obsoleted entry
781 if((!hist->module->hide_enable_button && !hist->module->default_enabled)
782 || earlier_entry)
783 {
784 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));
786 dev->history = g_list_delete_link(dev->history, link);
787 }
788 else
789 {
790 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));
791 }
792 }
793}
794
795gboolean dt_dev_add_history_item_ext(dt_develop_t *dev, struct dt_iop_module_t *module, gboolean enable,
796 gboolean force_new_item)
797{
798 // If this history item is the first for this module,
799 // we need to notify the pipeline that its topology may change (aka insert a new node).
800 // Since changing topology is expensive, we want to do it only when needed.
801 gboolean add_new_pipe_node = FALSE;
802
803 if(IS_NULL_PTR(module))
804 {
805 // module = NULL means a mask was changed from the mask manager and that's where this function is called.
806 // Find it now, even though it is not enabled and won't be.
807 module = dt_masks_get_mask_manager(dev);
808 if(!IS_NULL_PTR(module))
809 {
810 // Mask manager is an IOP that never processes pixel aka it's an ugly hack to record mask history
811 force_new_item = FALSE;
812 enable = FALSE;
813 }
814 else
815 {
816 return add_new_pipe_node;
817 }
818 }
819
820 // look for leaks on top of history
822
823 // Check if the current module to append to history is actually the same as the last one in history,
824 GList *last = g_list_last(dev->history);
825 gboolean new_is_old = FALSE;
826 if(last && last->data && !force_new_item)
827 {
828 dt_dev_history_item_t *last_item = (dt_dev_history_item_t *)last->data;
829 dt_iop_module_t *last_module = last_item->module;
830 new_is_old = dt_iop_check_modules_equal(module, last_module);
831 add_new_pipe_node = FALSE;
832 }
833 else
834 {
835 const dt_dev_history_item_t *previous_item =
836 dt_dev_history_get_last_item_by_module(dev->history, module, g_list_length(dev->history));
837 // check if NULL first or prevous_item->module will segfault
838 // We need to add a new pipeline node if:
839 add_new_pipe_node = (IS_NULL_PTR(previous_item)) // it's the first history entry for this module
840 || (previous_item->enabled != module->enabled); // the previous history entry is disabled
841 // if previous history entry is disabled and we don't have any other entry,
842 // it is possible the pipeline will not have this node.
843 }
844
846 if(force_new_item || !new_is_old)
847 {
848 // Create a new history entry
849 hist = (dt_dev_history_item_t *)calloc(1, sizeof(dt_dev_history_item_t));
850 dev->history = g_list_append(dev->history, hist);
851 hist->num = g_list_index(dev->history, hist);
852 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] new history entry added for %s at position %i\n",
853 module->name(), hist->num);
854 }
855 else
856 {
857 // Reuse previous history entry
858 hist = (dt_dev_history_item_t *)last->data;
859
860 dt_print(DT_DEBUG_HISTORY, "[dt_dev_add_history_item_ext] history entry reused for %s at position %i\n",
861 module->name(), hist->num);
862 }
863
864 // Always resync history with all module internals
865 if(enable) module->enabled = TRUE;
866
867 // Include masks if module supports blending and masks are in use, or if it's the mask manager.
868 const gboolean include_masks = dt_iop_module_needs_mask_history(module);
869
870 GList *forms_snapshot = NULL;
871 if(include_masks)
872 {
873 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);
874 // FIXME: this copies ALL drawn masks AND masks groups used by all modules to any module history using masks.
875 // Kudos to the idiots who thought it would be reasonable. Expect database bloating and perf penalty.
876 forms_snapshot = dt_masks_snapshot_current_forms(dev, TRUE);
877 }
878 // else forms_snapshot stays NULL
879
880 // Fill history item and recompute hash (also applies params/blend_params to module to keep hash consistent).
881 dt_dev_history_item_update_from_params(dev, hist, module, module->enabled, module->params, module->params_size,
882 module->blend_params, forms_snapshot);
883
884 if(include_masks && hist->forms)
885 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);
886 else if(include_masks)
887 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);
888
889 // It is assumed that the last-added history entry is always on top
890 // so its cursor index is always equal to the number of elements,
891 // keeping in mind that history_end = 0 is the raw image, aka not a dev->history GList entry.
892 // So dev->history_end = index of last history entry + 1 = length of history
893 dt_dev_set_history_end_ext(dev, g_list_length(dev->history));
894
895 return add_new_pipe_node;
896}
897
899{
900 uint64_t hash = 5381;
901 for(GList *hist = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev) - 1);
902 hist;
903 hist = g_list_previous(hist))
904 {
905 dt_dev_history_item_t *item = (dt_dev_history_item_t *)hist->data;
906 hash = dt_hash(hash, (const char *)&item->hash, sizeof(uint64_t));
907 }
908 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));
909 return hash;
910}
911
912
913// The next 2 functions are always called from GUI controls setting parameters
914// This is why they directly start a pipeline recompute.
915// Otherwise, please keep GUI and pipeline fully separated.
916
917void dt_dev_add_history_item_real(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean redraw)
918{
919 gboolean add_new_pipe_node = FALSE;
920
923
926 add_new_pipe_node = dt_dev_add_history_item_ext(dev, module, enable, FALSE);
929
930 // Run the delayed post-commit actions if implemented
931 if(!IS_NULL_PTR(module) && !IS_NULL_PTR(module->post_history_commit)) module->post_history_commit(module);
932
933 // Figure out if the current history item includes masks/forms
934 GList *last_history = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev) - 1);
935 dt_dev_history_item_t *hist = NULL;
936 gboolean has_forms = FALSE;
937 if(last_history)
938 {
939 hist = (dt_dev_history_item_t *)last_history->data;
940 has_forms = (!IS_NULL_PTR(hist->forms));
941 }
942
943 // We don't update history hash in dt_dev_add_history_item_ext
944 // because it can be called within loops, so that can be expensive.
946 if(dev->image_storage.id > 0)
947 {
949 if(cache_img)
950 {
951 cache_img->history_hash = dt_dev_get_history_hash(dev);
953 }
954 }
955
956 // Recompute pipeline last
957 const gboolean has_raster = module && dt_iop_module_has_raster_mask(module);
958 if(!IS_NULL_PTR(module) && !(has_forms || has_raster) && !add_new_pipe_node)
959 {
960 // If we have a module and it doesn't use drawn or raster masks,
961 // we only need to resync the top-most history item with pipeline
963 }
964 else
965 {
966 // We either don't have a module, meaning we have the mask manager, or
967 // we have a module and it uses masks (drawn or raster), or the current
968 // history commit changes the pipeline topology. The latter happens for
969 // example when enabling/disabling a module or when a module gets its first
970 // history entry. In all these cases, updating only the top-most synced
971 // history tail is insufficient because the set of active pipe nodes itself
972 // may differ from the previous run.
973 // Because masks can affect several modules anywhere, not necessarily sequentially,
974 // we need a full resync of all pipeline with history.
975 // Note that the blendop params (thus their hash) references the raster mask provider
976 // in its consumer, and the consumer in its provider. So updating the whole pipe
977 // resyncs the cumulative hashes too, and triggers a new recompute from the provider on update.
979 }
980
982
983 if(!IS_NULL_PTR(darktable.gui) && dev->gui_attached && !IS_NULL_PTR(module))
984 {
985 // If module params change the geometry of the ROI,
986 // update immediately so we avoid drawing glitches.
987 if(module->modify_roi_in || module->modify_roi_out)
989
990
991 // Changing a parameter of a disabled module enables it,
992 // so update the GUI toggle state to reflect it.
993 ++darktable.gui->reset; // don't run GUI callbacks when setting GUI state
996 }
997
998 // Save history straight away. Regular GUI edits are the only place where
999 // we accept an asynchronous write because `dev` is the long-lived darkroom
1000 // context and there is no immediate DB read on the same control path.
1002}
1003
1004void dt_dev_free_history_item(gpointer data)
1005{
1007 if(IS_NULL_PTR(item)) return; // nothing to free
1008
1009 dt_free(item->params);
1010 dt_free(item->blend_params);
1011 g_list_free_full(item->forms, (void (*)(void *))dt_masks_free_form);
1012 item->forms = NULL;
1013 dt_free(item);
1014}
1015
1017{
1018 if(IS_NULL_PTR(dev->history)) return;
1019 g_list_free_full(g_steal_pointer(&dev->history), dt_dev_free_history_item);
1020 dev->history = NULL;
1021}
1022
1023gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
1024{
1025 // Recreate the whole history from scratch.
1026 // Backend only: GUI updates and pixelpipe rebuilds need to be triggered by callers.
1029 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
1033 return first_run;
1034}
1035
1036
1045{
1046 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1047 {
1048 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1049 dt_iop_reload_defaults(module);
1050
1051 if(module->multi_priority == 0)
1052 module->iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, module->op, module->multi_priority);
1053 else
1054 module->iop_order = INT_MAX;
1055
1056 // Last chance & desperate attempt at enabling/disabling critical modules
1057 // when history is garbled - This might prevent segfaults on invalid data
1058 if(module->force_enable)
1059 module->enabled = (module->force_enable(module, module->enabled) != 0);
1060
1061 // make sure that always-on modules are always on. duh.
1062 if(module->default_enabled == 1 && module->hide_enable_button == 1)
1063 module->enabled = TRUE;
1064
1065 dt_iop_compute_module_hash(module, dev->forms);
1066 }
1067}
1068
1069// Dump the content of an history entry into its associated module params, blendops, etc.
1078static inline void _history_to_module(const dt_dev_history_item_t *const hist, dt_iop_module_t *module)
1079{
1080 module->enabled = (hist->enabled != 0);
1081
1082 // Update IOP order stuff, that applies to all modules regardless of their internals
1083 module->iop_order = hist->iop_order;
1085
1086 // Copy instance name
1087 g_strlcpy(module->multi_name, hist->multi_name, sizeof(module->multi_name));
1088
1089 // Copy params from history entry to module internals
1090 memcpy(module->params, hist->params, module->params_size);
1092
1093 // Get the module hash
1094 dt_iop_compute_module_hash(module, hist->forms);
1095}
1096
1097
1099{
1100 dt_print(DT_DEBUG_HISTORY, "[dt_dev_pop_history_items_ext] loading history entries into modules...\n");
1101
1102 // Ensure `dev->image_storage` is up-to-date before modules reload their defaults.
1103 // This avoids using incomplete RAW metadata (WB coeffs, matrices) on newly-inited images.
1105
1106 // Shitty design ahead:
1107 // some modules (temperature.c, colorin.c) init their GUI comboboxes
1108 // in/from reload_defaults. Though we already loaded them once at
1109 // _read_history_ext() when initing history, and history is now sanitized
1110 // such that all used module will have at least an entry,
1111 // it's not enough and we need to reload defaults here.
1112 // But anyway, if user truncated history before mandatory modules,
1113 // and we reload it here, it's good to ensure defaults are re-inited.
1115
1116 const int history_end = dt_dev_get_history_end_ext(dev);
1117
1118 // go through history up to history_end and set modules params
1119 GList *history = g_list_first(dev->history);
1120 GList *forms = NULL;
1121 for(int i = 0; i < history_end && history; i++)
1122 {
1123 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1124 dt_iop_module_t *module = hist->module;
1125 if(module) _history_to_module(hist, module);
1126
1127 // Update the reference to the form snapshot that doesn't belong
1128 // conceptually to history items
1129 if(hist->forms) forms = hist->forms;
1130
1131 history = g_list_next(history);
1132 }
1133
1134 // Nuke dev->forms and replace it with the last hist->forms in history.
1136
1137 dt_ioppr_resync_pipeline(dev, 0, "dt_dev_pop_history_items_ext end", TRUE);
1138
1139 // Reloading defaults might have changed the global history hash
1141
1142 // Update darkroom sizes in case clipping & distortion changed.
1143 // This is now handled by the wrapper after releasing the history lock.
1144}
1145
1147{
1152 // Update darkroom sizes after releasing the history lock to avoid deadlocks.
1155}
1156
1158{
1159 if(!dev->gui_attached) return;
1160
1161 // Match the live module instances to the reloaded history before touching GTK.
1162 // This loop may remove obsolete instances or expose instances newly created
1163 // while reading a style/history from the database.
1167
1168 ++darktable.gui->reset;
1169
1170 for(GList *module = g_list_first(dev->iop); module; module = g_list_next(module))
1171 {
1172 dt_iop_module_t *mod = (dt_iop_module_t *)(module->data);
1173
1174 // History reload is backend-only and creates new multi-instances without
1175 // GTK state. Attach every missing GUI here, after releasing history_mutex,
1176 // so styles and global history actions expose their complete module set.
1177 if(!dt_iop_is_hidden(mod) && IS_NULL_PTR(mod->expander))
1178 {
1179 if(IS_NULL_PTR(mod->widget)) dt_iop_gui_init(mod);
1181 }
1182
1183 // Parameters, enabled state, headers and blending controls may all have
1184 // changed, therefore refresh every module rather than only history entries.
1185 dt_iop_gui_update(mod);
1186 }
1187
1189 --darktable.gui->reset;
1190
1192}
1193
1195{
1196 if(!dev->gui_attached) return;
1197
1198 if(rebuild)
1200 else
1202}
1203
1209static void _cleanup_history(const int32_t imgid)
1210{
1212}
1213
1214guint dt_dev_mask_history_overload(GList *dev_history, guint threshold)
1215{
1216 // Count all the mask forms used x history entries, up to a certain threshold.
1217 // Stop counting when the threshold is reached, for performance.
1218 guint states = 0;
1219 for(GList *history = g_list_first(dev_history); history; history = g_list_next(history))
1220 {
1221 dt_dev_history_item_t *hist_item = (dt_dev_history_item_t *)(history->data);
1222 states += g_list_length(hist_item->forms);
1223 if(states > threshold) break;
1224 }
1225 return states;
1226}
1227
1228void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
1229{
1230 if(IS_NULL_PTR(dev) || imgid <= 0) return;
1231
1232 if(darktable.gui && dev->gui_attached)
1233 {
1234 const guint states = dt_dev_mask_history_overload(dev->history, 250);
1235 if(states > 250)
1236 dt_toast_log(_("Image #%i history is storing %d mask states. n"
1237 "Consider compressing history and removing unused masks to keep reads/writes manageable."),
1238 imgid, states);
1239 }
1240
1241 // Reload metadata, drop the stale mipmap and refresh the lighttable thumbnail.
1242 // refresh_filmstrip = FALSE: in darkroom the filmstrip is best-effort, spawning another export
1243 // thread would slow down the current one and we want resources allocated to the realtime main
1244 // preview, not diverted to cosmetic GUI refreshes.
1246}
1247
1248
1249// helper used to synch a single history item with db
1250int dt_dev_write_history_item(const int32_t imgid, dt_dev_history_item_t *h, int32_t num)
1251{
1252 if(IS_NULL_PTR(h)) return 1;
1253
1254 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",
1255 h->op_name, h->multi_name, h->enabled, h->iop_order, imgid);
1256
1257 const char *operation = h->module ? h->module->op : h->op_name;
1258 const int params_size = h->module ? h->module->params_size : h->params_size;
1259 const int module_version = h->module ? h->module->version() : h->module_version;
1260 const int blendop_params_size
1261 = h->blend_params ? (h->blendop_params_size > 0 ? h->blendop_params_size : (int)sizeof(dt_develop_blend_params_t)) : 0;
1262 const int blendop_version
1264
1265 dt_history_db_write_history_item(imgid, num, operation, h->params, params_size, module_version, h->enabled != 0,
1266 h->blend_params, blendop_params_size, blendop_version, h->multi_priority, h->multi_name);
1267
1268 // write masks (if any)
1269 if(h->forms)
1270 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);
1271
1272 for(GList *forms = g_list_first(h->forms); forms; forms = g_list_next(forms))
1273 {
1274 dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
1275 if (form)
1276 dt_masks_write_masks_history_item(imgid, num, form);
1277 }
1278
1279 return 0;
1280}
1281
1283{
1284 // No-op: SQL statement caching/cleanup for history lives in common/history.c (dt_history_cleanup()).
1285}
1286
1287
1288
1289void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid)
1290{
1291 dt_image_t *cache_img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
1292 if(IS_NULL_PTR(cache_img)) return;
1293
1294 dt_print(DT_DEBUG_HISTORY, "[dt_dev_write_history_ext] writing history for image %i...\n", imgid);
1295
1297
1298 _cleanup_history(imgid);
1299
1300 // write history entries
1301 int i = 0;
1302 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
1303 {
1304 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1305 dt_dev_write_history_item(imgid, hist, i);
1306 i++;
1307 }
1308
1310
1311 // write the current iop-order-list for this image
1313
1314 cache_img->history_hash = dt_dev_get_history_hash(dev);
1315
1317}
1318
1319// Schedule history write as a background job to avoid blocking the GUI.
1320// If scheduling fails, fall back to synchronous write to preserve behaviour.
1322{
1324 if(IS_NULL_PTR(d)) return 1;
1325 dt_pthread_rwlock_rdlock(&d->history_mutex);
1326 dt_dev_write_history_ext(d, d->image_storage.id);
1327 dt_pthread_rwlock_unlock(&d->history_mutex);
1328 dt_dev_history_notify_change(d, d->image_storage.id);
1329 return 0;
1330}
1331
1332// Write TO XMP, so from the dev perspective, it's a read
1333void dt_dev_write_history(dt_develop_t *dev, gboolean async)
1334{
1335 if(!async)
1336 {
1341 return;
1342 }
1343
1345 dev->image_storage.id);
1346 dt_control_job_set_params(job, dev, NULL);
1347
1349 {
1350 // scheduling failed: dispose job and run synchronously
1353 }
1354}
1355
1363static gboolean _dev_auto_apply_presets(dt_develop_t *dev, int32_t imgid)
1364{
1365 dt_image_t *image = &dev->image_storage;
1366 const gboolean has_matrix = dt_image_is_matrix_correction_supported(image);
1367 const char *workflow_preset = has_matrix ? _("scene-referred default") : "\t\n";
1368
1369 int iformat = 0;
1370 if(dt_image_needs_rawprepare(image))
1371 iformat |= FOR_RAW;
1372 else
1373 iformat |= FOR_LDR;
1374
1375 if(dt_image_is_hdr(image))
1376 iformat |= FOR_HDR;
1377
1378 int excluded = 0;
1379 if(dt_image_monochrome_flags(image))
1380 excluded |= FOR_NOT_MONO;
1381 else
1382 excluded |= FOR_NOT_COLOR;
1383
1384 int legacy_params = 0;
1385 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = TRUE };
1386 dt_history_db_foreach_auto_preset_row(imgid, image, workflow_preset, iformat, excluded, _dev_history_db_row_cb, &ctx);
1387
1388 // now we want to auto-apply the iop-order list if one corresponds and none are
1389 // still applied. Note that we can already have an iop-order list set when
1390 // copying an history or applying a style to a not yet developed image.
1391
1392 if(!dt_ioppr_has_iop_order_list(imgid))
1393 {
1394 void *params = NULL;
1395 int32_t params_len = 0;
1396 if(dt_history_db_get_autoapply_ioporder_params(imgid, image, iformat, excluded, &params, &params_len))
1397 {
1398 GList *iop_list = dt_ioppr_deserialize_iop_order_list(params, params_len);
1399 dt_ioppr_write_iop_order_list(iop_list, imgid);
1400 g_list_free_full(iop_list, dt_free_gpointer);
1401 iop_list = NULL;
1403 dt_free(params);
1404 }
1405 else
1406 {
1407 // No auto-applied order exists for this image. Persist the built-in order
1408 // matching the input class; the non-RAW order is named ANSEL_JPG but also
1409 // covers rendered formats such as TIFF/PNG/HDR.
1410 const dt_iop_order_t default_order = dt_image_needs_rawprepare(image)
1413 GList *iop_list = dt_ioppr_get_iop_order_list_version(default_order);
1414 dt_ioppr_write_iop_order_list(iop_list, imgid);
1415 g_list_free_full(iop_list, dt_free_gpointer);
1416 iop_list = NULL;
1418 }
1419 }
1420
1421 // Notify our private image copy that auto-presets got applied
1423
1424 return TRUE;
1425}
1426
1437static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited)
1438{
1439 // Module already in history: don't prepend extra entries
1441 return;
1442
1443 // Module has no user params: no history: don't prepend either
1444 if((module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1445 && (module->default_enabled || (module->force_enable && module->force_enable(module, FALSE))))
1446 {
1447 module->enabled = TRUE;
1448 return;
1449 }
1450
1451 dt_image_t *image = &dev->image_storage;
1452
1453 // Prior to Darktable 3.0, modules enabled by default which still had
1454 // default params (no user change) were not inserted into history/DB.
1455 // We need to insert them here with default params.
1456 // But defaults have changed since then for some modules, so we need to ensure
1457 // we insert them with OLD defaults.
1458 if(module->default_enabled || (module->force_enable && module->force_enable(module, FALSE)))
1459 {
1460 module->enabled = TRUE;
1461 const gboolean has_matrix = dt_image_is_matrix_correction_supported(image);
1462 const gboolean is_raw = dt_image_is_raw(image);
1463
1464 if(!strcmp(module->op, "temperature")
1465 && (image->change_timestamp == -1) // change_timestamp is not defined for old pics
1466 && is_raw && is_inited && has_matrix)
1467 {
1468 dt_print(DT_DEBUG_HISTORY, "[history] Image history seems older than Darktable 3.0, we will insert white balance.\n");
1469
1470 // Temp revert to legacy defaults
1471 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "legacy");
1472 dt_iop_reload_defaults(module);
1473
1474 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1475
1476 // Go back to current defaults
1477 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1478 dt_iop_reload_defaults(module);
1479 }
1480 else
1481 {
1482 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1483 }
1484 }
1485 else if(module->workflow_enabled && !is_inited)
1486 {
1487 module->enabled = TRUE;
1488 dt_dev_add_history_item_ext(dev, module, TRUE, TRUE);
1489 }
1490
1491 if(module->enabled)
1492 dt_print(DT_DEBUG_HISTORY, "[history] %s was inserted into history by default (enabled %i)\n", module->op, module->enabled);
1493}
1494
1495// Returns TRUE if this is a freshly-inited history on which we just applied auto presets and defaults,
1496// FALSE if we had an earlier history
1497gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets)
1498{
1499 const gboolean is_inited = (dev->image_storage.flags & DT_IMAGE_AUTO_PRESETS_APPLIED);
1500
1501 // Make sure this is set
1502 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1503
1504 // make sure all modules default params are loaded to init history
1505 for(GList *iop = g_list_first(dev->iop); iop; iop = g_list_next(iop))
1506 {
1507 dt_iop_module_t *module = (dt_iop_module_t *)(iop->data);
1508 dt_iop_reload_defaults(module);
1509 _insert_default_modules(dev, module, is_inited);
1510 }
1511
1512 // On virgin history image, apply auto stuff (ours and user's)
1513 if(apply_auto_presets && !is_inited) _dev_auto_apply_presets(dev, imgid);
1514 dt_print(DT_DEBUG_HISTORY, "[history] temporary history initialised with default params and presets\n");
1515
1516 return !is_inited;
1517}
1518
1519int dt_dev_replace_history_on_image(dt_develop_t *dev_src, const int32_t dest_imgid,
1520 const gboolean reload_defaults, const char *msg)
1521{
1522 if(dest_imgid <= 0) return 1;
1523
1524 dt_dev_ensure_image_storage(dev_src, dest_imgid);
1525
1526 if(reload_defaults)
1527 {
1528 dt_dev_init_default_history(dev_src, dest_imgid, FALSE);
1529 dt_ioppr_resync_pipeline(dev_src, dest_imgid, msg, FALSE);
1530 }
1531
1533 dt_dev_write_history(dev_src, FALSE);
1534
1535 return 0;
1536}
1537
1538// populate hist->module
1548{
1549 dt_iop_module_t *match = NULL;
1550
1551 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1552 {
1553 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1554 if(!strcmp(module->op, hist->op_name))
1555 {
1556 if(module->multi_priority == hist->multi_priority)
1557 {
1558 // Found exact match at required priority: we are done
1559 hist->module = module;
1560 break;
1561 }
1562 else if(hist->multi_priority > 0)
1563 {
1564 // Found the right kind of module but the wrong instance.
1565 // Current history entry is targeting an instance that may exist later in the pipe, so keep looping/looking.
1566 match = module;
1567 }
1568 }
1569 }
1570
1571 if(!hist->module && match)
1572 {
1573 // We found a module having the required name but not the required instance number:
1574 // add a new instance of this module by using its ->so property
1575 dt_iop_module_t *new_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1576 if(!dt_iop_load_module(new_module, match->so, dev))
1577 {
1578 dev->iop = g_list_append(dev->iop, new_module);
1579 // Just init, it will get rewritten later by resync IOP order methods:
1580 new_module->instance = match->instance;
1581 hist->module = new_module;
1582 }
1583 }
1584 // else we found an already-existing instance and it's in hist->module already
1585
1586 if(hist->module) hist->module->enabled = (hist->enabled != 0);
1587}
1588
1589
1601static void _sync_blendop_params(dt_dev_history_item_t *hist, const void *blendop_params, const int bl_length,
1602 const int blendop_version, int *legacy_params)
1603{
1604 const gboolean is_valid_blendop_version = (blendop_version == dt_develop_blend_version());
1605 const gboolean is_valid_blendop_size = (bl_length == sizeof(dt_develop_blend_params_t));
1606
1607 hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
1610
1611 if(!IS_NULL_PTR(blendop_params) && is_valid_blendop_version && is_valid_blendop_size)
1612 {
1613 memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
1614 }
1615 else if(blendop_params
1616 && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params,
1617 dt_develop_blend_version(), bl_length)
1618 == 0)
1619 {
1621 }
1622 else
1623 {
1624 memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
1625 }
1626}
1627
1641static int _sync_params(dt_dev_history_item_t *hist, const void *module_params, const int param_length,
1642 const int modversion, int *legacy_params, const char *preset_name)
1643{
1644 hist->params_size = hist->module->params_size;
1645 hist->module_version = hist->module->version();
1646 const gboolean is_valid_module_version = (modversion == hist->module->version());
1647 const gboolean is_valid_params_size = (param_length == hist->module->params_size);
1648
1649 hist->params = malloc(hist->module->params_size);
1650 if(is_valid_module_version && is_valid_params_size)
1651 {
1652 memcpy(hist->params, module_params, hist->module->params_size);
1653 }
1654 else
1655 {
1656 if(!hist->module->legacy_params
1657 || hist->module->legacy_params(hist->module, module_params, labs(modversion),
1658 hist->params, labs(hist->module->version())))
1659 {
1660 gchar *preset = (preset_name) ? g_strdup_printf(_("from preset %s"), preset_name)
1661 : g_strdup("");
1662
1663 fprintf(stderr, "[dev_read_history] module `%s' %s version mismatch: history is %d, dt %d.\n", hist->module->op,
1664 preset, modversion, hist->module->version());
1665
1666 dt_control_log(_("module `%s' %s version mismatch: %d != %d"), hist->module->op,
1667 preset, hist->module->version(), modversion);
1668
1669 dt_free(preset);
1670 return 1;
1671 }
1672 else
1673 {
1674 // NOTE: spots version was bumped from 1 to 2 in 2013.
1675 // This handles edits made prior to Darktable 1.4.
1676 // Then spots was deprecated in 2021 in favour of retouch.
1677 // How many edits out there still need the legacy conversion in 2025 ?
1678 if(!strcmp(hist->module->op, "spots") && modversion == 1)
1679 {
1680 // quick and dirty hack to handle spot removal legacy_params
1681 memcpy(hist->blend_params, hist->module->blend_params, sizeof(dt_develop_blend_params_t));
1682 }
1684 }
1685
1686 /*
1687 * Fix for flip iop: previously it was not always needed, but it might be
1688 * in history stack as "orientation (off)", but now we always want it
1689 * by default, so if it is disabled, enable it, and replace params with
1690 * default_params. if user want to, he can disable it.
1691 * NOTE: Flip version was bumped from 1 to 2 in 2014.
1692 * This handles edits made prior to Darktable 1.6.
1693 * How many edits out there still need the legacy conversion in 2025 ?
1694 */
1695 if(!strcmp(hist->module->op, "flip") && hist->enabled == 0 && labs(modversion) == 1)
1696 {
1697 memcpy(hist->params, hist->module->default_params, hist->module->params_size);
1698 hist->enabled = TRUE;
1699 }
1700 }
1701
1702 return 0;
1703}
1704
1705// WARNING: this does not set hist->forms
1729static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num,
1730 const int modversion, const char *operation, const void *module_params,
1731 const int param_length, const gboolean enabled, const void *blendop_params,
1732 const int bl_length, const int blendop_version, const int multi_priority,
1733 const char *multi_name, const char *preset_name, int *legacy_params,
1734 const gboolean presets)
1735{
1736 // Sanity checks
1737 const gboolean is_valid_id = (id == imgid);
1738 const gboolean has_operation = (!IS_NULL_PTR(operation));
1739
1740 if(!(has_operation && is_valid_id))
1741 {
1742 fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n",
1743 dev->image_storage.filename);
1744 return;
1745 }
1746
1752 if(!dt_iop_get_module_from_list(dev->iop, operation)) return;
1753
1754 const int iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, operation, multi_priority);
1755
1756 // Init a bare minimal history entry
1758 hist->module = NULL;
1759 hist->num = num;
1760 hist->iop_order = iop_order;
1761 hist->multi_priority = multi_priority;
1762 hist->enabled = (enabled != 0); // cast to gboolean
1763 g_strlcpy(hist->op_name, operation, sizeof(hist->op_name));
1764 g_strlcpy(hist->multi_name, multi_name ? multi_name : "", sizeof(hist->multi_name));
1765
1766 // Find a .so file that matches our history entry, aka a module to run the params stored in DB
1767 _find_so_for_history_entry(dev, hist);
1768
1769 if(!hist->module)
1770 {
1771 // Keep the serialized history entry even though no live module can consume it.
1772 hist->params_size = MAX(param_length, 0);
1773 hist->module_version = modversion;
1774 hist->blendop_params_size = MAX(bl_length, 0);
1775 hist->blendop_version = blendop_version;
1776 if(!IS_NULL_PTR(module_params) && param_length > 0)
1777 {
1778 hist->params = malloc(param_length);
1779 memcpy(hist->params, module_params, param_length);
1780 }
1781 if(!IS_NULL_PTR(blendop_params) && bl_length > 0)
1782 {
1783 hist->blend_params = malloc(bl_length);
1784 memcpy(hist->blend_params, blendop_params, bl_length);
1785 }
1786
1787 fprintf(
1788 stderr,
1789 "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n",
1790 operation, dev->image_storage.filename);
1791 dev->history = g_list_append(dev->history, hist);
1792 return;
1793 }
1794
1795 // Update IOP order stuff, that applies to all modules regardless of their internals
1796 // Needed now to de-entangle multi-instances
1797 hist->module->iop_order = hist->iop_order;
1798 dt_iop_update_multi_priority(hist->module, hist->multi_priority);
1799
1800 // module has no user params and won't bother us in GUI - exit early, we are done
1801 if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1802 {
1803 // Since it's the last we hear from this module as far as history is concerned,
1804 // compute its hash here.
1805 dt_iop_compute_module_hash(hist->module, NULL);
1806
1807 // Done. We don't add to history
1808 dt_free(hist);
1809 return;
1810 }
1811
1812 // Copy module params if valid version, else try to convert legacy params
1813 if(_sync_params(hist, module_params, param_length, modversion, legacy_params, preset_name))
1814 {
1815 dt_free(hist);
1816 return;
1817 }
1818
1819 // So far, on error we haven't allocated any buffer, so we just freed the hist structure
1820
1821 // Last chance & desperate attempt at enabling/disabling critical modules
1822 // when history is garbled - This might prevent segfaults on invalid data
1823 if(hist->module->force_enable)
1824 hist->enabled = (hist->module->force_enable(hist->module, hist->enabled) != 0);
1825
1826 // make sure that always-on modules are always on. duh.
1827 if(hist->module->default_enabled == TRUE && hist->module->hide_enable_button == TRUE)
1828 hist->enabled = TRUE;
1829
1830 // Copy blending params if valid, else try to convert legacy params
1831 _sync_blendop_params(hist, blendop_params, bl_length, blendop_version, legacy_params);
1832
1833 if(presets && !IS_NULL_PTR(hist->blend_params) && !IS_NULL_PTR(hist->module)
1834 && !IS_NULL_PTR(hist->module->default_blendop_params))
1835 {
1836 dt_develop_blend_params_t preset_blend = *hist->blend_params;
1837 dt_develop_blend_params_t default_blend = *hist->module->default_blendop_params;
1838 preset_blend.mask_mode &= ~DEVELOP_MASK_ENABLED;
1839 default_blend.mask_mode &= ~DEVELOP_MASK_ENABLED;
1840 if(memcmp(&preset_blend, &default_blend, sizeof(dt_develop_blend_params_t)) == 0)
1841 hist->blend_params->mask_mode &= ~DEVELOP_MASK_ENABLED;
1842 }
1843
1844 dev->history = g_list_append(dev->history, hist);
1845
1846 dt_print(DT_DEBUG_HISTORY, "[history entry] read %s at pipe position %i (enabled %i) from %s %s\n", hist->op_name,
1847 hist->iop_order, hist->enabled, (presets) ? "preset" : "database", (presets) ? preset_name : "");
1848}
1849
1850
1851gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid)
1852{
1853 if(imgid == UNKNOWN_IMAGE) return FALSE;
1854
1855 // This should be inited already when creating a pipeline or entering darkroom
1856 // but some pathes don't handle it, so make sure we have modules loaded.
1857 if(IS_NULL_PTR(dev->iop))
1858 dev->iop = dt_dev_load_modules(dev);
1859
1860 // Ensure raw metadata (WB coeffs, matrices, etc.) is available for modules that
1861 // query it while (re)loading defaults (e.g. temperature/colorin).
1862 // This is redundant with `_dt_dev_load_raw()` called from `dt_dev_load_image()`,
1863 // but some call sites reload history without guaranteeing a prior FULL open.
1864 if(dt_dev_ensure_image_storage(dev, imgid))
1865 return FALSE;
1866
1867 // Start fresh
1869 int legacy_params = 0;
1871
1872 // Find the new history end from DB now, if defined.
1873 // Note: dt_dev_set_history_end_ext sanitizes the value with the actual history size.
1874 // It needs to run after dev->history is fully populated.
1875 int32_t history_end = dt_history_get_end(imgid);
1876
1877 // Find out if we already have an history, and how many items
1878 const int32_t db_items = dt_history_db_get_next_history_num(imgid);
1879
1880 // Load all the modules that may be required by the image format,
1881 // plus auto-presets if it's a new edit
1882 gboolean first_run = dt_dev_init_default_history(dev, imgid, (db_items == 0));
1883
1884 // Protect history DB reads with a cache read lock.
1885 // Release it before applying history to modules to avoid deadlocks.
1886 dt_image_t *read_lock_img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1887 if(IS_NULL_PTR(read_lock_img)) return FALSE;
1888
1889 // Load DB history into dev->history
1890 dt_dev_history_db_ctx_t ctx = { .dev = dev, .imgid = imgid, .legacy_params = &legacy_params, .presets = FALSE };
1892 const int32_t history_length = g_list_length(dev->history);
1893
1894 if(history_length > db_items)
1895 {
1896 // We have now more history entries than when reading DB,
1897 // meaning the sanitization step added required default modules.
1898 // We need to shift history end by the same amount to still point to the same entry.
1899 // This is valid whether history_end was 0 or not
1900 history_end += history_length - db_items;
1901 }
1902
1903 // Set a provisional history_end so mask history can resolve current forms.
1904 // We will recompute hashes once all history items are committed below.
1905 dt_dev_set_history_end_ext(dev, history_end);
1906
1907 // Sanitize and flatten module order
1908 dt_ioppr_resync_pipeline(dev, imgid, "dt_dev_read_history_no_image end", FALSE);
1909
1910 // Update "masks history"
1911 // This design is stupid because `dt_dev_history_item_t *hist->forms` is not read
1912 // from the DB history items (`main.history`), but from the `main.masks_history`,
1913 // and later on, `dev->forms` is restored at the `history_end` index from dumping
1914 // the content of the `dt_dev_history_item_t *hist->forms` snapshot.
1915 // This only means that `hist->forms` doesn't belong to the `dt_dev_history_item_t *`
1916 // but should live in its own branch. See `dt_dev_pop_history_items_ext()`
1917 dt_masks_read_masks_history(dev, imgid);
1918
1920 read_lock_img = NULL;
1921
1922 // Now we have fully-populated history items:
1923 // Commit params to modules and publish the masks on the raster stack for other modules to find
1924 for(GList *history = g_list_first(dev->history); history; history = g_list_next(history))
1925 {
1926 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1927 if(!hist)
1928 {
1929 fprintf(stderr, "[dt_dev_read_history_ext] we have no history item. This is not normal.\n");
1930 continue;
1931 }
1932 else if(!hist->module)
1933 {
1934 dt_print(DT_DEBUG_HISTORY, "[history] keeping archival history item %s (%s) without live module binding\n",
1935 hist->op_name, hist->multi_name);
1936 continue;
1937 }
1938
1939 dt_iop_module_t *module = hist->module;
1940 _history_to_module(hist, module);
1941 hist->hash = hist->module->hash;
1942
1943 dt_print(DT_DEBUG_HISTORY, "[history] successfully loaded module %s history (enabled: %i)\n", hist->module->op, hist->enabled);
1944 }
1945
1948
1949 dt_dev_set_history_end_ext(dev, history_end);
1950 // Note: dt_dev_set_history_end already updates dev->history_hash
1951
1952 dt_print(DT_DEBUG_HISTORY, "[history] dt_dev_read_history_ext completed\n");
1953 return first_run;
1954}
1955
1956
1958{
1959 for(; list; list = g_list_next(list))
1960 {
1961 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)list->data;
1962 if (hitem->module == module)
1963 {
1964 hitem->module = NULL;
1965 }
1966 }
1967}
1968
1973
1981{
1982 return (module->flags() & IOP_FLAGS_NO_HISTORY_STACK);
1983}
1984
1992{
1993 return module->default_enabled || (module->force_enable && module->force_enable(module, module->enabled));
1994}
1995
2003{
2004 return module->has_defaults ? module->has_defaults(module) : TRUE;
2005}
2006
2014{
2015 if(!module->blend_params || !module->default_blendop_params) return TRUE;
2016
2017 return memcmp(module->blend_params, module->default_blendop_params,
2018 sizeof(dt_develop_blend_params_t)) == 0;
2019}
2020
2031
2032typedef gboolean (*dt_iop_module_filter_t)(dt_iop_module_t *module);
2033
2041{
2042 for(GList *item = g_list_first(dev->iop); item; item = g_list_next(item))
2043 {
2044 dt_iop_module_t *module = (dt_iop_module_t *)(item->data);
2045 if(!_module_leaves_no_history(module) && filter(module))
2046 dt_dev_add_history_item_ext(dev, module, FALSE, TRUE);
2047 }
2048}
2049
2054{
2055 return module->enabled && _module_is_default_or_forced_enabled(module);
2056}
2057
2062{
2063 return module->enabled && !_module_is_default_or_forced_enabled(module)
2064 && _module_params_are_default(module);
2065}
2066
2071{
2072 return module->enabled && !_module_is_default_or_forced_enabled(module)
2073 && !_module_params_are_default(module);
2074}
2075
2080{
2081 return !module->enabled
2082 && (module->default_enabled || module->workflow_enabled || _module_has_nondefault_internal_params(module));
2083}
2084
2093static void _dt_dev_history_compress_internal(dt_develop_t *dev, const gboolean write_history)
2094{
2095 // Rebuild the history list under lock, but run expensive cross-subsystem
2096 // operations (history->modules sync, optional DB write) after releasing it.
2097 // This keeps history_mutex hold time short and avoids lock-order contention
2098 // with GUI/pipeline users touching masks/pixelpipe.
2100
2101 // Cleanup old history
2103
2104 // Rebuild an history from current pipeline.
2105 // First: modules enabled by default or forced enabled for technical reasons
2107
2108 // Second: modules enabled by user
2109 // 2.1 : start with modules that still have default params,
2111
2112 // 2.2 : then modules that are set to non-default
2114
2115 // Third: disabled modules that have history because they were enabled by default or
2116 // because their internal params (including blendops) differ from defaults. Maybe users
2117 // want to re-enable them later, or it's modules enabled by default that were manually disabled.
2118 // Put them the end of the history, so user can truncate it after the last enabled item
2119 // to get rid of disabled history if needed.
2121
2122 dt_dev_set_history_end_ext(dev, g_list_length(dev->history));
2124
2131 if(write_history)
2132 {
2136 }
2137}
2138
2139void dt_dev_history_compress_ext(dt_develop_t *dev, gboolean write_history)
2140{
2141 _dt_dev_history_compress_internal(dev, write_history);
2142}
2143
2148
2149void dt_dev_history_truncate(dt_develop_t *dev, const int32_t imgid)
2150{
2152
2153 // Remove tail entries (num >= end).
2154 // history_end is a cursor expressed in "number of applied items" terms:
2155 // - keep items [0..history_end-1]
2156 // - remove items [history_end..]
2157 GList *link = g_list_nth(dev->history, dt_dev_get_history_end_ext(dev));
2158 while(link)
2159 {
2160 GList *next = g_list_next(link);
2161 dt_dev_free_history_item(link->data);
2162 dev->history = g_list_delete_link(dev->history, link);
2163 link = next;
2164 }
2165
2167
2168 // Re-apply history and resync iop order from the truncated stack.
2176 dt_dev_write_history_ext(dev, imgid);
2178}
2179
2181{
2182 if(dt_dev_get_history_end_ext(dev) == g_list_length(dev->history))
2184 else
2186}
2187
2188
2197static int _check_deleted_instances(dt_develop_t *dev, GList **_iop_list, GList *history_list)
2198{
2199 GList *iop_list = *_iop_list;
2200 int deleted_module_found = 0;
2201
2202 // we will check on dev->iop if there's a module that is not in history
2203 GList *modules = iop_list;
2204 while(modules)
2205 {
2206 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2207 if(IS_NULL_PTR(mod)) continue;
2208
2209 int delete_module = 0;
2210
2211 // base modules are a special case
2212 // most base modules won't be in history and must not be deleted
2213 // but the user may have deleted a base instance of a multi-instance module
2214 // and then undo and redo, so we will end up with two entries in dev->iop
2215 // with multi_priority == 0, this can't happen and the extra one must be deleted
2216 // dev->iop is sorted by (priority, multi_priority DESC), so if the next one is
2217 // a base instance too, one must be deleted
2218 if(mod->multi_priority == 0)
2219 {
2220 GList *modules_next = g_list_next(modules);
2221 if(modules_next)
2222 {
2223 dt_iop_module_t *mod_next = (dt_iop_module_t *)modules_next->data;
2224 if(strcmp(mod_next->op, mod->op) == 0 && mod_next->multi_priority == 0)
2225 {
2226 // is the same one, check which one must be deleted
2227 const int mod_in_history = (dt_dev_history_get_first_item_by_module(history_list, mod) != NULL);
2228 const int mod_next_in_history = (dt_dev_history_get_first_item_by_module(history_list, mod_next) != NULL);
2229
2230 // current is in history and next is not, delete next
2231 if(mod_in_history && !mod_next_in_history)
2232 {
2233 mod = mod_next;
2234 modules = modules_next;
2235 delete_module = 1;
2236 }
2237 // current is not in history and next is, delete current
2238 else if(!mod_in_history && mod_next_in_history)
2239 {
2240 delete_module = 1;
2241 }
2242 else
2243 {
2244 if(mod_in_history && mod_next_in_history)
2245 fprintf(
2246 stderr,
2247 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) both in history\n",
2248 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2249 mod_next->multi_priority);
2250 else
2251 fprintf(
2252 stderr,
2253 "[_check_deleted_instances] found duplicate module %s %s (%i) and %s %s (%i) none in history\n",
2254 mod->op, mod->multi_name, mod->multi_priority, mod_next->op, mod_next->multi_name,
2255 mod_next->multi_priority);
2256 }
2257 }
2258 }
2259 }
2260 // this is a regular multi-instance and must be in history
2261 else
2262 {
2263 delete_module = (dt_dev_history_get_first_item_by_module(history_list, mod) == NULL);
2264 }
2265
2266 // if module is not in history we delete it
2267 if(delete_module && mod)
2268 {
2269 deleted_module_found = 1;
2270
2272
2273 ++darktable.gui->reset;
2274
2275 // we remove the plugin effectively
2276 if(!dt_iop_is_hidden(mod))
2277 {
2278 // we just hide the module to avoid lots of gtk critical warnings
2279 gtk_widget_hide(mod->expander);
2280
2281 // this is copied from dt_iop_gui_delete_callback(), not sure why the above sentence...
2283 gtk_widget_destroy(mod->widget);
2284 }
2285
2286 iop_list = g_list_delete_link(iop_list, modules);
2287
2288 // remove the module reference from all snapshots
2290
2291 // don't delete the module, a pipe may still need it
2292 dev->alliop = g_list_append(dev->alliop, mod);
2293
2294 --darktable.gui->reset;
2295
2296 // and reset the list
2297 modules = iop_list;
2298 continue;
2299 }
2300
2301 modules = g_list_next(modules);
2302 }
2303 if(deleted_module_found) iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2304
2305 *_iop_list = iop_list;
2306
2307 return deleted_module_found;
2308}
2309
2316static int _rebuild_multi_priority(GList *history_list)
2317{
2318 int changed = 0;
2319 for(const GList *history = history_list; history; history = g_list_next(history))
2320 {
2321 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)history->data;
2322
2323 // if multi_priority is different in history and dev->iop
2324 // we keep the history version
2325 if(hitem->module && hitem->module->multi_priority != hitem->multi_priority)
2326 {
2327 dt_iop_update_multi_priority(hitem->module, hitem->multi_priority);
2328 changed = 1;
2329 }
2330 }
2331 return changed;
2332}
2333
2341static void _reset_module_instance(GList *hist, dt_iop_module_t *module, int multi_priority)
2342{
2343 for(; hist; hist = g_list_next(hist))
2344 {
2345 dt_dev_history_item_t *hit = (dt_dev_history_item_t *)hist->data;
2346
2347 if(IS_NULL_PTR(hit->module) && strcmp(hit->op_name, module->op) == 0 && hit->multi_priority == multi_priority)
2348 {
2349 hit->module = module;
2350 }
2351 }
2352}
2353
2361static void _undo_items_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data)
2362{
2363 struct _cb_data *udata = (struct _cb_data *)user_data;
2364 dt_undo_history_t *hdata = (dt_undo_history_t *)data;
2365 _reset_module_instance(hdata->after_snapshot, udata->module, udata->multi_priority);
2366}
2367
2378static int _create_deleted_modules(GList **_iop_list, GList *history_list)
2379{
2380 GList *iop_list = *_iop_list;
2381 int changed = 0;
2382 gboolean done = FALSE;
2383
2384 GList *l = history_list;
2385 while(l)
2386 {
2387 GList *next = g_list_next(l);
2388 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)l->data;
2389
2390 // this fixes the duplicate module when undo: hitem->multi_priority = 0;
2391 if(IS_NULL_PTR(hitem->module))
2392 {
2393 changed = 1;
2394
2395 const dt_iop_module_t *base_module = dt_iop_get_module_from_list(iop_list, hitem->op_name);
2396 if(IS_NULL_PTR(base_module))
2397 {
2398 fprintf(stderr, "[_create_deleted_modules] can't find base module for %s\n", hitem->op_name);
2399 return changed;
2400 }
2401
2402 // from there we create a new module for this base instance. The goal is to do a very minimal setup of the
2403 // new module to be able to write the history items. From there we reload the whole history back and this
2404 // will recreate the proper module instances.
2405 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
2406 if(dt_iop_load_module(module, base_module->so, base_module->dev))
2407 {
2408 return changed;
2409 }
2410 module->instance = base_module->instance;
2411
2412 // adjust the multi_name of the new module
2413 g_strlcpy(module->multi_name, hitem->multi_name, sizeof(module->multi_name));
2415 module->iop_order = hitem->iop_order;
2416
2417 // we insert this module into dev->iop
2418 iop_list = g_list_insert_sorted(iop_list, module, dt_sort_iop_by_order);
2419
2420 // if not already done, set the module to all others same instance
2421 if(!done)
2422 {
2423 _reset_module_instance(history_list, module, hitem->multi_priority);
2424
2425 // and do that also in the undo/redo lists
2426 struct _cb_data udata = { module, hitem->multi_priority };
2428 done = TRUE;
2429 }
2430
2431 hitem->module = module;
2432 }
2433 l = next;
2434 }
2435
2436 *_iop_list = iop_list;
2437
2438 return changed;
2439}
2440
2441
2442// returns 1 if the topology of the pipe has changed, aka it needs a full rebuild
2443// 0 means only internal parameters of pipe nodes have change, so it's a mere resync
2444int dt_dev_history_refresh_nodes_ext(dt_develop_t *dev, GList **iop, GList *history)
2445{
2446 GList *iop_list = *iop;
2447
2448 // topology has changed?
2449 int pipe_remove = 0;
2450
2451 // we have to check if multi_priority has changed since history was saved
2452 // we will adjust it here
2453 if(_rebuild_multi_priority(history))
2454 {
2455 pipe_remove = 1;
2456 iop_list = g_list_sort(iop_list, dt_sort_iop_by_order);
2457 }
2458
2459 // check if this undo a delete module and re-create it
2460 if(_create_deleted_modules(&iop_list, history))
2461 pipe_remove = 1;
2462
2463 // check if this is a redo of a delete module or an undo of an add module
2464 if(_check_deleted_instances(dev, &iop_list, history))
2465 pipe_remove = 1;
2466
2467 *iop = iop_list;
2468
2469 // if topology has changed, we need to reorder modules in GUI
2470 if(pipe_remove) dt_dev_signal_modules_moved(dev);
2471
2472 return pipe_remove;
2473}
2474
2475
2476/* ---------------------------------------------------------------------------------------------------
2477 * Out-of-history transient param channel (see dev_history.h).
2478 *
2479 * Thread-safety: the slot is guarded by `dev->transient_params_mutex`. Writers (GUI/worker thread)
2480 * `_set`/`_clear`; the pipeline reader (`_get`) copies out under the same lock and never dereferences
2481 * the publishing module. The published payload is a plain byte copy of the module params struct, so the
2482 * pipeline never touches GUI-owned `module->params`.
2483 * ------------------------------------------------------------------------------------------------- */
2484
2485void dt_dev_transient_params_set(dt_iop_module_t *module, const void *params, const size_t params_size,
2486 const void *blend_params, const size_t blend_size)
2487{
2488 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev) || IS_NULL_PTR(params) || params_size == 0) return;
2489 dt_develop_t *dev = module->dev;
2490
2492
2493 if(dev->transient_params.params_size != (int32_t)params_size)
2494 {
2496 dev->transient_params.params = g_malloc0(params_size);
2498 }
2500 memcpy(dev->transient_params.params, params, params_size);
2501
2502 if(!IS_NULL_PTR(blend_params) && blend_size > 0)
2503 {
2504 if(dev->transient_params.blend_size != (int32_t)blend_size)
2505 {
2507 dev->transient_params.blend_params = g_malloc0(blend_size);
2508 dev->transient_params.blend_size = (int32_t)blend_size;
2509 }
2511 memcpy(dev->transient_params.blend_params, blend_params, blend_size);
2512 }
2513 else
2514 {
2517 }
2518
2519 dev->transient_params.module = module;
2520 dev->transient_params.serial++;
2521
2523
2524 /* Intentionally does NOT trigger a pipe recompute. The caller flags the pipe the way that fits its
2525 * use case: a realtime module (drawlayer) raises DT_DEV_PIPE_TOP_CHANGED on the main pipe for a fast
2526 * focused-piece resync, while a geometry-changing edit (crop/ashift) drives a full
2527 * dt_dev_pixelpipe_resync_history_all() so every pipe (main, preview, virtual) replans ROI/formats.
2528 * Auto-triggering here too would route those edits through the partial focused-piece resync as well
2529 * and race the full one (garbled geometry on warm re-edits). */
2530}
2531
2533{
2534 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->dev)) return;
2535 dt_develop_t *dev = module->dev;
2536
2538 if(dev->transient_params.module == module)
2539 {
2544 dev->transient_params.module = NULL;
2545 dev->transient_params.serial++;
2546 }
2548
2549 /* As with _set, the caller is responsible for re-triggering the pipe (resync / history commit) so it
2550 * re-commits the focused module's piece from history instead of the dropped transient snapshot. */
2551}
2552
2554 void *out_params, const size_t out_params_size,
2555 void *out_blend, const size_t out_blend_size, gboolean *out_has_blend)
2556{
2557 if(!IS_NULL_PTR(out_has_blend)) *out_has_blend = FALSE;
2558 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module) || IS_NULL_PTR(out_params) || out_params_size == 0) return FALSE;
2559
2560 gboolean ok = FALSE;
2562 if(dev->transient_params.module == module && !IS_NULL_PTR(dev->transient_params.params)
2563 && dev->transient_params.params_size == (int32_t)out_params_size)
2564 {
2565 memcpy(out_params, dev->transient_params.params, out_params_size);
2566 ok = TRUE;
2567
2568 if(!IS_NULL_PTR(out_blend) && out_blend_size > 0 && !IS_NULL_PTR(dev->transient_params.blend_params)
2569 && dev->transient_params.blend_size == (int32_t)out_blend_size)
2570 {
2571 memcpy(out_blend, dev->transient_params.blend_params, out_blend_size);
2572 if(!IS_NULL_PTR(out_has_blend)) *out_has_blend = TRUE;
2573 }
2574 }
2576 return ok;
2577}
2578
2580{
2581 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module)) return FALSE;
2583 const gboolean active = (dev->transient_params.module == module);
2585 return active;
2586}
void reload_defaults(dt_iop_module_t *module)
Definition ashift.c:5565
#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)
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:565
#define m
Definition basecurve.c:278
int dt_develop_blend_version(void)
Definition blend.c:1630
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:1694
void dt_iop_gui_update_blendif(dt_iop_module_t *module)
Definition blend_gui.c:3484
const float threshold
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
gboolean dt_history_check_module_exists(int32_t imgid, const char *operation, gboolean enabled)
gboolean dt_history_set_end(const int32_t imgid, const int32_t history_end)
gboolean dt_history_db_write_history_item(const int32_t imgid, const int num, const char *operation, const void *op_params, const int op_params_size, const int module_version, const gboolean enabled, const void *blendop_params, const int blendop_params_size, const int blendop_version, const int multi_priority, const char *multi_name)
gboolean dt_history_db_get_autoapply_ioporder_params(const int32_t imgid, const dt_image_t *image, const int iformat, const int excluded, void **params, int32_t *params_len)
int32_t dt_history_db_get_next_history_num(const int32_t imgid)
void dt_history_db_foreach_auto_preset_row(const int32_t imgid, const dt_image_t *image, const char *workflow_preset, const int iformat, const int excluded, dt_history_db_row_cb cb, void *user_data)
int32_t dt_history_get_end(const int32_t imgid)
gboolean dt_history_db_delete_dev_history(const int32_t imgid)
void dt_history_db_foreach_history_row(const int32_t imgid, dt_history_db_row_cb cb, void *user_data)
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
gboolean dt_image_is_raw(const dt_image_t *img)
int dt_image_monochrome_flags(const dt_image_t *img)
gboolean dt_image_is_hdr(const dt_image_t *img)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
int type
char * name
void dt_conf_set_string(const char *name, const char *val)
void dt_toast_log(const char *msg,...)
Definition control.c:808
void dt_control_log(const char *msg,...)
Definition control.c:761
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define UNKNOWN_IMAGE
Definition darktable.h:182
@ DT_DEBUG_HISTORY
Definition darktable.h:740
@ DT_DEBUG_VERBOSE
Definition darktable.h:743
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1043
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
void dt_dev_add_history_item_real(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean redraw)
Thread-safe wrapper around dt_dev_add_history_item_ext().
int dt_history_merge_module_into_history(dt_develop_t *dev_dest, dt_develop_t *dev_src, dt_iop_module_t *mod_src)
Merge a single module instance into a destination history.
static gboolean _module_blend_params_are_default(dt_iop_module_t *module)
Check if blend params match defaults.
static void _history_to_module(const dt_dev_history_item_t *const hist, dt_iop_module_t *module)
Apply a history item to a module instance.
static int _rebuild_multi_priority(GList *history_list)
Resync module multi_priority values from history.
int dt_dev_write_history_item(const int32_t imgid, dt_dev_history_item_t *h, int32_t num)
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)
Reload defaults for all modules in dev->iop.
gboolean dt_dev_transient_params_get(dt_develop_t *dev, const dt_iop_module_t *module, void *out_params, const size_t out_params_size, void *out_blend, const size_t out_blend_size, gboolean *out_has_blend)
static void _undo_items_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data)
Undo iterator callback to fix module pointers in snapshots.
static void _pop_undo(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
Apply an undo/redo history snapshot to a develop context.
static void _history_invalidate_cb(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item)
Undo iterator callback to invalidate module pointers in snapshots.
static gboolean _compress_enabled_user_nondefault_params(dt_iop_module_t *module)
Filter: enabled modules with non-default params (user edits).
static void _dev_history_db_row_cb(void *user_data, const int32_t id, const int num, const int modversion, const char *operation, const void *module_params, const int param_length, const gboolean enabled, const void *blendop_params, const int bl_length, const int blendop_version, const int multi_priority, const char *multi_name, const char *preset_name)
Adapter callback for history DB rows.
static void _find_so_for_history_entry(dt_develop_t *dev, dt_dev_history_item_t *hist)
Bind a history entry to a module instance (.so) in dev->iop.
int dt_dev_history_refresh_nodes_ext(dt_develop_t *dev, GList **iop, GList *history)
Refresh GUI module nodes to match history state.
static gboolean _module_params_are_default(dt_iop_module_t *module)
Check if module params match defaults.
static gboolean _module_is_default_or_forced_enabled(dt_iop_module_t *module)
Check if a module is enabled by default or force-enabled.
static int _sync_params(dt_dev_history_item_t *hist, const void *module_params, const int param_length, const int modversion, int *legacy_params, const char *preset_name)
Load or convert module params into a history item.
gboolean _module_leaves_no_history(dt_iop_module_t *module)
Return whether a module never writes history entries.
int dt_dev_replace_history_on_image(dt_develop_t *dev_src, const int32_t dest_imgid, const gboolean reload_defaults, const char *msg)
Replace an image history with the content of dev_src.
static int _create_deleted_modules(GList **_iop_list, GList *history_list)
Recreate missing module instances referenced by history.
void dt_dev_transient_params_clear(dt_iop_module_t *module)
Drop the transient slot if it belongs to module.
void dt_dev_history_cleanup(void)
Cleanup cached statements or state used by history I/O.
dt_iop_module_t * dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name, const int multi_priority)
Find a module instance by op name and instance metadata.
void dt_dev_transient_params_set(dt_iop_module_t *module, const void *params, const size_t params_size, const void *blend_params, const size_t blend_size)
Out-of-history transient param channel.
static int _check_deleted_instances(dt_develop_t *dev, GList **_iop_list, GList *history_list)
Detect and handle module instances that exist in iop list but not in history.
void dt_dev_free_history_item(gpointer data)
Free a single 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.
int dt_dev_copy_module_contents(dt_develop_t *dev_dest, dt_develop_t *dev_src, dt_iop_module_t *mod_dest, const dt_iop_module_t *mod_src)
static dt_iop_module_t * _history_merge_resolve_dest_instance(dt_develop_t *dev_dest, const dt_iop_module_t *mod_src, gboolean *created, gboolean *reused_base)
Resolve or create the destination module instance for history merge.
static void _dev_history_add_filtered(dt_develop_t *dev, dt_iop_module_filter_t filter)
Append history items for modules that pass a filter.
void dt_dev_history_free_history(dt_develop_t *dev)
Free the whole history list attached to dev->history.
static void _history_undo_data_free(gpointer data)
Free an undo history snapshot structure.
void dt_dev_history_compress_ext(dt_develop_t *dev, gboolean write_history)
Variant of history compression that optionally skips DB writeback.
gboolean(* dt_iop_module_filter_t)(dt_iop_module_t *module)
void dt_dev_history_undo_invalidate_module(dt_iop_module_t *module)
Invalidate a module pointer inside undo snapshots.
void dt_dev_history_compress(dt_develop_t *dev)
Compress an history from a loaded pipeline, aka simply take a snapshot of all modules parameters....
void dt_dev_pop_history_items_ext(dt_develop_t *dev)
Apply history items to module params up to dev->history_end.
static dt_dev_history_item_t * _search_history_by_op(dt_develop_t *dev, const dt_iop_module_t *module)
Find the first history item matching a module operation name.
int dt_dev_history_item_from_source_history_item(dt_develop_t *dev_dest, dt_develop_t *dev_src, const dt_dev_history_item_t *hist_src, dt_iop_module_t *mod_dest, dt_dev_history_item_t **out_hist)
uint64_t dt_dev_history_compute_hash(dt_develop_t *dev)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
int dt_dev_merge_history_into_image(dt_develop_t *dev_src, int32_t dest_imgid, const GList *mod_list, gboolean merge_iop_order, const dt_history_merge_strategy_t mode, const gboolean paste_instances, const char *source_label, dt_hm_batch_state_t *batch)
Merge a list of modules into a destination image history via dt_history_merge().
void dt_dev_pop_history_items(dt_develop_t *dev)
Thread-safe wrapper around dt_dev_pop_history_items_ext(), then update GUI.
void dt_dev_write_history(dt_develop_t *dev, gboolean async)
Thread-safe wrapper around dt_dev_write_history_ext() for dev->image_storage.id.
void dt_dev_history_truncate(dt_develop_t *dev, const int32_t imgid)
static gboolean _compress_enabled_default_or_forced(dt_iop_module_t *module)
Filter: enabled modules that are default/forced enabled.
static void _reset_module_instance(GList *hist, dt_iop_module_t *module, int multi_priority)
Rebind history items to a module instance after recreation.
dt_dev_history_item_t * dt_dev_history_get_last_item_by_module(GList *history_list, dt_iop_module_t *module, int history_end)
Find the last history item referencing a module up to history_end.
void dt_dev_history_compress_or_truncate(dt_develop_t *dev)
Compress history if history_end is at top, otherwise truncate.
gboolean dt_dev_transient_params_active(dt_develop_t *dev, const dt_iop_module_t *module)
static void _dt_dev_history_compress_internal(dt_develop_t *dev, const gboolean write_history)
Rebuild history from current pipeline state.
static void _cleanup_history(const int32_t imgid)
Delete all history entries for an image from the DB.
static void _process_history_db_entry(dt_develop_t *dev, const int32_t imgid, const int id, const int num, const int modversion, const char *operation, const void *module_params, const int param_length, const gboolean enabled, const void *blendop_params, const int bl_length, const int blendop_version, const int multi_priority, const char *multi_name, const char *preset_name, int *legacy_params, const gboolean presets)
Build a history item from DB row data and append it to dev->history.
void dt_dev_history_undo_start_record_locked(dt_develop_t *dev)
Start an undo record with history_mutex already locked.
void dt_dev_history_undo_end_record(dt_develop_t *dev)
Finish an undo record for history changes.
void dt_dev_history_undo_end_record_locked(dt_develop_t *dev)
Finish an undo record with history_mutex already locked.
gboolean dt_dev_init_default_history(dt_develop_t *dev, const int32_t imgid, gboolean apply_auto_presets)
Initialize module defaults and insert required default modules.
gboolean dt_history_module_skip_copy(const int flags)
Determine whether a module should be skipped during history copy.
guint dt_dev_mask_history_overload(GList *dev_history, guint threshold)
static int _dt_dev_write_history_job_run(dt_job_t *job)
void dt_dev_history_pixelpipe_update(dt_develop_t *dev, gboolean rebuild)
Rebuild or resync pixelpipes after backend history changes.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
void dt_dev_history_undo_start_record(dt_develop_t *dev)
Start an undo record for history changes.
static void _remove_history_leaks(dt_develop_t *dev)
Remove history items past history_end when allowed.
static void _sync_blendop_params(dt_dev_history_item_t *hist, const void *blendop_params, const int bl_length, const int blendop_version, int *legacy_params)
Load or convert blendop params into a history item.
void dt_dev_invalidate_history_module(GList *list, dt_iop_module_t *module)
Remove a module pointer from a history list.
void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid)
Write dev->history to DB and XMP for a given image id.
void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
Notify the rest of the app that history changes were written.
gboolean dt_dev_reload_history_items(dt_develop_t *dev, const int32_t imgid)
Reload history from DB and rebuild pipelines/GUI state.
static gboolean _compress_disabled_with_history(dt_iop_module_t *module)
Filter: disabled modules that still need history entries.
gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid)
Read history and masks from DB and populate dev->history.
gboolean dt_dev_add_history_item_ext(dt_develop_t *dev, struct dt_iop_module_t *module, gboolean enable, gboolean force_new_item)
Append or update a history item for a module.
static gboolean _compress_enabled_user_default_params(dt_iop_module_t *module)
Filter: enabled modules with default params (user enabled).
static void _insert_default_modules(dt_develop_t *dev, dt_iop_module_t *module, gboolean is_inited)
Insert default modules into history when needed.
static gboolean _dev_auto_apply_presets(dt_develop_t *dev, int32_t imgid)
Apply auto-presets and default iop order for a fresh history.
static gboolean _module_has_nondefault_internal_params(dt_iop_module_t *module)
Check if any module params (including blend params) are non-default.
dt_dev_history_item_t * dt_dev_history_get_first_item_by_module(GList *history_list, dt_iop_module_t *module)
Find the first history item referencing a module.
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:1665
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1659
#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:1608
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1187
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:309
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:188
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1614
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:1681
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1625
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:847
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1192
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:504
dt_dev_pixelpipe_display_mask_t
Definition develop.h:116
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
Definition develop.h:509
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
#define dt_pthread_rwlock_wrlock
Definition dtpthread.h:394
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:393
void * legacy_params(dt_imageio_module_format_t *self, const void *const old_params, const size_t old_params_size, const int old_version, const int new_version, size_t *new_size)
Definition exr.cc:290
@ FOR_RAW
Definition gui/presets.h:41
@ FOR_NOT_MONO
Definition gui/presets.h:43
@ FOR_LDR
Definition gui/presets.h:40
@ FOR_HDR
Definition gui/presets.h:42
@ FOR_NOT_COLOR
Definition gui/presets.h:44
static gboolean enable(dt_image_t *image)
int dt_history_merge(dt_develop_t *dev_dest, dt_develop_t *dev_src, const int32_t dest_imgid, const GList *mod_list, const gboolean merge_iop_order, const dt_history_merge_strategy_t strategy, const gboolean force_new_modules, const char *source_label, dt_hm_batch_state_t *batch)
Merge a list of modules into a destination image, solving pipeline topologies for proper insertion of...
dt_history_merge_strategy_t
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:119
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:117
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:49
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
Definition imageop.c:2938
void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1998
void dt_iop_compute_module_hash(dt_iop_module_t *module, GList *masks)
Definition imageop.c:1890
void dt_iop_reload_defaults(dt_iop_module_t *module)
Definition imageop.c:1268
void dt_iop_gui_set_expander(dt_iop_module_t *module)
Definition imageop.c:2731
void dt_iop_gui_init(dt_iop_module_t *module)
Definition imageop.c:1229
void dt_iop_commit_blend_params(dt_iop_module_t *module, const dt_develop_blend_params_t *blendop_params)
Definition imageop.c:1667
void dt_iop_gui_update(dt_iop_module_t *module)
Definition imageop.c:2091
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1550
gboolean dt_iop_module_needs_mask_history(const dt_iop_module_t *module)
Definition imageop.c:1647
void dt_iop_gui_set_enable_button(dt_iop_module_t *module)
Definition imageop.c:1205
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2169
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1127
gboolean dt_iop_check_modules_equal(dt_iop_module_t *mod_1, dt_iop_module_t *mod_2)
Definition imageop.c:1818
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
Definition imageop.c:3081
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:3043
@ IOP_FLAGS_HIDDEN
Definition imageop.h:170
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:177
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:174
struct dt_iop_tonecurve_params_t preset
gboolean dt_ioppr_has_iop_order_list(int32_t imgid)
Check whether the image has an explicit order list stored in DB.
Definition iop_order.c:1073
GList * dt_ioppr_get_iop_order_list_version(dt_iop_order_t version)
Return the built-in order list for a given version.
Definition iop_order.c:1044
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:2002
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:1996
gboolean dt_ioppr_write_iop_order_list(GList *iop_order_list, const int32_t imgid)
Persist an order list to the DB for a given image.
Definition iop_order.c:1013
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
Definition iop_order.c:868
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:1325
void dt_ioppr_resync_pipeline(dt_develop_t *dev, const int32_t imgid, const char *msg, gboolean check_duplicates)
Resynchronize pipeline order and related structures.
Definition iop_order.c:1263
GList * dt_ioppr_deserialize_iop_order_list(const char *buf, size_t size)
Deserialize an order list from a binary blob.
Definition iop_order.c:2632
dt_iop_order_t
Definition iop_order.h:143
@ DT_IOP_ORDER_ANSEL_RAW
Definition iop_order.h:148
@ DT_IOP_ORDER_ANSEL_JPG
Definition iop_order.h:149
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:135
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:405
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:129
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:112
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:153
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:55
GList * dt_masks_dup_forms_deep(GList *forms, dt_masks_form_t *form)
Duplicate the list of forms, replacing a single item by formid match.
dt_masks_edit_mode_t
Definition masks.h:200
@ DT_MASKS_EDIT_OFF
Definition masks.h:201
void dt_masks_free_form(dt_masks_form_t *form)
void dt_masks_read_masks_history(dt_develop_t *dev, const int32_t imgid)
void dt_masks_write_masks_history_item(const int32_t imgid, const int num, dt_masks_form_t *form)
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_edit_mode_t dt_masks_get_edit_mode(struct dt_iop_module_t *module)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
int dt_masks_copy_used_forms_for_module(dt_develop_t *dev_dest, dt_develop_t *dev_src, const struct dt_iop_module_t *mod_src)
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_module_t *int multi_priority
struct dt_undo_t * undo
Definition darktable.h:787
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_image_cache_t * image_cache
Definition darktable.h:777
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_control_t * control
Definition darktable.h:773
dt_iop_params_t * params
Definition dev_history.h:52
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:55
struct dt_iop_module_t *gboolean enabled
Definition dev_history.h:50
dt_atomic_int shutdown
int32_t gui_attached
Definition develop.h:162
GList * iop_order_list
Definition develop.h:285
int undo_history_before_end
Definition develop.h:292
dt_image_t image_storage
Definition develop.h:259
GList * undo_history_before_snapshot
Definition develop.h:291
GList * iop
Definition develop.h:279
struct dt_develop_t::@18 transient_params
GList * undo_history_before_iop_order_list
Definition develop.h:293
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
uint64_t serial
Definition develop.h:309
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
GList * history
Definition develop.h:275
struct dt_iop_module_t *void * params
Definition develop.h:304
GList * alliop
Definition develop.h:281
dt_pthread_mutex_t transient_params_mutex
Definition develop.h:311
int32_t blend_size
Definition develop.h:308
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
GList * forms
Definition develop.h:321
int undo_history_depth
Definition develop.h:290
void * blend_params
Definition develop.h:307
int32_t params_size
Definition develop.h:306
int32_t reset
Definition gtk.h:172
uint64_t history_hash
Definition image.h:322
int32_t flags
Definition image.h:319
GTimeSpan change_timestamp
Definition image.h:333
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
int32_t id
Definition image.h:319
GtkWidget * showmask
Definition blend.h:341
gpointer blend_data
Definition imageop.h:318
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:316
GtkWidget * widget
Definition imageop.h:337
char multi_name[128]
Definition imageop.h:363
struct dt_develop_t * dev
Definition imageop.h:296
gboolean default_enabled
Definition imageop.h:303
GModule *dt_dev_operation_t op
Definition imageop.h:256
int32_t instance
Definition imageop.h:258
gboolean workflow_enabled
Definition imageop.h:305
gboolean enabled
Definition imageop.h:298
dt_iop_module_so_t * so
Definition imageop.h:359
GtkWidget * expander
Definition imageop.h:345
int request_mask_display
Definition imageop.h:268
struct dt_develop_blend_params_t * default_blendop_params
Definition imageop.h:316
int32_t params_size
Definition imageop.h:309
dt_iop_params_t * params
Definition imageop.h:307
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
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_undo_iterate_internal(dt_undo_t *self, uint32_t filter, gpointer user_data, void(*apply)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item))
Definition undo.c:364
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
Definition undo.c:163
dt_undo_type_t
Definition undo.h:39
@ DT_UNDO_HISTORY
Definition undo.h:42
void * dt_undo_data_t
Definition undo.h:67
dt_undo_action_t
Definition undo.h:62
@ DT_ACTION_UNDO
Definition undo.h:63