Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
masks_history.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel
3 Copyright (C) 2026 Guillaume Stutin.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20#include "develop/masks.h"
21#include "develop/masks_gui.h"
22
23#include "system/atomic.h"
24#include "develop/develop.h"
25
27{
28 if(IS_NULL_PTR(form)) return NULL;
29 dt_atomic_add_int(&form->refcount, 1);
30 return form;
31}
32
34{
35 if(IS_NULL_PTR(form)) return;
36 // dt_atomic_sub_int returns the value *before* the decrement.
37 if(dt_atomic_sub_int(&form->refcount, 1) == 1) dt_masks_free_form(form);
38}
39
41{
42 if(IS_NULL_PTR(dev) || IS_NULL_PTR(form)) return form;
43 if(dt_atomic_get_int(&form->refcount) <= 1) return form;
44
46
47 GList *node = g_list_find(dev->forms, form);
48 if(IS_NULL_PTR(node))
49 {
50 // Not (yet) a member of dev->forms: e.g. a transient in-creation shape.
51 // Nothing to splice, leave it alone.
53 return form;
54 }
55
57 node->data = clone;
58
60
61 if(!IS_NULL_PTR(dev->form_gui) && dev->form_gui->form_visible == form)
62 dev->form_gui->form_visible = clone;
63
65
66 return clone;
67}
68
70{
72
73 GList *forms_shared = g_list_copy(forms);
74 for(GList *node = forms_shared; node; node = g_list_next(node))
76
77 GList *old_forms = dev->forms;
78 dev->forms = forms_shared;
79
81
82 // form_visible is a raw pointer cached outside dev->forms (dt_masks_form_gui_t::form_visible).
83 // If it pointed at an object we're about to release below, re-resolve it by formid in the new
84 // dev->forms (or drop it if that formid is gone) before old_forms is freed -- otherwise the
85 // very next mask interaction (mouse move, scroll...) dereferences freed memory.
87 && g_list_find(old_forms, dev->form_gui->form_visible))
88 {
89 const int visible_formid = dev->form_gui->form_visible->formid;
90 dev->form_gui->form_visible = dt_masks_get_from_id_ext(dev->forms, visible_formid);
91 }
92
93 g_list_free_full(old_forms, (void (*)(void *))dt_masks_form_unref);
94
95 // Don't recompute gravity_center/area for every form here: this runs on every history
96 // load, undo/redo and history navigation, for every mask of every module in the image,
97 // whether or not the mask editor is even open. Just invalidate -- the one GUI hit-testing
98 // read site (masks.c, group member selection) recomputes lazily on first actual use.
99 for(GList *form_node = dev->forms; form_node; form_node = g_list_next(form_node))
101}
102
103GList *dt_masks_snapshot_current_forms(dt_develop_t *dev, gboolean reset_changed)
104{
106
107 GList *forms_snapshot = g_list_copy(dev->forms);
108 for(GList *node = forms_snapshot; node; node = g_list_next(node))
109 dt_masks_form_ref((dt_masks_form_t *)node->data);
110
112
113 if(reset_changed) dev->forms_changed = FALSE;
114 return forms_snapshot;
115}
116
118{
119 if(IS_NULL_PTR(dev)) return;
120
122 /* Release both claims, never free unconditionally: a form held by dev->forms AND dev->allforms
123 * only reaches refcount 0 on the second unref. */
124 g_list_free_full(dev->forms, (void (*)(void *))dt_masks_form_unref);
125 dev->forms = NULL;
126 g_list_free_full(dev->allforms, (void (*)(void *))dt_masks_form_unref);
127 dev->allforms = NULL;
129}
#define FALSE
Definition ashift_lsd.c:158
int dt_atomic_get_int(dt_atomic_int *var)
int dt_atomic_sub_int(dt_atomic_int *var, int decr)
int dt_atomic_add_int(dt_atomic_int *var, int incr)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock) ACQUIRE(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:299
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:217
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:267
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
void dt_masks_form_invalidate_gravity_center(dt_masks_form_t *mask_form)
Definition masks.c:1322
void dt_masks_free_form(dt_masks_form_t *form)
Definition masks.c:1140
dt_masks_form_t * dt_masks_dup_masks_form(const dt_masks_form_t *form)
Deep-copy a mask form, including its points list.
Definition masks.c:77
dt_masks_form_t * dt_masks_get_from_id_ext(GList *forms, int id)
Definition masks.c:899
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
GList * dt_masks_snapshot_current_forms(dt_develop_t *dev, gboolean reset_changed)
void dt_masks_replace_current_forms(dt_develop_t *dev, GList *forms)
dt_masks_form_t * dt_masks_form_ref(dt_masks_form_t *form)
void dt_masks_form_unref(dt_masks_form_t *form)
dt_masks_form_t * dt_masks_cow_touch(dt_develop_t *dev, dt_masks_form_t *form)
void dt_masks_release_all_forms(dt_develop_t *dev)
gboolean forms_changed
Definition develop.h:309
GList * allforms
Definition develop.h:312
struct dt_masks_form_gui_t * form_gui
Definition develop.h:310
dt_pthread_rwlock_t masks_mutex
Definition develop.h:316
GList * forms
Definition develop.h:304
dt_masks_form_t * form_visible
Definition masks_gui.h:105
dt_atomic_int refcount
Definition masks.h:285