![]() |
Ansel 0.0
A darktable fork - bloat + design vision
|
Ask the masks module about a shape or a group, instead of reading its structs. More...
#include "develop/masks_types.h"
Include dependency graph for masks_group.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Functions | |
| gboolean | dt_masks_form_get_info (const struct dt_masks_form_t *form, dt_masks_form_info_t *out) |
| Describe a form: identity, kind, and – for a group – how many members it holds. | |
| guint | dt_masks_group_copy_members (const struct dt_masks_form_t *group, dt_masks_member_t *out, guint out_max) |
| Copy a group's membership rows, in order, into caller storage. | |
| const char * | dt_masks_type_name (dt_masks_type_t type) |
| The stable, untranslated token for a shape kind: circle, ellipse, polygon, brush, gradient, group, or "unknown". | |
| dt_masks_result_t | dt_masks_group_set_member_operation (struct dt_develop_t *dev, int group_id, int formid, dt_masks_state_t operation, dt_masks_member_t *out) |
| Set a group member's combination operator, or toggle its inversion. | |
| dt_masks_result_t | dt_masks_group_get_member (struct dt_develop_t *dev, int group_id, int formid, dt_masks_member_t *out) |
| Read one group member by identity. | |
| dt_masks_result_t | dt_masks_group_contains (struct dt_develop_t *dev, int container_id, int needle_id) |
Whether container_id holds needle_id, at any depth (and trivially when they are the same id). | |
| dt_masks_result_t | dt_masks_group_covers_shapes (struct dt_develop_t *dev, int group_id, int target_id, gboolean *has_shapes) |
Whether every shape group_id ultimately holds is also held by target_id. | |
| dt_masks_result_t | dt_masks_group_first_use (struct dt_develop_t *dev, int root_id, int formid, int *holder_id, guint *index, char *holder_name, size_t holder_name_size) |
Where the mask rooted at root_id FIRST applies formid. | |
| int | dt_masks_group_find_holder (struct dt_develop_t *dev, int formid) |
Which group references formid, searching every group in dev->forms depth-first. | |
| guint | dt_masks_group_list (struct dt_develop_t *dev, dt_masks_form_info_t *out, guint out_max) |
| Describe every group of the live list, in dev->forms order, into caller storage. | |
| dt_masks_result_t | dt_masks_group_set_member_opacity (struct dt_develop_t *dev, int group_id, int formid, float opacity, dt_masks_member_t *out) |
| Set a group member's opacity. | |
| gchar * | dt_masks_group_name_for_module (const struct dt_iop_module_t *module) |
| The name a module's own mask group carries: "Group <module>", after the module's multi-instance name when it has one, its own name otherwise. | |
| dt_masks_result_t | dt_masks_group_set_name_from_module (struct dt_develop_t *dev, int group_id, struct dt_iop_module_t *module) |
| Name a group after the module that renders it, by id. | |
Ask the masks module about a shape or a group, instead of reading its structs.
Part of the enclosure of src/develop/masks (issue #1299, phase P2). Eight files outside the module reach directly into dt_masks_form_t and dt_masks_form_group_t; this is where the questions they are really asking get names.
WHAT THE FIRST PARAMETER MEANS – the convention that replaces per-function threading notes:
GList *forms first – resolve against a borrowed refcounted snapshot (pipe->forms, hist->forms). No lock, no copy-on-write, read-only. const dt_masks_form_t * first – an already-resolved handle. Thread-neutral: reads only that object's own memory. No lock, no copy-on-write. dt_develop_t *dev first – touches the live list. Returns dt_masks_result_t => it writes, and owns the lock and the copy-on-write internally.
Only the resolvers come in pairs, which is what makes a cross-thread resolve visible at the call site rather than invisible.
Everything crosses this boundary BY VALUE (dt_masks_form_info_t, dt_masks_member_t). A caller never holds a pointer into a refcounted form: the next dt_masks_cow_touch() replaces the object wholesale, so such a pointer is a use-after-free waiting for a slow enough reader.
This header includes develop/masks_types.h and nothing else – in particular no GTK, unlike develop/masks_gui.h. Model: develop/masks/masks_history.h, which already compiles against an opaque dt_masks_form_t on tag declarations alone.
Definition in file masks_group.h.
| gboolean dt_masks_form_get_info | ( | const struct dt_masks_form_t * | form, |
| dt_masks_form_info_t * | out | ||
| ) |
Describe a form: identity, kind, and – for a group – how many members it holds.
Thread-neutral: reads only form's own memory. Takes no lock and does not copy-on-write.
| form | the form to describe. NULL is not an error, it is simply not a form: returns FALSE. |
| out | filled on TRUE, and left COMPLETELY UNTOUCHED on FALSE, so a caller may keep a default in it across a failed call. |
out was filled. | dt_masks_result_t dt_masks_group_contains | ( | struct dt_develop_t * | dev, |
| int | container_id, | ||
| int | needle_id | ||
| ) |
Whether container_id holds needle_id, at any depth (and trivially when they are the same id).
The question every caller wiring one group into another has to ask first: closing a cycle in the membership graph makes every walk over it non-terminating, the tree builds included.
container_id that names no group, or names a shape, is NOT_FOUND. Definition at line 1942 of file masks.c.
References _group_contains_recurs(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, and IS_NULL_PTR.
Referenced by _form_can_join_group(), _modchooser_run(), and _tooltip_add_text().
| guint dt_masks_group_copy_members | ( | const struct dt_masks_form_t * | group, |
| dt_masks_member_t * | out, | ||
| guint | out_max | ||
| ) |
Copy a group's membership rows, in order, into caller storage.
Thread-neutral: reads only group's own memory. Takes no lock and does not copy-on-write.
ORDER IS THE CONTRACT, and it is not cosmetic. The stored order is the compositing order, the GTK row order, the index into iop/retouch.c's rt_forms[] and the index into iop/spots.c's clone_algo[] – the last two persisted in every user's database. This function must never filter, never recurse into sub-groups, and never reorder. A row that cannot be read still consumes its index (it comes back zeroed), because dropping it would silently re-pair every later shape with the wrong algorithm.
| group | a group form. Anything else – including NULL, and including a shape whose ->points holds geometry nodes rather than membership rows – returns 0. That check is what keeps the polymorphic ->points unreachable from outside the module. |
| out | caller storage, or NULL to query the count only. |
| out_max | capacity of out in elements. |
out_max; exactly MIN(total, out_max) elements are written. | dt_masks_result_t dt_masks_group_covers_shapes | ( | struct dt_develop_t * | dev, |
| int | group_id, | ||
| int | target_id, | ||
| gboolean * | has_shapes | ||
| ) |
Whether every shape group_id ultimately holds is also held by target_id.
Leaf by leaf and at any depth on both sides, so it answers for a group of groups too. What it means is that nesting group_id into target_id would add no shape the target does not already apply – NOT that it would change nothing, which is a different question the combine operators answer (a shape applied twice in difference or exclusion does change the mask).
| has_shapes | (may be NULL) set to TRUE as soon as a leaf shape is met, so a caller can tell "all covered" from "there was nothing in it to cover". |
Definition at line 1973 of file masks.c.
References _group_covers_recurs(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, FALSE, and IS_NULL_PTR.
Referenced by _form_can_join_group().
| int dt_masks_group_find_holder | ( | struct dt_develop_t * | dev, |
| int | formid | ||
| ) |
Which group references formid, searching every group in dev->forms depth-first.
A shape's own dt_masks_form_t does not record who holds it, and the row's parentid records where it was AUTHORED, not where it currently lives – so a caller holding only a shape id, as the shape manager's tree does when it lists shapes at top level, has to search. Returns the first holder found; a shape referenced by two groups has no single answer, and the caller wanting a specific one already knows which.
formid). Definition at line 1878 of file masks.c.
References _find_holder(), dt_masks_form_t::formid, and IS_NULL_PTR.
Referenced by _find_holder_names_the_group_that_references_a_shape(), and _tree_context_menu().
| dt_masks_result_t dt_masks_group_first_use | ( | struct dt_develop_t * | dev, |
| int | root_id, | ||
| int | formid, | ||
| int * | holder_id, | ||
| guint * | index, | ||
| char * | holder_name, | ||
| size_t | holder_name_size | ||
| ) |
Where the mask rooted at root_id FIRST applies formid.
Walked in compositing order: a group's members in their own order, descending into a member group at the position that group sits at. A shape can legitimately appear more than once in one mask – union and intersection are idempotent, difference and exclusion are not – so this answers which application the later ones are read against, not which ones are redundant.
| holder_id | (may be NULL) the group directly holding that first use. |
| index | (may be NULL) its position inside that group, i.e. its compositing rank. |
| holder_name | (may be NULL) copied, not borrowed, like dt_masks_form_get_info()'s name. |
Definition at line 2013 of file masks.c.
References _first_use_recurs(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, IS_NULL_PTR, and dt_masks_form_t::name.
Referenced by _modchooser_run(), and _tree_append_row().
| dt_masks_result_t dt_masks_group_get_member | ( | struct dt_develop_t * | dev, |
| int | group_id, | ||
| int | formid, | ||
| dt_masks_member_t * | out | ||
| ) |
Read one group member by identity.
The pointer-free counterpart to dt_masks_group_copy_members() when only one row is wanted, and the read half of the read-modify-write a caller needs when it can only express a change as an increment on the current value.
Deliberately does NOT copy-on-write. Touching a group on a plain read would clone a shared group every time the GUI asks what a shape's opacity is – copy-on-write is a writer's obligation.
| out | (may be NULL, though then the call only answers "does this row exist") receives the row, including its index. |
Definition at line 1909 of file masks.c.
References _member_from_entry(), _resolve_member(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, FALSE, IS_NULL_PTR, and out.
Referenced by _a_shared_group_is_cloned_before_its_opacity_changes(), _get_member_reads_a_row_by_identity(), _modchooser_run(), dt_masks_form_change_opacity(), dt_masks_form_get_interaction_value(), dt_masks_form_set_interaction_value(), and dt_masks_gui_populate_interaction_sliders().
| guint dt_masks_group_list | ( | struct dt_develop_t * | dev, |
| dt_masks_form_info_t * | out, | ||
| guint | out_max | ||
| ) |
Describe every group of the live list, in dev->forms order, into caller storage.
The catalogue a caller offers a destination group from – the shape manager's "Attach to the group" – BY VALUE, so the caller neither walks dev->forms nor holds a pointer into a refcounted form. Retouch and spot groups come too; a caller that must not offer them filters on dt_masks_form_info_t.is_retouch.
Takes the masks lock for reading across the walk.
| out | caller storage, or NULL to query the count only. |
| out_max | capacity of out in elements. |
out_max; exactly MIN(total, out_max) elements are written. 0 for a NULL dev. Definition at line 1887 of file masks.c.
References dt_masks_form_get_info(), DT_MASKS_GROUP, dt_pthread_rwlock_rdlock(), dt_pthread_rwlock_unlock(), dt_develop_t::forms, IS_NULL_PTR, dt_develop_t::masks_mutex, out, and dt_masks_form_t::type.
Referenced by _menu_append_join_group(), and _tree_group().
| gchar * dt_masks_group_name_for_module | ( | const struct dt_iop_module_t * | module | ) |
The name a module's own mask group carries: "Group <module>", after the module's multi-instance name when it has one, its own name otherwise.
The convention belongs to the masks module because the string it builds is a form's name. It is published because two callers need the text without a form to write it into: the blend panel shows it as the name entry's placeholder, and puts it back when the user empties the entry.
Thread-neutral: reads only module. Takes no lock and does not copy-on-write.
| dt_masks_result_t dt_masks_group_set_member_opacity | ( | struct dt_develop_t * | dev, |
| int | group_id, | ||
| int | formid, | ||
| float | opacity, | ||
| dt_masks_member_t * | out | ||
| ) |
Set a group member's opacity.
Same id-keyed contract as dt_masks_group_set_member_operation() above, and it exists for a failure this codebase actually shipped. Opacity used to be written through a resolved dt_masks_form_group_t*, which CANNOT copy-on-write: it has the row but not the group that owns it, so every caller had to touch the group itself. Callers compensated by touching the parent once, when a context menu was built, and then mutating the row in place for the whole interaction – but the opacity slider commits history on every step, and each commit re-snapshots dev->forms and shares the group again. The up-front touch was consumed by the first commit, so from the second step on, every drag rewrote the opacity inside history snapshots that were supposed to be frozen, and undo could not restore it. Taking ids instead of a row makes that arrangement unrepresentable.
| opacity | clamped to [0;1]. NaN is INVALID rather than clamped – see the implementation. |
| out | (may be NULL) receives the row AFTER the change, including the clamped opacity, so a caller driving a slider can show what was actually stored. |
Definition at line 2029 of file masks.c.
References _member_from_entry(), _resolve_member(), CLAMPF, DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, DT_MASKS_UNCHANGED, IS_NULL_PTR, dt_masks_form_group_t::opacity, out, and TRUE.
Referenced by _a_shared_group_is_cloned_before_its_opacity_changes(), _set_opacity_clamps_and_reports_what_it_stored(), _set_opacity_refuses_a_nan_rather_than_clamping_it(), dt_masks_form_change_opacity(), dt_masks_form_set_interaction_value(), and rt_masks_form_change_opacity().
| dt_masks_result_t dt_masks_group_set_member_operation | ( | struct dt_develop_t * | dev, |
| int | group_id, | ||
| int | formid, | ||
| dt_masks_state_t | operation, | ||
| dt_masks_member_t * | out | ||
| ) |
Set a group member's combination operator, or toggle its inversion.
Takes the live list, and owns the copy-on-write itself – which is the entire reason this exists. A group is refcounted and shared with every history snapshot that references it, so a caller must touch the group before mutating a row. Doing that correctly ALSO means the caller may not resolve the row first: cloning a group clones its membership blocks too, so an entry pointer taken before the touch belongs to the abandoned copy and the mutation lands nowhere. An id-keyed signature is the only shape that cannot get this wrong, which is why it takes ids and hands the result back rather than letting anyone hold a row.
| operation | DT_MASKS_STATE_INVERSE toggles inversion; any of UNION / INTERSECTION / DIFFERENCE / EXCLUSION replaces the combination operator. Anything else is INVALID. |
| out | (may be NULL) receives the row AFTER the change, including its index – the caller usually needs both to refresh its own view, and this is the only pointer-free way to get them. |
Definition at line 1785 of file masks.c.
References _member_from_entry(), _resolve_member(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, DT_MASKS_STATE_INVERSE, DT_MASKS_STATE_IS_COMBINE_OP, DT_MASKS_UNCHANGED, IS_NULL_PTR, out, dt_masks_form_group_t::state, and TRUE.
Referenced by _blendop_masks_group_operation_callback(), _inverse_toggles_and_leaves_the_operator_alone(), _set_operation_rejects_what_it_cannot_do(), _set_operation_replaces_the_combine_op(), _setting_the_state_it_already_has_is_unchanged(), and _tree_apply_operation().
| dt_masks_result_t dt_masks_group_set_name_from_module | ( | struct dt_develop_t * | dev, |
| int | group_id, | ||
| struct dt_iop_module_t * | module | ||
| ) |
Name a group after the module that renders it, by id.
The write half of dt_masks_group_name_for_module(), and id-keyed for the reason the whole write API is: a group is refcounted and shared with every history snapshot referencing it, so a rename is a mutation that must copy on write. A caller holding the form cannot do that correctly – cloning replaces the object wholesale, and the pointer it wrote through belongs to the copy that was just abandoned.
Definition at line 1808 of file masks.c.
References dt_free, dt_masks_cow_touch(), dt_masks_get_from_id(), DT_MASKS_GROUP, dt_masks_group_name_for_module(), DT_MASKS_INVALID, DT_MASKS_NOT_FOUND, DT_MASKS_OK, DT_MASKS_UNCHANGED, IS_NULL_PTR, name, dt_masks_form_t::name, and dt_masks_form_t::type.
Referenced by _blendop_masks_group_create(), _module_create_own_mask(), and dt_masks_group_update_name().
| const char * dt_masks_type_name | ( | dt_masks_type_t | type | ) |
The stable, untranslated token for a shape kind: circle, ellipse, polygon, brush, gradient, group, or "unknown".
Takes a VALUE, not a form, so a caller can name a kind it recorded earlier, after the form it came from may be gone.
THESE TOKENS ARE PERSISTED. They build the conf keys plugins/darkroom/<plugin>/<type>/<feature> declared in data/anselconfig.xml.in (".../polygon/fading" and friends), so the polygon token is "polygon" and can never become "path": a shape reading a key that is not in confgen gets 0, which would silently reset the user's setting. dt_masks_type_t is a bit field, so first match wins and the order below is load-bearing.
Definition at line 1767 of file masks.c.
References DT_MASKS_BRUSH, DT_MASKS_CIRCLE, DT_MASKS_ELLIPSE, DT_MASKS_GRADIENT, DT_MASKS_GROUP, DT_MASKS_POLYGON, and type.
Referenced by _forms_json(), _type_tokens_are_the_persisted_conf_key_spellings(), dt_masks_get_set_conf_value(), dt_supervisor_describe(), and dt_supervisor_form().