67 const float center_x,
const float center_y,
68 const float squared_radius)
70 const float delta_x = point_x - center_x;
71 const float delta_y = point_y - center_y;
72 const float squared_distance = delta_x * delta_x + delta_y * delta_y;
73 return squared_distance <= squared_radius;
110 const float cursor_radius2 = cursor_radius * cursor_radius;
111 const float cursor_x = mask_gui->
pos[0];
112 const float cursor_y = mask_gui->
pos[1];
148 const int node_count = (node_count_override >= 0) ? node_count_override
149 : (int)g_list_length(mask_form->
points);
152 const gboolean can_test_nodes = (!
IS_NULL_PTR(node_position_cb)) || has_bezier_layout;
153 const int first_node_index = has_bezier_layout ? 0 : 1;
154 const gboolean has_selected_node = (node_count > 0) && can_test_nodes
156 && selected_node >= first_node_index && selected_node < node_count;
158 if(has_selected_node)
161 float handle_x = NAN;
162 float handle_y = NAN;
164 && border_handle_cb(gui_points, node_count, selected_node, &handle_x, &handle_y, user_data)
174 curve_handle_cb(gui_points, selected_node, &handle_x, &handle_y, user_data);
187 node_position_cb(gui_points, selected_node, &node_x, &node_y, user_data);
189 else if(has_bezier_layout)
191 node_x = gui_points->
points[selected_node * 6 + 2];
192 node_y = gui_points->
points[selected_node * 6 + 3];
194 if(!isnan(node_x) && !isnan(node_y)
204 for(
int node_index = first_node_index; node_index < node_count; node_index++)
210 node_position_cb(gui_points, node_index, &node_x, &node_y, user_data);
212 else if(has_bezier_layout)
214 node_x = gui_points->
points[node_index * 6 + 2];
215 node_y = gui_points->
points[node_index * 6 + 3];
217 if(!isnan(node_x) && !isnan(node_y)
230 int inside_border = 0;
231 int near_segment = -1;
232 int inside_source = 0;
233 float nearest_distance = 0.0f;
234 distance_cb(cursor_x, cursor_y, cursor_radius, mask_gui, form_index, node_count, &inside, &inside_border,
235 &near_segment, &inside_source, &nearest_distance, user_data);
242 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
249 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
252 if(near_segment >= 0)
254 if(near_segment < node_count)
261 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
266 return need_refresh_anyway;
284 GList *duplicated_points = NULL;
290 if(point_struct_size != 0)
292 for(GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
294 void *point_copy = malloc(point_struct_size);
295 memcpy(point_copy, point_node->data, point_struct_size);
296 duplicated_points = g_list_prepend(duplicated_points, point_copy);
302 duplicate_form->
points = g_list_reverse(duplicated_points);
304 return duplicate_form;
327 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
330 if(group_entry && group_entry->
formid == form_id)
return group_entry;
340 for(
const GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
490 if(selected_group_entry->
parentid > 0)
495 if(resolved_group_entry)
return resolved_group_entry;
498 return selected_group_entry;
510 int *parent_id,
int *form_index)
512 if(group_entry) *group_entry = NULL;
513 if(parent_id) *parent_id = 0;
514 if(form_index) *form_index = 0;
526 if(group_entry) *group_entry = selected_group_entry;
527 if(parent_id) *parent_id = selected_group_entry->
parentid;
530 return selected_form;
546 const float cursor_x = mask_gui->
pos[0];
547 const float cursor_y = mask_gui->
pos[1];
550 int locked_formid = -1;
555 if(prev_border_selected && prev_group_selected >= 0)
561 const gboolean has_border_lock_candidate = selected_group_entry
566 if(has_border_lock_candidate)
570 int inside_border = 0;
572 int inside_source = 0;
573 float dist = FLT_MAX;
575 g_list_length(selected_form->
points), &inside, &inside_border,
576 &near, &inside_source, &
dist);
577 if(inside_border || near >= 0)
578 locked_formid = selected_group_entry->
formid;
582 if(prev_group_selected >= 0)
586 int selected_index = -1;
587 float best_dist = FLT_MAX;
590 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node), index++)
594 if(locked_formid >= 0 && group_entry->
formid != locked_formid)
continue;
600 int inside_border = 0;
602 int inside_source = 0;
603 float dist = FLT_MAX;
606 &inside, &inside_border, &near, &inside_source, &
dist);
608 const gboolean is_selected_form = (prev_group_selected == index);
609 const gboolean hit_border = (inside_border || near >= 0);
612 if(!is_selected_form && hit_border && !is_open_shape)
615 if(inside || hit_border || inside_source)
619 const float center_dist2 = dx * dx + dy * dy;
620 const float combined_dist2 =
dist * center_dist2;
621 if(combined_dist2 < best_dist)
623 selected_form = form;
624 selected_index = index;
625 best_dist = combined_dist2;
648 const int form_index)
657 const int form_index)
663 int inside_border = 0;
665 int inside_source = 0;
666 float dist = FLT_MAX;
668 mask_gui, form_index,
669 g_list_length(dispatch_form->
points), &inside, &inside_border, &near,
670 &inside_source, &
dist);
671 return inside || inside_border || near >= 0 || inside_source;
684 if((mask_gui->
scrollx - mask_gui->
pos[0] < radius && mask_gui->
scrollx - mask_gui->
pos[0] > -radius)
685 && (mask_gui->
scrolly - mask_gui->
pos[1] < radius && mask_gui->
scrolly - mask_gui->
pos[1] > -radius))
699 const int form_index,
const int button)
701 if(button != 1)
return FALSE;
729 return (GList *)g_list_copy_deep(form_list,
_dup_masks_form_cb, (gpointer)replacement_form);
740 char message[256] =
"";
742 const int form_type = mask_form->
type;
744 int opacity_percent = 100;
747 int selected_form_id = 0;
752 if(!
IS_NULL_PTR(selected_group_entry)) selected_form_id = selected_group_entry->
formid;
756 "[masks] hint begin: form=%p type=%d gui=%p group_selected=%d form_selected=%d node_hovered=%d seg_hovered=%d selected_formid=%d\n",
757 (
void *)mask_form, mask_form ? mask_form->
type : -1, (
void *)mask_gui,
775 opacity_percent = (int)(
dt_conf_get_float(
"plugins/darkroom/masks/opacity") * 100);
785 "[masks] hint end: sel=%p has_set_hint=%d opacity=%d msg_len=%" G_GSIZE_FORMAT
" msg='%s'\n",
786 (
void *)selected_form,
788 opacity_percent, strlen(message), message);
789 return message[0] !=
'\0';
796 mask_gui->
pos[0] = mask_gui->
pos[1] = -1.0f;
834 mask_gui->
delta[0] = mask_gui->
delta[1] = 0.0f;
847 const int gui_points_count = g_list_length(mask_gui->
points);
848 if(gui_points_count == form_index)
852 mask_gui->
points = g_list_append(mask_gui->
points, gui_points_new);
854 else if(gui_points_count < form_index)
867 NULL, NULL,
TRUE, module)
882 float posx,
float posy)
894 const double min_delta_time = 1.0 / 60.0;
895 const float min_dist2 = 4.0f;
896 const gboolean force_rebuild
905 if(elapsed_time < min_delta_time && (delta_x * delta_x + delta_y * delta_y) < min_dist2)
938 mask_form->
points = g_list_remove(mask_form->
points, brush_node);
965 if(parent_id <= 0)
return 1;
975 const int edit_mode = mask_gui->
edit_mode;
980 for(GList *forms = visible_form->
points; forms; forms = g_list_next(forms))
985 visible_form->
points = g_list_remove(visible_form->
points, group_entry);
1009 for(GList *form_node = dev->
forms; form_node; form_node = g_list_next(form_node))
1014 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
1017 if(group_entry && group_entry->
formid == formid)
1020 if(count > 1)
goto done;
1037 const gboolean internal_masks
1039 && ((
module->flags() & IOP_FLAGS_INTERNAL_MASKS) == IOP_FLAGS_INTERNAL_MASKS);
1041 int response = GTK_RESPONSE_YES;
1042 if(use_count <= 1 && !internal_masks)
1045 if(response == GTK_RESPONSE_CANCEL)
return FALSE;
1049 int next_form_index = -1;
1050 int next_formid = 0;
1054 const int group_length = g_list_length(visible_form->
points);
1064 GList *next_node = g_list_next(selected_node);
1065 if(
IS_NULL_PTR(next_node)) next_node = g_list_previous(selected_node);
1074 gboolean res =
TRUE;
1075 int signal_parent_id = 0;
1078 if(response == GTK_RESPONSE_NO)
1082 signal_parent_id = parent_id;
1094 const int edit_mode = mask_gui->
edit_mode;
1098 for(GList *forms = visible_form->
points; forms; forms = g_list_next(forms))
1103 visible_form->
points = g_list_remove(visible_form->
points, group_entry);
1123 if(res && next_formid > 0)
1190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit),
TRUE);
1191 gtk_widget_queue_draw(blend_data->
masks_edit);
1199 if(selected_index >= 0)
1220 else if(last_formid > 0)
1263 else if(parent_id > 0)
1282 gui_points->
points = NULL;
1284 gui_points->
border = NULL;
1286 gui_points->
source = NULL;
1311 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
1329 int new_form_id = 100;
1335 mask_form->
formid = new_form_id++;
1340 form_node = g_list_next(form_node);
1348 g_strlcpy(group_form->
name, group_name,
sizeof(group_form->
name));
1358 module->blend_params->mask_id = group_form->formid;
1371 develop->
forms = g_list_append(develop->
forms, mask_form);
1378 develop->
forms = g_list_remove(develop->
forms, mask_form);
1389 guint form_count = 0;
1393 for(GList *form_node = develop->
forms; form_node; form_node = g_list_next(form_node))
1396 if(existing_form->
type == mask_form->
type) form_count++;
1400 gboolean name_exists =
FALSE;
1406 name_exists =
FALSE;
1413 for(GList *form_node = develop->
forms; form_node; form_node = g_list_next(form_node))
1416 if(!strcmp(existing_form->
name, mask_form->
name))
1424 }
while(name_exists);
1454 group_form->
points = g_list_append(group_form->
points, group_entry);
1515 snprintf(dest_form->
name,
sizeof(dest_form->
name), _(
"copy of %s"), base_form->
name);
1524 return dest_form->
formid;
1528 float **point_buffer,
int *point_count,
1529 float **border_buffer,
int *border_count,
1534 border_buffer, border_count, source, module);
1540 int *area_width,
int *area_height,
int *area_pos_x,
int *area_pos_y)
1543 return mask_form->
functions->
get_area(module, pipe, piece, mask_form, area_width, area_height,
1544 area_pos_x, area_pos_y);
1550 int *area_width,
int *area_height,
1551 int *area_pos_x,
int *area_pos_y)
1553 *area_width = *area_height = *area_pos_x = *area_pos_y = 0;
1560 area_pos_x, area_pos_y);
1592 const char *opname =
"flip";
1595 for(GList *module_node = develop->
iop; module_node; module_node = g_list_next(module_node))
1598 if(!strcmp(iop_module->
op, opname))
1600 module = iop_module;
1609 module->init_pipe(module, NULL, &piece);
1610 module->commit_params(module, module->default_params, NULL, &piece);
1615 GList *point_node = mask_form->
points;
1623 module->distort_backtransform(module, NULL, &piece, circle->center, 1);
1627 for(; point_node; point_node = g_list_next(point_node))
1631 module->distort_backtransform(module, NULL, &piece, polygon_node->node, 1);
1632 module->distort_backtransform(module, NULL, &piece, polygon_node->ctrl1, 1);
1633 module->distort_backtransform(module, NULL, &piece, polygon_node->ctrl2, 1);
1640 module->distort_backtransform(module, NULL, &piece, gradient->center, 1);
1652 module->distort_backtransform(module, NULL, &piece, ellipse->center, 1);
1656 const float y = ellipse->
radius[0];
1663 for(; point_node; point_node = g_list_next(point_node))
1667 module->distort_backtransform(module, NULL, &piece, brush_node->node, 1);
1668 module->distort_backtransform(module, NULL, &piece, brush_node->ctrl1, 1);
1669 module->distort_backtransform(module, NULL, &piece, brush_node->ctrl2, 1);
1676 module->distort_backtransform(module, NULL, &piece, mask_form->source, 1);
1687 const float image_width = (float)image->
width;
1688 const float image_height = (float)image->
height;
1690 const float crop_x = (float)image->
crop_x;
1691 const float crop_y = (float)image->
crop_y;
1702 coords[0] = ((coords[0] * crop_width) + crop_x) / image_width;
1703 coords[1] = ((coords[1] * crop_height) + crop_y) / image_height;
1707 size_t coords_count)
1709 const float image_width = (float)image->
width;
1710 const float image_height = (float)image->
height;
1720 const float crop_min =
MIN(crop_width, crop_height);
1721 const float image_min =
MIN(image_width, image_height);
1722 for(
size_t coord_index = 0; coord_index < coords_count; coord_index++)
1723 coords[coord_index] = ((coords[coord_index] * crop_min)) / image_min;
1745 GList *point_node = mask_form->
points;
1759 for(; point_node; point_node = g_list_next(point_node))
1783 for(; point_node; point_node = g_list_next(point_node))
1817 GList *point_node = mask_form->
points;
1843 GList *point_node = mask_form->
points;
1868 GList *point_node = mask_form->
points;
1888 if(old_version == 1 && new_version == 2)
1894 if(old_version == 1 && new_version == 6)
1902 else if(old_version == 2 && new_version == 6)
1909 else if(old_version == 3 && new_version == 6)
1915 else if(old_version == 4 && new_version == 6)
1920 else if(old_version == 5 && new_version == 6)
1978 while(develop->
forms)
1984 develop->
forms = forms_tmp;
1987 for(GList *form_node = develop->
forms; form_node; form_node = g_list_next(form_node))
1996 for(; form_list; form_list = g_list_next(form_list))
1999 if(mask_form->
formid == form_id)
return mask_form;
2014 for(GList *module_node = g_list_first(develop->
iop); module_node; module_node = g_list_next(module_node))
2017 if(strcmp(module->op,
"mask_manager") == 0)
2029 return forms_snapshot;
2033 const int used_count)
2035 for(
int used_index = 0; used_index < used_count; used_index++)
2037 if(used_form_ids[used_index] == 0)
2039 used_form_ids[used_index] = form_id;
2042 if(used_form_ids[used_index] == form_id)
break;
2048 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
2062 const guint form_count = g_list_length(develop_src->
forms);
2063 if(form_count == 0)
return 0;
2065 int *used_form_ids = calloc(form_count,
sizeof(
int));
2069 used_form_ids, form_count);
2071 for(
int form_index = 0; form_index < (int)form_count && used_form_ids[form_index] > 0; form_index++)
2077 used_form_ids[form_index]);
2080 develop_dest->
forms = g_list_remove(develop_dest->
forms, existing_form);
2081 develop_dest->
allforms = g_list_append(develop_dest->
allforms, existing_form);
2090 develop_dest->
forms = g_list_append(develop_dest->
forms, new_form);
2094 fprintf(stderr,
"[dt_masks_copy_used_forms_for_module] form %i not found in source image\n",
2095 used_form_ids[form_index]);
2107 int previous_num = -1;
2109 sqlite3_stmt *statement = NULL;
2113 "SELECT imgid, formid, form, name, version, points, points_count, source, num "
2114 "FROM main.masks_history WHERE imgid = ?1 ORDER BY num",
2115 -1, &statement, NULL);
2119 while(sqlite3_step(statement) == SQLITE_ROW)
2126 const int form_id = sqlite3_column_int(statement, 1);
2127 const int history_num = sqlite3_column_int(statement, 8);
2130 mask_form->
formid = form_id;
2131 const char *form_name = (
const char *)sqlite3_column_text(statement, 3);
2132 g_strlcpy(mask_form->
name, form_name,
sizeof(mask_form->
name));
2133 mask_form->
version = sqlite3_column_int(statement, 4);
2134 mask_form->
points = NULL;
2135 const int point_count = sqlite3_column_int(statement, 6);
2136 memcpy(mask_form->
source, sqlite3_column_blob(statement, 7),
sizeof(
float) * 2);
2141 const char *
const point_buffer = (
char *)sqlite3_column_blob(statement, 5);
2143 for(
int point_index = 0; point_index < point_count; point_index++)
2145 char *point_data = (
char *)malloc(point_struct_size);
2146 memcpy(point_data, point_buffer + point_index * point_struct_size, point_struct_size);
2147 mask_form->
points = g_list_append(mask_form->
points, point_data);
2160 "[_dev_read_masks_history] %s (imgid `%i'): mask version mismatch: history is %d, dt %d.\n",
2172 if(previous_num != history_num)
2174 history_item = NULL;
2175 for(GList *history_node = g_list_first(develop->
history); history_node; history_node = g_list_next(history_node))
2178 if(history_entry->
num == history_num)
2180 history_item = history_entry;
2184 previous_num = history_num;
2194 history_item->
forms = g_list_append(history_item->
forms, mask_form);
2198 "[_dev_read_masks_history] can't find history entry %i while adding mask %s(%i)\n",
2199 history_num, mask_form->
name, form_id);
2203 sqlite3_finalize(statement);
2212 sqlite3_stmt *statement = NULL;
2215 mask_form->
name, mask_form->
type, image_id);
2220 "INSERT INTO main.masks_history (imgid, num, formid, form, name, "
2221 "version, points, points_count,source) VALUES "
2222 "(?1, ?9, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
2223 -1, &statement, NULL);
2235 const guint point_count = g_list_length(mask_form->
points);
2236 char *
const restrict point_buffer = (
char *)malloc(point_count * point_struct_size);
2237 int buffer_offset = 0;
2238 for(GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
2240 memcpy(point_buffer + buffer_offset, point_node->data, point_struct_size);
2241 buffer_offset += point_struct_size;
2244 point_count * point_struct_size, SQLITE_TRANSIENT);
2246 sqlite3_step(statement);
2247 sqlite3_finalize(statement);
2256 mask_form->
points = NULL;
2310 const uint32_t
state,
2311 const gboolean shape_was_selected)
2317 const gboolean prev_node_selected = mask_gui->
node_selected;
2319 const gboolean prev_form_selected = mask_gui->
form_selected;
2339 if(prev_node_selected)
2348 if(prev_node_selected)
2368 if(!shape_was_selected)
return;
2386 float point[2] = { (float)
x, (
float)y };
2433 dispatch_form, parent_id, mask_gui, form_index);
2465 state, dispatch_form, parent_id, mask_gui, form_index);
2474 if(selected_group_entry)
2479 if(mask_gui && !mask_gui->
creation && button == 1)
2492 int button,
int event_type, uint32_t
state)
2518 gboolean return_val =
FALSE;
2521 button, event_type,
state,
2522 dispatch_form, parent_id, mask_gui, form_index);
2528 ? (prev_group_selected >= 0 && prev_group_selected == form_index)
2529 : prev_any_selected;
2532 if(button == 3 && !return_val)
2543 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
2559 gboolean return_value =
FALSE;
2569 parent_id, mask_gui, form_index);
2584 case GDK_KEY_Escape:
2589 case GDK_KEY_Delete:
2605 return return_value;
2609 int scroll_up, uint32_t key_state,
int scrolling_delta)
2625 int scroll_flow = (scrolling_delta < 0) ? -scrolling_delta : scrolling_delta;
2643 scroll_increases ? 1 : 0, scroll_flow,
2644 key_state, dispatch_form, parent_id, mask_gui, form_index,
2651 "[masks] scroll: ret=%d hinted=%d form=%p type=%d gui=%p group_selected=%d flow=%d state=0x%x\n",
2652 result, hinted, (
void *)mask_form, mask_form ? mask_form->
type : -1, (
void *)mask_gui,
2665 const float *point_values = &gui_points->
points[node_index * 6];
2666 return (point_values[0 + 2] == point_values[2 + 2]
2667 && point_values[1 + 2] == point_values[3 + 2]);
2686 const float *points,
const int points_count,
const float zoom_scale,
2688 const gboolean is_closed_shape,
2689 const float offset_factor,
2693 result[0] = ray_1[0];
2694 result[1] = ray_1[1];
2696 const int available_points = points_count - first_pt;
2697 if(available_points < 2)
return;
2699 const float ray2_x = ray_2[0];
2700 const float ray2_y = ray_2[1];
2701 const float ray_center_x = 0.5f * (ray_1[0] + ray_2[0]);
2702 const float ray_center_y = 0.5f * (ray_1[1] + ray_2[1]);
2703 const float dir_x = ray_1[0] - ray_2[0];
2704 const float dir_y = ray_1[1] - ray_2[1];
2705 float min_s = FLT_MAX;
2707 const float inv_dir_len =
f_inv_sqrtf(dir_x * dir_x + dir_y * dir_y);
2708 const float ux = dir_x * inv_dir_len;
2709 const float uy = dir_y * inv_dir_len;
2712 const int segment_count = (available_points - 1) + ((is_closed_shape) ? 1 : 0);
2713 for(
int seg = 0; seg < segment_count; seg++)
2716 const int i = first_pt + seg;
2717 const int j = (
i + 1 < points_count) ? (
i + 1) : first_pt;
2718 const float x3 = points[
i * 2];
2719 const float y3 = points[
i * 2 + 1];
2720 const float x4 = points[j * 2];
2721 const float y4 = points[j * 2 + 1];
2724 const float dx = x4 - x3;
2725 const float dy = y4 - y3;
2726 const float det = dx * (-dir_y) + dy * dir_x;
2727 if(det > -1e-8f && det < 1e-8f)
continue;
2728 const float inv_det = 1.0f / det;
2730 const float segment_param = ((ray2_x - x3) * (-dir_y) + (ray2_y - y3) * dir_x) * inv_det;
2731 const float ray_param = ((x3 - ray2_x) * dy - (y3 - ray2_y) * dx) * inv_det;
2732 if(segment_param < 0.0f || segment_param > 1.0f || ray_param <= 0.0f || ray_param >= min_s)
continue;
2735 const float ix = ray2_x + ray_param * dir_x;
2736 const float iy = ray2_y + ray_param * dir_y;
2739 const float to_center = (ray_center_x - ix) * ux + (ray_center_y - iy) * uy;
2740 const float side_sign = (to_center >= 0.0f) ? 1.0f : -1.0f;
2741 result[0] = ix + side_sign * offset * ux;
2742 result[1] = iy + side_sign * offset * uy;
2747 const int node_count,
const float zoom_scale,
2758 const float main[2] = { center_point->
main.
x, center_point->
main.
y };
2759 const float source[2] = { center_point->
source.
x, center_point->
source.
y };
2763 int first_point_index = 2;
2765 first_point_index = 10;
2767 first_point_index = node_count * 3;
2773 attach_points_count /= 2;
2774 attach_source_count /= 2;
2777 float head[2] = { 0.0f, 0.0f };
2778 float tail[2] = { 0.0f, 0.0f };
2782 zoom_scale, first_point_index, is_closed_shape, 1.f, head);
2786 zoom_scale, first_point_index, is_closed_shape, 0.5f, tail);
2788 const gboolean selected = (mask_gui->
group_selected == form_index)
2790 gboolean draw_tail =
TRUE;
2799 const float pts[4] = { tail[0], tail[1], source[0], source[1] };
2805 const float origin_pt[2] = {
main[0],
main[1] };
2813 head[0] = 0.5f * (
main[0] + source[0]);
2814 head[1] = 0.5f * (
main[1] + source[1]);
2817 const float arrow_len_sq = sqf(tail[0] - head[0]) + sqf(tail[1] - head[1]);
2818 draw_tail = arrow_len_sq > 1e-6f && !overlap;
2822 const float angle = is_open_shape ? atan2f(tail[1] - head[1], tail[0] - head[0])
2823 : atan2f(head[1] -
main[1], head[0] -
main[0]);
2835 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1);
2838 cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1);
2842 cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1);
2852 const int rendered_node_count = node_count + !mask_gui->
creation;
2853 const gboolean shape_selected = (mask_gui->
group_selected == form_index)
2857 rendered_node_count, draw_shape_func);
2862 const float *points,
const int points_count,
const int node_count,
2863 const float zoom_scale)
2866 if(node_count <= 0 || points_count <= node_count * 3 + 6)
return;
2868 const int total_coords = points_count * 2;
2869 if(total_coords <= (node_count * 6 + 1))
return;
2871 const gboolean group_selected = (mask_gui->
group_selected == form_index);
2873 int show_segment_index = 1;
2874 int current_segment_index = 0;
2875 cairo_move_to(cr, points[node_count * 6], points[node_count * 6 + 1]);
2877 for(
int point_index = node_count * 3; point_index < points_count; point_index++)
2879 const int coord_index = point_index * 2;
2880 if((coord_index + 1) >= total_coords)
break;
2882 const double coord_x = points[coord_index];
2883 const double coord_y = points[coord_index + 1];
2884 cairo_line_to(cr, coord_x, coord_y);
2886 const int segment_coord_index = show_segment_index * 6;
2887 if((segment_coord_index + 3) >= total_coords)
continue;
2889 const double segment_x = points[segment_coord_index + 2];
2890 const double segment_y = points[segment_coord_index + 3];
2891 if(coord_x == segment_x && coord_y == segment_y)
2893 const gboolean seg_is_selected = group_selected
2895 == current_segment_index);
2896 const gboolean all_selected = group_selected
2900 if(mask_gui->
creation && current_segment_index == node_count - 2)
2904 CAIRO_LINE_CAP_BUTT);
2906 show_segment_index = (show_segment_index + 1) % node_count;
2907 current_segment_index++;
2910 if(mask_gui->
creation && current_segment_index >= node_count - 1)
break;
2923 cairo_t *cr,
const float zoom_scale,
2936 for(GList *formid_node = creation_gui->
creation_formids; formid_node; formid_node = g_list_next(formid_node))
2938 const int formid = GPOINTER_TO_INT(formid_node->data);
2945 const guint point_count = g_list_length(session_form->
points);
2946 draw_gui.
type = session_form->
type;
2961 int32_t pointerx, int32_t pointery)
2970 int buffer_width = 0;
2971 int buffer_height = 0;
2974 if(buffer_width < 1.0 || buffer_height < 1.0)
return;
2979 cairo_surface_t *overlay = NULL;
2980 cairo_t *mask_draw = NULL;
2981 cairo_surface_t *target = cairo_get_target(cr);
2982 double sx = 1.0, sy = 1.0;
2983 cairo_surface_get_device_scale(target, &sx, &sy);
2984 overlay = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR_ALPHA, (
int)ceil(
width * sx),
2986 cairo_surface_set_device_scale(overlay, sx, sy);
2987 mask_draw = cairo_create(overlay);
2994 cairo_save(mask_draw);
2999 cairo_restore(mask_draw);
3000 cairo_destroy(mask_draw);
3001 cairo_surface_destroy(overlay);
3017 const guint point_count = g_list_length(mask_form->
points);
3021 cairo_restore(mask_draw);
3025 cairo_identity_matrix(cr);
3026 cairo_set_source_surface(cr, overlay, 0.0, 0.0);
3030 cairo_destroy(mask_draw);
3031 cairo_surface_destroy(overlay);
3089 gboolean is_registered =
FALSE;
3090 gboolean is_registered_for_cleanup =
FALSE;
3092 for(
const GList *form_node =
darktable.
develop->
forms; form_node; form_node = g_list_next(form_node))
3094 if(form_node->data == old_form)
3096 is_registered =
TRUE;
3103 if(form_node->data == old_form)
3105 is_registered_for_cleanup =
TRUE;
3131 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit), 0);
3138 for(GList *module_node =
darktable.
develop->
iop; module_node; module_node = g_list_next(module_node))
3142 && !IS_NULL_PTR(module->blend_data))
3144 dt_iop_gui_blend_data_t *blend_data = (dt_iop_gui_blend_data_t *)module->blend_data;
3148 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->masks_edit),
FALSE);
3149 gtk_widget_queue_draw(blend_data->masks_edit);
3181 if(
value && mask_form)
3187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit),
3198 module->blend_params->mask_id = 0;
3258 for(GList *group_node = source_group->
points; group_node; group_node = g_list_next(group_node))
3285 const guint forms_count = g_list_length(module->dev->forms);
3286 const guint iop_count = g_list_length(module->dev->iop);
3287 guint combo_capacity = 5 + forms_count + iop_count;
3300 int combo_index = 0;
3301 combo_ids[combo_index] = 0;
3306 for(GList *form_node = module->dev->forms; form_node; form_node = g_list_next(form_node))
3310 || mask_form->
formid == module->blend_params->mask_id)
3320 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3333 combo_ids[combo_index] = mask_form->
formid;
3341 for(GList *module_node = module->dev->iop; module_node; module_node = g_list_next(module_node))
3353 combo_ids[combo_index] = -1 * iop_index;
3367 if(selection_index == 0)
return;
3368 if(selection_index > 0)
3371 const guint iop_count = g_list_length(module->
dev->
iop);
3373 if(selection_value == -1000000)
3378 else if(selection_value == -2000001)
3383 else if(selection_value == -2000002)
3388 else if(selection_value == -2000016)
3393 else if(selection_value == -2000032)
3398 else if(selection_value == -2000064)
3403 else if(selection_value < 0)
3406 selection_value = -1 * selection_value - 1;
3407 if(selection_value < (
int)iop_count)
3418 else if(selection_value > 0)
3435 int form_id = mask_form->
formid;
3442 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3445 if(group_entry->
formid == form_id)
3448 group_form->
points = g_list_remove(group_form->
points, group_entry);
3496 GList *shapes = iop_group->
points;
3500 if(group_entry->
formid == form_id)
3504 iop_group->
points = g_list_remove(iop_group->
points, group_entry);
3506 shapes = iop_group->
points;
3509 shapes = g_list_next(shapes);
3525 if(existing_form->
formid == form_id)
3563 float center_point[2];
3568 mask_form->
area = area;
3571 "[masks] gravity center updated: form=%p id=%d type=0x%x ok=%d center=(%f,%f), area=%f\n",
3572 (
void *)mask_form, mask_form->
formid, mask_form->
type, ok,
3591 float center[2] = { 0.0f, 0.0f };
3599 dev->
roi.
x = center[0];
3600 dev->
roi.
y = center[1];
3635 flow, mask_gui, module);
3636 if(isnan(result))
return NAN;
3676 return current * powf(amount, (
float)flow);
3678 return current + amount * (float)flow;
3691 return current * scale_amount;
3693 return current + offset_amount;
3701 float value_min,
float value_max,
3704 gchar *config_key = NULL;
3705 if(!strcmp(feature,
"opacity"))
3706 config_key = g_strdup_printf(
"plugins/darkroom/%s_opacity",
_get_mask_plugin(mask_form));
3708 config_key = g_strdup_printf(
"plugins/darkroom/%s/%s/%s",
3711 if(!g_strcmp0(feature,
"rotation")) flow = (flow > 1) ? (flow - 1) * 5 : flow;
3715 if(!g_strcmp0(feature,
"rotation"))
3718 if(updated_value > value_max) updated_value = fmodf(updated_value, value_max);
3719 else if(updated_value < value_min)
3720 updated_value = value_max - fmodf(value_min - updated_value, value_max);
3722 else updated_value =
MAX(value_min,
MIN(updated_value, value_max));
3727 return updated_value;
3731 float value_min,
float value_max,
3733 const char *toast_fmt,
float toast_scale)
3736 value_min, value_max, increment, flow);
3737 if(!
IS_NULL_PTR(toast_fmt) && toast_fmt[0] !=
'\0')
3747 for(
const GList *point_node = base_form->
points; point_node; point_node = g_list_next(point_node))
3749 const void *point_data = point_node->data;
3751 void *point_copy = malloc(node_size);
3753 memcpy(point_copy, point_data, node_size);
3754 dest_form->
points = g_list_append(dest_form->
points, point_copy);
3765 float amount = scroll_up ? 0.02f : -0.02f;
3768 return !isnan(changed);
3777 guint group_index = 0;
3778 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3781 if(entry->
formid == form_id)
3783 group_entry = entry;
3792 const guint group_length = g_list_length(group_form->
points);
3793 if(!move_up && group_index == 0)
return;
3794 if(move_up && group_index == group_length - 1)
return;
3796 group_form->
points = g_list_remove(group_form->
points, group_entry);
3801 group_form->
points = g_list_insert(group_form->
points, group_entry, group_index);
3809 if(group_form->
formid == form_id)
return 1;
3810 int nested_count = 0;
3811 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3820 return nested_count;
3837 group_form->
points = g_list_append(group_form->
points, group_entry);
3851 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3868 dest_group->
points = g_list_append(dest_group->
points, new_entry);
3882 hash =
dt_hash(hash, (
char *)&mask_form->
formid,
sizeof(
int));
3884 hash =
dt_hash(hash, (
char *)&mask_form->
source,
sizeof(
float) * 2);
3886 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
3895 hash =
dt_hash(hash, (
char *)&group_entry->
state,
sizeof(
int));
3896 hash =
dt_hash(hash, (
char *)&group_entry->
opacity,
sizeof(
float));
3915 for(
int used_index = 0; used_index < used_count; used_index++)
3917 if(used_form_ids[used_index] == 0)
3920 used_form_ids[used_index] = form_id;
3923 if(used_form_ids[used_index] == form_id)
break;
3930 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
3941 int masks_removed = 0;
3942 GList *forms = *forms_list;
3945 guint form_count = g_list_length(forms);
3946 int *used_form_ids = calloc(form_count,
sizeof(
int));
3949 int history_index = 0;
3950 for(GList *history_node = history_list; history_node && history_index < history_end;
3951 history_node = g_list_next(history_node))
3967 GList *shape_node = forms;
3972 for(
int used_index = 0; used_index < form_count; used_index++)
3974 if(used_form_ids[used_index] == mask_form->
formid)
3979 if(used_form_ids[used_index] == 0)
break;
3982 shape_node = g_list_next(shape_node);
3986 forms = g_list_remove(forms, mask_form);
3995 *forms_list = forms;
3997 return masks_removed;
4011 int history_count = g_list_length(history_list);
4012 int history_end = history_count;
4013 for(
const GList *history_node = g_list_last(history_list); history_node;
4014 history_node = g_list_previous(history_node))
4020 history_end = history_count - 1;
4039 GList *forms = NULL;
4040 int history_index = 0;
4041 for(
const GList *history_node = g_list_first(develop->
history);
4043 history_node = g_list_next(history_node))
4047 if(history_item->
forms) forms = history_item->
forms;
4067 const float *form_points,
int form_points_start,
int form_points_count)
4070 if(form_points_count <= 2 + form_points_start)
return -1;
4071 if(form_points_start < 0 || form_points_start >= form_points_count)
return -1;
4073 const int start_index = form_points_start;
4074 for(
int test_index = 0; test_index < test_point_count; test_index++)
4076 int intersection_count = 0;
4077 const float point_x = test_points[test_index * 2];
4078 const float point_y = test_points[test_index * 2 + 1];
4079 int visited_points = 0;
4081 for(
int i = form_points_start, next = start_index + 1;
i < form_points_count;)
4086 if(next < start_index || next >= form_points_count)
break;
4087 if(++visited_points > form_points_count - start_index + 1)
break;
4089 const float y1 = form_points[
i * 2 + 1];
4090 const float y2 = form_points[next * 2 + 1];
4093 if(isnan(form_points[next * 2]))
4095 const int jump_index = isnan(y2) ? start_index : (int)y2;
4096 if(jump_index == next || jump_index < start_index || jump_index >= form_points_count)
break;
4101 if(((point_y <= y2 && point_y > y1) || (point_y >= y2 && point_y < y1))
4102 && (form_points[
i * 2] > point_x))
4103 intersection_count++;
4105 if(next == start_index)
break;
4108 if(next >= form_points_count) next = start_index;
4111 if(intersection_count & 1)
return test_index;
4124 const int selected_formid =
IS_NULL_PTR(selected_form) ? 0 : selected_form->
formid;
4127 module = darktable.develop->gui_module;
4129 if(!
IS_NULL_PTR(module) && module->masks_selection_changed)
4130 module->masks_selection_changed(module, selected_formid);
4147 fprintf(stderr,
"[dt_masks_set_source_pos_initial_state] unknown state for setting masks position type\n");
4170 const float xx = mask_gui->
pos[0];
4171 const float yy = mask_gui->
pos[1];
4185 fprintf(stderr,
"[dt_masks_set_source_pos_initial_value] unsupported masks type when calculating source position initial value\n");
4201 mask_form->
source[0] = source_points[0];
4202 mask_form->
source[1] = source_points[1];
4223 mask_form->
source[0] = source_points[0];
4224 mask_form->
source[1] = source_points[1];
4227 fprintf(stderr,
"[dt_masks_set_source_pos_initial_value] unknown source position type\n");
4236 const float initial_ypos,
const float xpos,
const float ypos,
4237 float *pos_x,
float *pos_y,
const int adding)
4239 float source_x = 0.0f;
4240 float source_y = 0.0f;
4260 fprintf(stderr,
"[dt_masks_calculate_source_pos_origin] unsupported masks type when calculating source position value\n");
4273 source_x = xpos + mask_gui->
pos_source[0] - initial_xpos;
4274 source_y = ypos + mask_gui->
pos_source[1] - initial_ypos;
4284 fprintf(stderr,
"[dt_masks_calculate_source_pos_origin] unknown source position type for setting source position value\n");
4301 const float center_x = center[0];
4302 const float center_y = center[1];
4305 const float anchor_x = anchor[0];
4306 const float anchor_y = anchor[1];
4307 const float angle_current = atan2f(anchor_y - center_y, anchor_x - center_x);
4310 const float delta_x = mask_gui->
delta[0];
4311 const float delta_y = mask_gui->
delta[1];
4312 const float angle_prev = atan2f(delta_y - center_y, delta_x - center_x);
4315 float delta_angle = angle_current - angle_prev;
4316 float angle = atan2f(sinf(delta_angle), cosf(delta_angle));
4319 float test_points[8] = { center_x, center_y, anchor_x , anchor_y,
4320 center_x + 10.0f, center_y, center_x, center_y + 10.0f };
4322 float check_angle = atan2f(test_points[7] - test_points[1], test_points[6] - test_points[0])
4323 - atan2f(test_points[5] - test_points[1], test_points[4] - test_points[0]);
4325 check_angle = atan2f(sinf(check_angle), cosf(check_angle));
4328 if(check_angle < 0.0f) angle = -angle;
4331 mask_gui->
delta[0] = anchor_x;
4332 mask_gui->
delta[1] = anchor_y;
4334 return angle /
M_PI * 180.0f;
4391 group_entry->
state = (group_entry->
state & ~DT_MASKS_STATE_IS_COMBINE_OP) | apply_state;
static double dist(double x1, double y1, double x2, double y2)
int dt_bauhaus_combobox_get(GtkWidget *widget)
int dt_bauhaus_combobox_length(GtkWidget *widget)
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
void dt_masks_iop_update(dt_iop_module_t *module)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_toast_log(const char *msg,...)
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
#define dt_control_queue_cursor(cursor)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
static void dt_free_gpointer(gpointer ptr)
#define dt_pixelpipe_cache_free_align(mem)
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
static const dt_aligned_pixel_simd_t value
static double dt_get_wtime(void)
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static gboolean g_list_shorter_than(const GList *list, unsigned len)
sqlite3 * dt_database_get(const dt_database_t *db)
#define DT_DEBUG_SQLITE3_BIND_BLOB(a, b, c, d, e)
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
#define dt_dev_add_history_item(dev, module, enable, redraw)
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
void dt_dev_pixelpipe_change_zoom_main(dt_develop_t *dev)
static void dt_masks_legacy_params_v2_to_v3_transform(const dt_image_t *image, float *coords)
gboolean dt_masks_remove_or_delete(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
If the form to remove is used once, ask to the user if he wants to delete it from the list or just re...
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const float *points, const int points_count, const int node_count, const float zoom_scale)
const char * _get_mask_type(dt_masks_form_t *mask_form)
void dt_masks_events_post_expose(struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const int node_count, const float zoom_scale, struct dt_masks_gui_center_point_t *center_point, const shape_draw_function_t *draw_shape_func)
Draw the source for a correction mask.
void dt_masks_gui_set_dragging(dt_masks_form_gui_t *gui)
static int _find_in_group(dt_masks_form_t *group_form, int form_id)
int dt_masks_point_in_form_exact(const float *test_points, int test_point_count, const float *form_points, int form_points_start, int form_points_count)
Check whether any 2D point in pts[] lies inside the form points[].
int dt_masks_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event)
void dt_masks_clear_form_gui(dt_develop_t *develop)
static void _menu_add_exist(dt_iop_module_t *module, int form_id)
static gboolean _dt_masks_events_should_update_hover_on_move(dt_masks_form_gui_t *mask_gui)
void dt_masks_remove_node(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index, int node_index)
static void * _dup_masks_form_cb(const void *formdata, gpointer user_data)
uint64_t dt_masks_group_get_hash(uint64_t hash, dt_masks_form_t *mask_form)
gboolean dt_masks_point_is_within_radius(const float point_x, const float point_y, const float center_x, const float center_y, const float squared_radius)
Check whether a point lies within a squared radius of a center.
float dt_masks_get_set_conf_value(dt_masks_form_t *mask_form, char *feature, float new_value, float value_min, float value_max, dt_masks_increment_t increment, int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
static void _menu_no_masks(struct dt_iop_module_t *module)
void dt_masks_group_ungroup(dt_masks_form_t *dest_group, dt_masks_form_t *group_form)
int dt_masks_form_change_opacity(dt_masks_form_t *mask_form, int parent_id, int scroll_up, const int flow)
int dt_masks_group_index_from_formid(const dt_masks_form_t *group_form, int form_id)
void dt_masks_gui_form_remove(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
static int dt_masks_legacy_params_v3_to_v4(dt_develop_t *develop, void *params)
static void _set_cursor_shape(dt_masks_form_gui_t *mask_gui)
void dt_masks_gui_form_test_create(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, dt_iop_module_t *module)
void dt_masks_gui_reset_dragging(dt_masks_form_gui_t *gui)
static void _apply_gui_button_pressed_state(dt_masks_form_gui_t *mask_gui, const int button, const uint32_t state, const gboolean shape_was_selected)
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module, float posx, float posy)
static void dt_masks_legacy_params_v2_to_v3_transform_only_rescale(const dt_image_t *image, float *coords, size_t coords_count)
void dt_masks_reset_form_gui(void)
void dt_masks_free_form(dt_masks_form_t *mask_form)
void dt_masks_replace_current_forms(dt_develop_t *develop, GList *forms)
int dt_masks_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which)
static int _masks_gui_form_group_use_count(const dt_develop_t *dev, const int formid)
dt_masks_form_group_t * dt_masks_form_get_selected_group(const dt_masks_form_t *mask_form, const dt_masks_form_gui_t *mask_gui)
Get the selected group entry from the GUI selection index.
int dt_masks_events_button_released(struct dt_iop_module_t *module, double x, double y, int button, uint32_t state)
static gboolean _dt_masks_events_group_update_selection(dt_masks_form_t *group_form, dt_masks_form_gui_t *mask_gui)
Update group selection from the current cached cursor before leaf dispatch.
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_gui)
static int dt_masks_legacy_params_v5_to_v6(dt_develop_t *develop, void *params)
void dt_masks_soft_reset_form_gui(dt_masks_form_gui_t *mask_gui)
static void _dt_masks_events_set_current_pos(const double x, const double y, dt_masks_form_gui_t *mask_gui)
Convert the GTK/Cairo widget cursor once for the full mask event chain.
dt_masks_form_group_t * dt_masks_form_get_selected_group_live(const dt_masks_form_t *mask_form, const dt_masks_form_gui_t *mask_gui)
Resolve a "live" selected group entry, even if GUI selection is stale.
void dt_masks_init_form_gui(dt_masks_form_gui_t *mask_gui)
void dt_masks_select_form(struct dt_iop_module_t *module, dt_masks_form_t *selected_form)
Select or clear the current mask form, notifying the owning module if needed.
static void _menu_add_shape(struct dt_iop_module_t *module, dt_masks_type_t type)
void dt_masks_append_form(dt_develop_t *develop, dt_masks_form_t *mask_form)
static dt_masks_form_t * _dt_masks_events_get_dispatch_form(dt_masks_form_t *visible_form, const dt_masks_form_gui_t *mask_gui, dt_masks_form_group_t **group_entry, int *parent_id, int *form_index)
Resolve the concrete form that should receive an event.
void dt_masks_duplicate_points(const dt_masks_form_t *base_form, dt_masks_form_t *dest_form, size_t node_size)
Duplicate a points list for a mask using a fixed node size.
float dt_masks_rotate_with_anchor(dt_develop_t *develop, const float anchor[2], const float center[2], dt_masks_form_gui_t *mask_gui)
Compute rotation angle (degrees) around a center using an anchor point.
int dt_masks_copy_used_forms_for_module(dt_develop_t *develop_dest, dt_develop_t *develop_src, const dt_iop_module_t *source_module)
dt_iop_module_t * dt_masks_get_mask_manager(dt_develop_t *develop)
float dt_masks_form_get_interaction_value(dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction)
dt_masks_form_group_t * dt_masks_group_add_form(dt_masks_form_t *group_form, dt_masks_form_t *mask_form)
static gboolean _dt_masks_events_cursor_over_form(const dt_masks_form_t *dispatch_form, dt_masks_form_gui_t *mask_gui, const int form_index)
GList * dt_masks_dup_forms_deep(GList *form_list, dt_masks_form_t *replacement_form)
Duplicate the list of forms, replacing a single item by formid match.
void dt_masks_read_masks_history(dt_develop_t *develop, const int32_t image_id)
static dt_masks_form_t * _group_create(dt_develop_t *develop, dt_iop_module_t *module, dt_masks_type_t group_type)
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gui_points, const int node_index)
returns wether a node is a corner or not. A node is a corner if its 2 control handles are at the same...
int dt_masks_version(void)
dt_masks_form_t * dt_masks_get_from_id_ext(GList *form_list, int form_id)
static gboolean _masks_remove_shape(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Remove a shape from the GUI and free its resources.
dt_masks_form_t * dt_masks_dup_masks_form(const dt_masks_form_t *mask_form)
Deep-copy a mask form, including its points list.
dt_masks_form_t * dt_masks_get_visible_form(const dt_develop_t *dev)
Return the currently visible form used by the masks GUI.
int dt_masks_events_mouse_leave(struct dt_iop_module_t *module)
gboolean dt_masks_gui_remove(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, const int parent_id)
remove a mask shape or node form from the GUI. This function is used with a popupmenu "Delete" action...
static gboolean _set_hinter_message(dt_masks_form_gui_t *mask_gui, const dt_masks_form_t *mask_form)
Build and display the on-canvas hint message for masks interactions.
gboolean dt_masks_gui_is_dragging(const dt_masks_form_gui_t *gui)
int dt_masks_legacy_params(dt_develop_t *develop, void *params, const int old_version, const int new_version)
static void _dt_masks_find_best_attachment_point(const float ray_1[2], const float ray_2[2], const float *points, const int points_count, const float zoom_scale, const int first_pt, const gboolean is_closed_shape, const float offset_factor, float result[2])
Find the best attachment point on the shape contour for a ray crossing the form.
int dt_masks_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int scroll_up, uint32_t key_state, int scrolling_delta)
static int dt_masks_legacy_params_v2_to_v3(dt_develop_t *develop, void *params)
static int dt_masks_legacy_params_v1_to_v2(dt_develop_t *develop, void *params)
dt_masks_form_t * dt_masks_create(dt_masks_type_t type)
float dt_masks_form_set_interaction_value(dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction, float value, dt_masks_increment_t increment, int flow, dt_masks_form_gui_t *mask_gui, dt_iop_module_t *module)
int dt_masks_get_points_border(dt_develop_t *develop, dt_masks_form_t *mask_form, float **point_buffer, int *point_count, float **border_buffer, int *border_count, int source, dt_iop_module_t *module)
void dt_masks_iop_combo_populate(GtkWidget *widget, void *data)
void apply_operation(struct dt_masks_form_group_t *group_entry, const dt_masks_state_t apply_state)
Apply a mask state operation on a group entry.
void dt_masks_cleanup_unused(dt_develop_t *develop)
Cleanup unused masks and refresh the current forms snapshot.
void dt_masks_form_move(dt_masks_form_t *group_form, int form_id, int move_up)
void dt_masks_calculate_source_pos_origin(dt_masks_form_gui_t *mask_gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, float *pos_x, float *pos_y, const int adding)
Compute preview-space source position for drawing the clone indicator.
static dt_masks_form_t * _group_from_module(dt_develop_t *develop, dt_iop_module_t *module)
const char * _get_mask_plugin(dt_masks_form_t *mask_form)
float dt_masks_apply_increment(float current, float amount, dt_masks_increment_t increment, int flow)
Apply a scroll increment to a scalar value.
int dt_masks_events_mouse_enter(struct dt_iop_module_t *module)
float dt_masks_apply_increment_precomputed(float current, float amount, float scale_amount, float offset_amount, dt_masks_increment_t increment)
Apply a scroll increment using precomputed scale/offset factors.
gboolean dt_masks_creation_mode_enter(dt_iop_module_t *module, const dt_masks_type_t type)
Enter mask creation mode for a given shape type.
static int _dt_masks_events_update_hover(dt_masks_form_t *dispatch_form, dt_masks_form_gui_t *mask_gui, const int form_index)
static gboolean _dt_masks_events_group_blocks_motion(dt_masks_form_gui_t *mask_gui)
Consume the initial drag motion used to disambiguate scrolling vs dragging in groups.
void dt_masks_write_masks_history_item(const int32_t image_id, const int history_num, dt_masks_form_t *mask_form)
int dt_masks_events_button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure, int button, int event_type, uint32_t state)
void dt_masks_gui_cleanup(dt_develop_t *dev)
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *mask_gui, dt_masks_form_t *mask_form)
Initialize the clone source position based on current GUI state.
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *develop, int form_id)
static int _masks_cleanup_unused(GList **forms_list, GList *history_list, const int history_end)
static float _change_opacity(dt_masks_form_group_t *form_group, float value, const dt_masks_increment_t increment, const int flow)
int dt_masks_form_duplicate(dt_develop_t *develop, int form_id)
static void _check_id(dt_masks_form_t *mask_form)
void dt_masks_set_visible_form(dt_develop_t *dev, dt_masks_form_t *form)
dt_masks_edit_mode_t dt_masks_get_edit_mode(struct dt_iop_module_t *module)
static gboolean _dt_masks_events_flush_rebuild_if_needed(struct dt_iop_module_t *module, dt_masks_form_t *dispatch_form, dt_masks_form_gui_t *mask_gui, const int form_index, const int button)
Flush a deferred throttled rebuild before drag state is reset.
void dt_masks_iop_value_changed_callback(GtkWidget *widget, struct dt_iop_module_t *module)
int dt_masks_center_view_on_form(dt_develop_t *dev, const dt_masks_form_t *mask_form)
Center the darkroom ROI on a mask form gravity center.
void dt_masks_remove_form(dt_develop_t *develop, dt_masks_form_t *mask_form)
void dt_masks_cleanup_unused_from_list(GList *history_list)
Remove unused mask forms from a history list, preserving undo safety.
gboolean dt_masks_form_get_gravity_center(const dt_masks_form_t *mask_form, float center[2], float *area)
static void _masks_fill_used_forms(GList *forms_list, const int form_id, int *used_form_ids, const int used_count)
static dt_masks_form_group_t * _masks_group_find_form(dt_masks_form_t *group_form, const int form_id)
Find a form entry inside a group by form id.
void dt_masks_reset_show_masks_icons(void)
int dt_masks_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *mask_form, int *area_width, int *area_height, int *area_pos_x, int *area_pos_y)
GList * dt_masks_snapshot_current_forms(dt_develop_t *develop, gboolean reset_changed)
static void _masks_draw_creation_session_forms(dt_develop_t *develop, dt_iop_module_t *module, cairo_t *cr, const float zoom_scale, const dt_masks_form_gui_t *creation_gui)
Draw completed shapes from the current creation session.
dt_masks_form_group_t * dt_masks_form_group_from_parentid(int parent_id, int form_id)
Return the group entry for a (parent, form) pair.
void dt_masks_form_update_gravity_center(dt_masks_form_t *mask_form)
static void _set_group_name_from_module(dt_iop_module_t *module, dt_masks_form_t *group_form)
gboolean dt_masks_is_anything_hovered(const dt_masks_form_gui_t *mask_gui)
void dt_masks_iop_use_same_as(dt_iop_module_t *module, dt_iop_module_t *source_module)
int dt_masks_find_closest_handle_common(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, int node_count_override, dt_masks_border_handle_fn border_handle_cb, dt_masks_curve_handle_fn curve_handle_cb, dt_masks_node_position_fn node_position_cb, dt_masks_distance_fn distance_cb, dt_masks_post_select_fn post_select_cb, void *user_data)
Centralized hit-testing for node/handle/segment selection across shapes.
void dt_masks_gui_init(dt_develop_t *dev)
void dt_masks_creation_mode_quit(dt_masks_form_gui_t *mask_gui)
Exit mask creation mode, restoring cursor visibility and resetting GUI state.
void dt_masks_group_update_name(dt_iop_module_t *module)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
void dt_masks_form_delete(struct dt_iop_module_t *module, dt_masks_form_t *group_form, dt_masks_form_t *mask_form)
dt_masks_form_t * dt_masks_create_ext(dt_masks_type_t type)
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *mask_gui, const uint32_t key_state)
Decide initial source positioning mode for clone masks.
void dt_masks_form_gui_points_free(gpointer data)
void dt_masks_change_form_gui(dt_masks_form_t *new_form)
void dt_masks_gui_form_create(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module)
int dt_masks_get_area(dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *mask_form, int *area_width, int *area_height, int *area_pos_x, int *area_pos_y)
float dt_masks_get_set_conf_value_with_toast(dt_masks_form_t *mask_form, const char *feature, float amount, float value_min, float value_max, dt_masks_increment_t increment, int flow, const char *toast_fmt, float toast_scale)
Update a mask configuration value and emit a toast message.
static void _cleanup_unused_recurs(GList *form_list, int form_id, int *used_form_ids, int used_count)
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *mask_gui)
static int dt_masks_legacy_params_v4_to_v5(dt_develop_t *develop, void *params)
void dt_masks_gui_form_save_creation(dt_develop_t *develop, dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui)
Save the form creation right after a shape has been finished drawing.
void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
float dt_dev_get_zoom_level(const dt_develop_t *dev)
void dt_dev_masks_selection_change(dt_develop_t *dev, struct dt_iop_module_t *module, const int selectid, const int throw_event)
void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
Ensure that the current ROI position is within allowed bounds .
gboolean dt_dev_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit the input size within given width/height, centered.
int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
gchar * dt_dev_get_masks_group_name(const struct dt_iop_module_t *module)
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
static void dt_draw_stroke_line(const dt_draw_dash_type_t dash_type, const gboolean source, cairo_t *cr, const gboolean selected, const float zoom_scale, const cairo_line_cap_t line_cap)
Stroke a line with style.
static void dt_draw_arrow(cairo_t *cr, const float zoom_scale, const gboolean selected, const gboolean draw_tail, const dt_draw_dash_type_t dash_style, const float arrow[2], const float tail[2], const float angle)
Draw an arrow with head and, if needed, tail. The length of the arrow head is defined by DT_DRAW_SCAL...
void(* shape_draw_function_t)(cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source)
static void dt_draw_source_shape(cairo_t *cr, const float zoom_scale, const gboolean selected, const float *source_pts, const int source_pts_count, const int nodes_nb, const shape_draw_function_t *draw_shape_func)
#define dt_pthread_rwlock_wrlock
#define dt_pthread_rwlock_unlock
#define dt_pthread_rwlock_rdlock
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
#define DT_GUI_MOUSE_EFFECT_RADIUS
#define DT_PIXEL_APPLY_DPI(value)
@ ORIENTATION_ROTATE_CCW_90_DEG
@ ORIENTATION_ROTATE_CW_90_DEG
@ ORIENTATION_ROTATE_180_DEG
static dt_image_orientation_t dt_image_orientation(const dt_image_t *img)
void dt_iop_request_focus(dt_iop_module_t *module)
@ IOP_FLAGS_SUPPORTS_BLENDING
gboolean dt_mask_scroll_increases(int up)
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
void(* dt_masks_distance_fn)(float pointer_x, float pointer_y, float cursor_radius, dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside, int *inside_border, int *near, int *inside_source, float *dist, void *user_data)
Shape-specific callback for inside/border/segment hit testing.
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
void(* dt_masks_post_select_fn)(dt_masks_form_gui_t *mask_gui, int inside, int inside_border, int inside_source, void *user_data)
Optional hook to customize selection flags after inside/border/source resolution.
const dt_masks_functions_t dt_masks_functions_group
@ DT_MASKS_STATE_IS_COMBINE_OP
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
gboolean(* dt_masks_border_handle_fn)(const dt_masks_form_gui_points_t *gui_points, int node_count, int node_index, float *handle_x, float *handle_y, void *user_data)
Shape-specific callback to fetch a node's border handle in GUI space.
@ DT_MASKS_IS_PRIMITIVE_SHAPE
@ DT_MASKS_IS_CLOSED_SHAPE
@ DT_MASKS_INTERACTION_OPACITY
@ DT_MASKS_INTERACTION_UNDEF
@ DT_MASKS_ELLIPSE_EQUIDISTANT
@ DT_MASKS_SOURCE_POS_RELATIVE_TEMP
@ DT_MASKS_SOURCE_POS_RELATIVE
@ DT_MASKS_SOURCE_POS_ABSOLUTE
static gboolean dt_masks_gui_was_anything_selected(const dt_masks_form_gui_t *gui)
int dt_masks_gui_confirm_delete_form_dialog(const char *form_name)
const dt_masks_functions_t dt_masks_functions_polygon
static gboolean dt_masks_gui_should_hit_test(dt_masks_form_gui_t *gui)
@ DT_MASKS_INCREMENT_SCALE
@ DT_MASKS_INCREMENT_OFFSET
@ DT_MASKS_INCREMENT_ABSOLUTE
const dt_masks_functions_t dt_masks_functions_ellipse
const dt_masks_functions_t dt_masks_functions_circle
GtkWidget * dt_masks_create_menu(dt_masks_form_gui_t *gui, dt_masks_form_t *form, const dt_masks_form_group_t *fpt, const float pzx, const float pzy)
@ DT_MASKS_GRADIENT_STATE_LINEAR
const dt_masks_functions_t dt_masks_functions_brush
#define DEVELOP_MASKS_VERSION
void(* dt_masks_node_position_fn)(const dt_masks_form_gui_points_t *gui_points, int node_index, float *node_x, float *node_y, void *user_data)
Shape-specific callback to fetch a node's position in GUI space.
void dt_group_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_t *form, dt_masks_form_gui_t *gui)
void(* dt_masks_curve_handle_fn)(const dt_masks_form_gui_points_t *gui_points, int node_index, float *handle_x, float *handle_y, void *user_data)
Shape-specific callback to fetch a node's curve handle in GUI space.
const dt_masks_functions_t dt_masks_functions_gradient
static void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
static float f_inv_sqrtf(const float x)
#define CLAMPF(a, mn, mx)
#define DT_PIXELPIPE_CACHE_HASH_INVALID
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
unsigned __int64 uint64_t
struct dt_gui_gtk_t * gui
const struct dt_database_t * db
struct dt_control_signal_t * signals
struct dt_develop_t * develop
struct dt_control_t * control
struct dt_develop_blend_params_t * blend_params
struct dt_dev_pixelpipe_t * preview_pipe
gboolean darkroom_skip_mouse_events
struct dt_masks_form_gui_t * form_gui
struct dt_develop_t::@17 roi
dt_pthread_rwlock_t masks_mutex
struct dt_gui_gtk_t::@47 mouse
char filename[DT_MAX_FILENAME_LEN]
struct dt_develop_blend_params_t * blend_params
struct dt_develop_t * dev
GModule *dt_dev_operation_t op
dt_masks_gradient_states_t state
float(* get_interaction_value)(const struct dt_masks_form_t *form, dt_masks_interaction_t interaction)
gboolean(* get_gravity_center)(const struct dt_masks_form_t *form, float center[2], float *area)
int(* button_pressed)(struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state, struct dt_masks_form_t *form, int parentid, struct dt_masks_form_gui_t *gui, int index)
int(* button_released)(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state, struct dt_masks_form_t *form, int parentid, struct dt_masks_form_gui_t *gui, int index)
int(* get_source_area)(dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, struct dt_masks_form_t *form, int *width, int *height, int *posx, int *posy)
int(* key_pressed)(struct dt_iop_module_t *module, GdkEventKey *event, struct dt_masks_form_t *form, int parentid, struct dt_masks_form_gui_t *gui, int index)
int(* mouse_moved)(struct dt_iop_module_t *module, double x, double y, double pressure, int which, struct dt_masks_form_t *form, int parentid, struct dt_masks_form_gui_t *gui, int index)
void(* sanitize_config)(dt_masks_type_t type_flags)
void(* set_form_name)(struct dt_masks_form_t *const form, const size_t nb)
void(* duplicate_points)(struct dt_develop_t *const dev, struct dt_masks_form_t *base, struct dt_masks_form_t *dest)
int(* update_hover)(struct dt_masks_form_t *form, struct dt_masks_form_gui_t *gui, int index)
float(* set_interaction_value)(struct dt_masks_form_t *form, dt_masks_interaction_t interaction, float value, dt_masks_increment_t increment, int flow, struct dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
int(* get_points_border)(struct dt_develop_t *dev, struct dt_masks_form_t *form, float **points, int *points_count, float **border, int *border_count, int source, const dt_iop_module_t *const module)
int(* get_area)(const dt_iop_module_t *const module, struct dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, struct dt_masks_form_t *const form, int *width, int *height, int *posx, int *posy)
void(* get_distance)(float x, float y, float as, struct dt_masks_form_gui_t *gui, int index, int num_points, int *inside, int *inside_border, int *near, int *inside_source, float *dist)
int(* mouse_scrolled)(struct dt_iop_module_t *module, double x, double y, int up, const int delta_y, uint32_t state, struct dt_masks_form_t *form, int parentid, struct dt_masks_form_gui_t *gui, int index, dt_masks_interaction_t interaction)
void(* initial_source_pos)(const float iwd, const float iht, float *x, float *y)
void(* post_expose)(cairo_t *cr, float zoom_scale, struct dt_masks_form_gui_t *gui, int index, int num_points)
void(* set_hint_message)(const struct dt_masks_form_gui_t *const gui, const struct dt_masks_form_t *const form, const int opacity, char *const __restrict__ msgbuf, const size_t msgbuf_len)
void(* init_ctrl_points)(struct dt_masks_form_t *form)
struct dt_masks_gui_center_point_t::@34 main
struct dt_masks_gui_center_point_t::@35 source
dt_masks_ellipse_flags_t flags