68 const float center_x,
const float center_y,
69 const float squared_radius)
71 const float delta_x = point_x - center_x;
72 const float delta_y = point_y - center_y;
73 const float squared_distance = delta_x * delta_x + delta_y * delta_y;
74 return squared_distance <= squared_radius;
111 const float cursor_radius2 = cursor_radius * cursor_radius;
112 const float cursor_x = mask_gui->
pos[0];
113 const float cursor_y = mask_gui->
pos[1];
149 const int node_count = (node_count_override >= 0) ? node_count_override
150 : (int)g_list_length(mask_form->
points);
153 const gboolean can_test_nodes = (!
IS_NULL_PTR(node_position_cb)) || has_bezier_layout;
154 const int first_node_index = has_bezier_layout ? 0 : 1;
155 const gboolean has_selected_node = (node_count > 0) && can_test_nodes
157 && selected_node >= first_node_index && selected_node < node_count;
159 if(has_selected_node)
162 float handle_x = NAN;
163 float handle_y = NAN;
165 && border_handle_cb(gui_points, node_count, selected_node, &handle_x, &handle_y, user_data)
175 curve_handle_cb(gui_points, selected_node, &handle_x, &handle_y, user_data);
188 node_position_cb(gui_points, selected_node, &node_x, &node_y, user_data);
190 else if(has_bezier_layout)
192 node_x = gui_points->
points[selected_node * 6 + 2];
193 node_y = gui_points->
points[selected_node * 6 + 3];
195 if(!isnan(node_x) && !isnan(node_y)
205 for(
int node_index = first_node_index; node_index < node_count; node_index++)
211 node_position_cb(gui_points, node_index, &node_x, &node_y, user_data);
213 else if(has_bezier_layout)
215 node_x = gui_points->
points[node_index * 6 + 2];
216 node_y = gui_points->
points[node_index * 6 + 3];
218 if(!isnan(node_x) && !isnan(node_y)
231 int inside_border = 0;
232 int near_segment = -1;
233 int inside_source = 0;
234 float nearest_distance = 0.0f;
235 distance_cb(cursor_x, cursor_y, cursor_radius, mask_gui, form_index, node_count, &inside, &inside_border,
236 &near_segment, &inside_source, &nearest_distance, user_data);
243 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
250 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
253 if(near_segment >= 0)
255 if(near_segment < node_count)
262 if(post_select_cb) post_select_cb(mask_gui, inside, inside_border, inside_source, user_data);
267 return need_refresh_anyway;
289 GList *duplicated_points = NULL;
295 if(point_struct_size != 0)
297 for(GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
299 void *point_copy = malloc(point_struct_size);
300 memcpy(point_copy, point_node->data, point_struct_size);
301 duplicated_points = g_list_prepend(duplicated_points, point_copy);
307 duplicate_form->
points = g_list_reverse(duplicated_points);
309 return duplicate_form;
322 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
325 if(group_entry && group_entry->
formid == form_id)
return group_entry;
335 for(
const GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
485 if(selected_group_entry->
parentid > 0)
490 if(resolved_group_entry)
return resolved_group_entry;
493 return selected_group_entry;
505 int *parent_id,
int *form_index)
507 if(group_entry) *group_entry = NULL;
508 if(parent_id) *parent_id = 0;
509 if(form_index) *form_index = 0;
521 if(group_entry) *group_entry = selected_group_entry;
522 if(parent_id) *parent_id = selected_group_entry->
parentid;
525 return selected_form;
541 const float cursor_x = mask_gui->
pos[0];
542 const float cursor_y = mask_gui->
pos[1];
545 int locked_formid = -1;
550 if(prev_border_selected && prev_group_selected >= 0)
556 const gboolean has_border_lock_candidate = selected_group_entry
561 if(has_border_lock_candidate)
565 int inside_border = 0;
567 int inside_source = 0;
568 float dist = FLT_MAX;
570 g_list_length(selected_form->
points), &inside, &inside_border,
571 &near, &inside_source, &
dist);
572 if(inside_border || near >= 0)
573 locked_formid = selected_group_entry->
formid;
577 if(prev_group_selected >= 0)
581 int selected_index = -1;
582 float best_dist = FLT_MAX;
585 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node), index++)
589 if(locked_formid >= 0 && group_entry->
formid != locked_formid)
continue;
595 int inside_border = 0;
597 int inside_source = 0;
598 float dist = FLT_MAX;
601 &inside, &inside_border, &near, &inside_source, &
dist);
603 const gboolean is_selected_form = (prev_group_selected == index);
604 const gboolean hit_border = (inside_border || near >= 0);
607 if(!is_selected_form && hit_border && !is_open_shape)
610 if(inside || hit_border || inside_source)
617 const float center_dist2 = dx * dx + dy * dy;
618 const float combined_dist2 =
dist * center_dist2;
619 if(combined_dist2 < best_dist)
621 selected_form = form;
622 selected_index = index;
623 best_dist = combined_dist2;
646 const int form_index)
655 const int form_index)
661 int inside_border = 0;
663 int inside_source = 0;
664 float dist = FLT_MAX;
666 mask_gui, form_index,
667 g_list_length(dispatch_form->
points), &inside, &inside_border, &near,
668 &inside_source, &
dist);
669 return inside || inside_border || near >= 0 || inside_source;
682 if((mask_gui->
scrollx - mask_gui->
pos[0] < radius && mask_gui->
scrollx - mask_gui->
pos[0] > -radius)
683 && (mask_gui->
scrolly - mask_gui->
pos[1] < radius && mask_gui->
scrolly - mask_gui->
pos[1] > -radius))
697 const int form_index,
const int button)
699 if(button != 1)
return FALSE;
730 char message[256] =
"";
732 const int form_type = mask_form->
type;
734 int opacity_percent = 100;
737 int selected_form_id = 0;
742 if(!
IS_NULL_PTR(selected_group_entry)) selected_form_id = selected_group_entry->
formid;
746 "[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",
747 (
void *)mask_form, mask_form ? mask_form->
type : -1, (
void *)mask_gui,
765 opacity_percent = (int)(
dt_conf_get_float(
"plugins/darkroom/masks/opacity") * 100);
775 "[masks] hint end: sel=%p has_set_hint=%d opacity=%d msg_len=%" G_GSIZE_FORMAT
" msg='%s'\n",
776 (
void *)selected_form,
778 opacity_percent, strlen(message), message);
779 return message[0] !=
'\0';
786 mask_gui->
pos[0] = mask_gui->
pos[1] = -1.0f;
824 mask_gui->
delta[0] = mask_gui->
delta[1] = 0.0f;
837 const int gui_points_count = g_list_length(mask_gui->
points);
838 if(gui_points_count == form_index)
842 mask_gui->
points = g_list_append(mask_gui->
points, gui_points_new);
844 else if(gui_points_count < form_index)
857 NULL, NULL,
TRUE, module)
872 float posx,
float posy)
884 const double min_delta_time = 1.0 / 60.0;
885 const float min_dist2 = 4.0f;
886 const gboolean force_rebuild
895 if(elapsed_time < min_delta_time && (delta_x * delta_x + delta_y * delta_y) < min_dist2)
929 mask_form->
points = g_list_remove(mask_form->
points, brush_node);
956 if(parent_id <= 0)
return 1;
966 const int edit_mode = mask_gui->
edit_mode;
972 for(GList *forms = visible_form->
points; forms; forms = g_list_next(forms))
977 visible_form->
points = g_list_remove(visible_form->
points, group_entry);
1001 for(GList *form_node = dev->
forms; form_node; form_node = g_list_next(form_node))
1006 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
1009 if(group_entry && group_entry->
formid == formid)
1012 if(count > 1)
goto done;
1029 int next_form_index = -1;
1030 int next_formid = 0;
1034 const int group_length = g_list_length(visible_form->
points);
1044 GList *next_node = g_list_next(selected_node);
1045 if(
IS_NULL_PTR(next_node)) next_node = g_list_previous(selected_node);
1054 gboolean res =
TRUE;
1055 int signal_parent_id = 0;
1062 signal_parent_id = parent_id;
1074 const int edit_mode = mask_gui->
edit_mode;
1079 for(GList *forms = visible_form->
points; forms; forms = g_list_next(forms))
1084 visible_form->
points = g_list_remove(visible_form->
points, group_entry);
1104 if(res && next_formid > 0)
1123 const gboolean internal_masks
1125 && ((
module->flags() & IOP_FLAGS_INTERNAL_MASKS) == IOP_FLAGS_INTERNAL_MASKS);
1127 int response = GTK_RESPONSE_YES;
1128 if(use_count <= 1 && !internal_masks)
1131 if(response == GTK_RESPONSE_CANCEL)
return FALSE;
1207 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit),
TRUE);
1208 gtk_widget_queue_draw(blend_data->
masks_edit);
1216 if(selected_index >= 0)
1237 else if(last_formid > 0)
1280 else if(parent_id > 0)
1299 gui_points->
points = NULL;
1301 gui_points->
border = NULL;
1303 gui_points->
source = NULL;
1328 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
1346 int new_form_id = 100;
1352 mask_form->
formid = new_form_id++;
1357 form_node = g_list_next(form_node);
1365 g_strlcpy(group_form->
name, group_name,
sizeof(group_form->
name));
1375 module->blend_params->mask_id = group_form->formid;
1391 develop->
forms = g_list_append(develop->
forms, mask_form);
1398 develop->
forms = g_list_remove(develop->
forms, mask_form);
1412 guint form_count = 0;
1416 for(GList *form_node = develop->
forms; form_node; form_node = g_list_next(form_node))
1419 if(existing_form->
type == mask_form->
type) form_count++;
1423 gboolean name_exists =
FALSE;
1429 name_exists =
FALSE;
1436 for(GList *form_node = develop->
forms; form_node; form_node = g_list_next(form_node))
1439 if(!strcmp(existing_form->
name, mask_form->
name))
1447 }
while(name_exists);
1478 group_form->
points = g_list_append(group_form->
points, group_entry);
1539 snprintf(dest_form->
name,
sizeof(dest_form->
name), _(
"copy of %s"), base_form->
name);
1548 return dest_form->
formid;
1552 float **point_buffer,
int *point_count,
1553 float **border_buffer,
int *border_count,
1558 border_buffer, border_count, source, module);
1564 int *area_width,
int *area_height,
int *area_pos_x,
int *area_pos_y)
1567 return mask_form->
functions->
get_area(module, pipe, piece, mask_form, area_width, area_height,
1568 area_pos_x, area_pos_y);
1574 int *area_width,
int *area_height,
1575 int *area_pos_x,
int *area_pos_y)
1577 *area_width = *area_height = *area_pos_x = *area_pos_y = 0;
1584 area_pos_x, area_pos_y);
1616 const char *opname =
"flip";
1619 for(GList *module_node = develop->
iop; module_node; module_node = g_list_next(module_node))
1622 if(!strcmp(iop_module->
op, opname))
1624 module = iop_module;
1633 module->init_pipe(module, NULL, &piece);
1634 module->commit_params(module, module->default_params, NULL, &piece);
1639 GList *point_node = mask_form->
points;
1647 module->distort_backtransform(module, NULL, &piece, circle->center, 1);
1651 for(; point_node; point_node = g_list_next(point_node))
1655 module->distort_backtransform(module, NULL, &piece, polygon_node->node, 1);
1656 module->distort_backtransform(module, NULL, &piece, polygon_node->ctrl1, 1);
1657 module->distort_backtransform(module, NULL, &piece, polygon_node->ctrl2, 1);
1664 module->distort_backtransform(module, NULL, &piece, gradient->center, 1);
1676 module->distort_backtransform(module, NULL, &piece, ellipse->center, 1);
1680 const float y = ellipse->
radius[0];
1687 for(; point_node; point_node = g_list_next(point_node))
1691 module->distort_backtransform(module, NULL, &piece, brush_node->node, 1);
1692 module->distort_backtransform(module, NULL, &piece, brush_node->ctrl1, 1);
1693 module->distort_backtransform(module, NULL, &piece, brush_node->ctrl2, 1);
1700 module->distort_backtransform(module, NULL, &piece, mask_form->source, 1);
1711 const float image_width = (float)image->
width;
1712 const float image_height = (float)image->
height;
1714 const float crop_x = (float)image->
crop_x;
1715 const float crop_y = (float)image->
crop_y;
1726 coords[0] = ((coords[0] * crop_width) + crop_x) / image_width;
1727 coords[1] = ((coords[1] * crop_height) + crop_y) / image_height;
1731 size_t coords_count)
1733 const float image_width = (float)image->
width;
1734 const float image_height = (float)image->
height;
1744 const float crop_min =
MIN(crop_width, crop_height);
1745 const float image_min =
MIN(image_width, image_height);
1746 for(
size_t coord_index = 0; coord_index < coords_count; coord_index++)
1747 coords[coord_index] = ((coords[coord_index] * crop_min)) / image_min;
1769 GList *point_node = mask_form->
points;
1783 for(; point_node; point_node = g_list_next(point_node))
1807 for(; point_node; point_node = g_list_next(point_node))
1841 GList *point_node = mask_form->
points;
1867 GList *point_node = mask_form->
points;
1892 GList *point_node = mask_form->
points;
1912 if(old_version == 1 && new_version == 2)
1918 if(old_version == 1 && new_version == 6)
1926 else if(old_version == 2 && new_version == 6)
1933 else if(old_version == 3 && new_version == 6)
1939 else if(old_version == 4 && new_version == 6)
1944 else if(old_version == 5 && new_version == 6)
2004 for(; form_list; form_list = g_list_next(form_list))
2007 if(mask_form->
formid == form_id)
return mask_form;
2022 for(GList *module_node = g_list_first(develop->
iop); module_node; module_node = g_list_next(module_node))
2025 if(strcmp(module->op,
"mask_manager") == 0)
2032 const int used_count)
2034 for(
int used_index = 0; used_index < used_count; used_index++)
2036 if(used_form_ids[used_index] == 0)
2038 used_form_ids[used_index] = form_id;
2041 if(used_form_ids[used_index] == form_id)
break;
2047 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
2061 const guint form_count = g_list_length(develop_src->
forms);
2062 if(form_count == 0)
return 0;
2064 int *used_form_ids = calloc(form_count,
sizeof(
int));
2068 used_form_ids, form_count);
2070 for(
int form_index = 0; form_index < (int)form_count && used_form_ids[form_index] > 0; form_index++)
2076 used_form_ids[form_index]);
2079 develop_dest->
forms = g_list_remove(develop_dest->
forms, existing_form);
2080 develop_dest->
allforms = g_list_append(develop_dest->
allforms, existing_form);
2089 develop_dest->
forms = g_list_append(develop_dest->
forms, new_form);
2093 fprintf(stderr,
"[dt_masks_copy_used_forms_for_module] form %i not found in source image\n",
2094 used_form_ids[form_index]);
2106 int previous_num = -1;
2108 sqlite3_stmt *statement = NULL;
2112 "SELECT imgid, formid, form, name, version, points, points_count, source, num "
2113 "FROM main.masks_history WHERE imgid = ?1 ORDER BY num",
2114 -1, &statement, NULL);
2118 while(sqlite3_step(statement) == SQLITE_ROW)
2125 const int form_id = sqlite3_column_int(statement, 1);
2126 const int history_num = sqlite3_column_int(statement, 8);
2129 mask_form->
formid = form_id;
2130 const char *form_name = (
const char *)sqlite3_column_text(statement, 3);
2131 g_strlcpy(mask_form->
name, form_name,
sizeof(mask_form->
name));
2132 mask_form->
version = sqlite3_column_int(statement, 4);
2133 mask_form->
points = NULL;
2134 const int point_count = sqlite3_column_int(statement, 6);
2135 memcpy(mask_form->
source, sqlite3_column_blob(statement, 7),
sizeof(
float) * 2);
2140 const char *
const point_buffer = (
char *)sqlite3_column_blob(statement, 5);
2142 for(
int point_index = 0; point_index < point_count; point_index++)
2144 char *point_data = (
char *)malloc(point_struct_size);
2145 memcpy(point_data, point_buffer + point_index * point_struct_size, point_struct_size);
2146 mask_form->
points = g_list_append(mask_form->
points, point_data);
2159 "[_dev_read_masks_history] %s (imgid `%i'): mask version mismatch: history is %d, dt %d.\n",
2177 if(previous_num != history_num)
2179 history_item = NULL;
2180 for(GList *history_node = g_list_first(develop->
history); history_node; history_node = g_list_next(history_node))
2183 if(history_entry->
num == history_num)
2185 history_item = history_entry;
2189 previous_num = history_num;
2199 history_item->
forms = g_list_append(history_item->
forms, mask_form);
2203 "[_dev_read_masks_history] can't find history entry %i while adding mask %s(%i)\n",
2204 history_num, mask_form->
name, form_id);
2208 sqlite3_finalize(statement);
2217 sqlite3_stmt *statement = NULL;
2220 mask_form->
name, mask_form->
type, image_id);
2225 "INSERT INTO main.masks_history (imgid, num, formid, form, name, "
2226 "version, points, points_count,source) VALUES "
2227 "(?1, ?9, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
2228 -1, &statement, NULL);
2240 const guint point_count = g_list_length(mask_form->
points);
2241 char *
const restrict point_buffer = (
char *)malloc(point_count * point_struct_size);
2242 int buffer_offset = 0;
2243 for(GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
2245 memcpy(point_buffer + buffer_offset, point_node->data, point_struct_size);
2246 buffer_offset += point_struct_size;
2249 point_count * point_struct_size, SQLITE_TRANSIENT);
2251 sqlite3_step(statement);
2252 sqlite3_finalize(statement);
2261 mask_form->
points = NULL;
2315 const uint32_t
state,
2316 const gboolean shape_was_selected)
2322 const gboolean prev_node_selected = mask_gui->
node_selected;
2324 const gboolean prev_form_selected = mask_gui->
form_selected;
2344 if(prev_node_selected)
2353 if(prev_node_selected)
2373 if(!shape_was_selected)
return;
2391 float point[2] = { (float)
x, (
float)y };
2439 dispatch_form, parent_id, mask_gui, form_index);
2472 state, dispatch_form, parent_id, mask_gui, form_index);
2481 if(selected_group_entry)
2486 if(mask_gui && !mask_gui->
creation && button == 1)
2499 int button,
int event_type, uint32_t
state)
2526 gboolean return_val =
FALSE;
2529 button, event_type,
state,
2530 dispatch_form, parent_id, mask_gui, form_index);
2536 ? (prev_group_selected >= 0 && prev_group_selected == form_index)
2537 : prev_any_selected;
2540 if(button == 3 && !return_val)
2551 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
2567 gboolean return_value =
FALSE;
2578 parent_id, mask_gui, form_index);
2597 case GDK_KEY_Escape:
2602 case GDK_KEY_Delete:
2618 return return_value;
2622 int scroll_up, uint32_t key_state,
int scrolling_delta)
2638 int scroll_flow = (scrolling_delta < 0) ? -scrolling_delta : scrolling_delta;
2657 scroll_increases ? 1 : 0, scroll_flow,
2658 key_state, dispatch_form, parent_id, mask_gui, form_index,
2665 "[masks] scroll: ret=%d hinted=%d form=%p type=%d gui=%p group_selected=%d flow=%d state=0x%x\n",
2666 result, hinted, (
void *)mask_form, mask_form ? mask_form->
type : -1, (
void *)mask_gui,
2679 const float *point_values = &gui_points->
points[node_index * 6];
2680 return (point_values[0 + 2] == point_values[2 + 2]
2681 && point_values[1 + 2] == point_values[3 + 2]);
2700 const float *points,
const int points_count,
const float zoom_scale,
2702 const gboolean is_closed_shape,
2703 const float offset_factor,
2707 result[0] = ray_1[0];
2708 result[1] = ray_1[1];
2710 const int available_points = points_count - first_pt;
2711 if(available_points < 2)
return;
2713 const float ray2_x = ray_2[0];
2714 const float ray2_y = ray_2[1];
2715 const float ray_center_x = 0.5f * (ray_1[0] + ray_2[0]);
2716 const float ray_center_y = 0.5f * (ray_1[1] + ray_2[1]);
2717 const float dir_x = ray_1[0] - ray_2[0];
2718 const float dir_y = ray_1[1] - ray_2[1];
2719 float min_s = FLT_MAX;
2721 const float inv_dir_len =
f_inv_sqrtf(dir_x * dir_x + dir_y * dir_y);
2722 const float ux = dir_x * inv_dir_len;
2723 const float uy = dir_y * inv_dir_len;
2726 const int segment_count = (available_points - 1) + ((is_closed_shape) ? 1 : 0);
2727 for(
int seg = 0; seg < segment_count; seg++)
2730 const int i = first_pt + seg;
2731 const int j = (
i + 1 < points_count) ? (
i + 1) : first_pt;
2732 const float x3 = points[
i * 2];
2733 const float y3 = points[
i * 2 + 1];
2734 const float x4 = points[j * 2];
2735 const float y4 = points[j * 2 + 1];
2738 const float dx = x4 - x3;
2739 const float dy = y4 - y3;
2740 const float det = dx * (-dir_y) + dy * dir_x;
2741 if(det > -1e-8f && det < 1e-8f)
continue;
2742 const float inv_det = 1.0f / det;
2744 const float segment_param = ((ray2_x - x3) * (-dir_y) + (ray2_y - y3) * dir_x) * inv_det;
2745 const float ray_param = ((x3 - ray2_x) * dy - (y3 - ray2_y) * dx) * inv_det;
2746 if(segment_param < 0.0f || segment_param > 1.0f || ray_param <= 0.0f || ray_param >= min_s)
continue;
2749 const float ix = ray2_x + ray_param * dir_x;
2750 const float iy = ray2_y + ray_param * dir_y;
2753 const float to_center = (ray_center_x - ix) * ux + (ray_center_y - iy) * uy;
2754 const float side_sign = (to_center >= 0.0f) ? 1.0f : -1.0f;
2755 result[0] = ix + side_sign * offset * ux;
2756 result[1] = iy + side_sign * offset * uy;
2761 const int node_count,
const float zoom_scale,
2772 const float main[2] = { center_point->
main.
x, center_point->
main.
y };
2773 const float source[2] = { center_point->
source.
x, center_point->
source.
y };
2777 int first_point_index = 2;
2779 first_point_index = 10;
2781 first_point_index = node_count * 3;
2787 attach_points_count /= 2;
2788 attach_source_count /= 2;
2791 float head[2] = { 0.0f, 0.0f };
2792 float tail[2] = { 0.0f, 0.0f };
2796 zoom_scale, first_point_index, is_closed_shape, 1.f, head);
2800 zoom_scale, first_point_index, is_closed_shape, 0.5f, tail);
2802 const gboolean selected = (mask_gui->
group_selected == form_index)
2804 gboolean draw_tail =
TRUE;
2813 const float pts[4] = { tail[0], tail[1], source[0], source[1] };
2819 const float origin_pt[2] = {
main[0],
main[1] };
2827 head[0] = 0.5f * (
main[0] + source[0]);
2828 head[1] = 0.5f * (
main[1] + source[1]);
2831 const float arrow_len_sq = sqf(tail[0] - head[0]) + sqf(tail[1] - head[1]);
2832 draw_tail = arrow_len_sq > 1e-6f && !overlap;
2836 const float angle = is_open_shape ? atan2f(tail[1] - head[1], tail[0] - head[0])
2837 : atan2f(head[1] -
main[1], head[0] -
main[0]);
2849 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1);
2852 cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1);
2856 cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1);
2866 const int rendered_node_count = node_count + !mask_gui->
creation;
2867 const gboolean shape_selected = (mask_gui->
group_selected == form_index)
2871 rendered_node_count, draw_shape_func);
2876 const float *points,
const int points_count,
const int node_count,
2877 const float zoom_scale)
2880 if(node_count <= 0 || points_count <= node_count * 3 + 6)
return;
2882 const int total_coords = points_count * 2;
2883 if(total_coords <= (node_count * 6 + 1))
return;
2885 const gboolean group_selected = (mask_gui->
group_selected == form_index);
2887 int show_segment_index = 1;
2888 int current_segment_index = 0;
2889 cairo_move_to(cr, points[node_count * 6], points[node_count * 6 + 1]);
2891 for(
int point_index = node_count * 3; point_index < points_count; point_index++)
2893 const int coord_index = point_index * 2;
2894 if((coord_index + 1) >= total_coords)
break;
2896 const double coord_x = points[coord_index];
2897 const double coord_y = points[coord_index + 1];
2898 cairo_line_to(cr, coord_x, coord_y);
2900 const int segment_coord_index = show_segment_index * 6;
2901 if((segment_coord_index + 3) >= total_coords)
continue;
2903 const double segment_x = points[segment_coord_index + 2];
2904 const double segment_y = points[segment_coord_index + 3];
2905 if(coord_x == segment_x && coord_y == segment_y)
2907 const gboolean seg_is_selected = group_selected
2909 == current_segment_index);
2910 const gboolean all_selected = group_selected
2914 if(mask_gui->
creation && current_segment_index == node_count - 2)
2918 CAIRO_LINE_CAP_BUTT);
2920 show_segment_index = (show_segment_index + 1) % node_count;
2921 current_segment_index++;
2924 if(mask_gui->
creation && current_segment_index >= node_count - 1)
break;
2937 cairo_t *cr,
const float zoom_scale,
2950 for(GList *formid_node = creation_gui->
creation_formids; formid_node; formid_node = g_list_next(formid_node))
2952 const int formid = GPOINTER_TO_INT(formid_node->data);
2959 const guint point_count = g_list_length(session_form->
points);
2960 draw_gui.
type = session_form->
type;
2975 int32_t pointerx, int32_t pointery)
2984 int buffer_width = 0;
2985 int buffer_height = 0;
2988 if(buffer_width < 1.0 || buffer_height < 1.0)
return;
2993 cairo_surface_t *overlay = NULL;
2994 cairo_t *mask_draw = NULL;
2995 cairo_surface_t *target = cairo_get_target(cr);
2996 double sx = 1.0, sy = 1.0;
2997 cairo_surface_get_device_scale(target, &sx, &sy);
2998 overlay = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR_ALPHA, (
int)ceil(
width * sx),
3000 cairo_surface_set_device_scale(overlay, sx, sy);
3001 mask_draw = cairo_create(overlay);
3008 cairo_save(mask_draw);
3013 cairo_restore(mask_draw);
3014 cairo_destroy(mask_draw);
3015 cairo_surface_destroy(overlay);
3031 const guint point_count = g_list_length(mask_form->
points);
3035 cairo_restore(mask_draw);
3039 cairo_identity_matrix(cr);
3040 cairo_set_source_surface(cr, overlay, 0.0, 0.0);
3044 cairo_destroy(mask_draw);
3045 cairo_surface_destroy(overlay);
3103 gboolean is_registered =
FALSE;
3104 gboolean is_registered_for_cleanup =
FALSE;
3106 for(
const GList *form_node =
darktable.
develop->
forms; form_node; form_node = g_list_next(form_node))
3108 if(form_node->data == old_form)
3110 is_registered =
TRUE;
3117 if(form_node->data == old_form)
3119 is_registered_for_cleanup =
TRUE;
3145 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit), 0);
3152 for(GList *module_node =
darktable.
develop->
iop; module_node; module_node = g_list_next(module_node))
3156 && !IS_NULL_PTR(module->blend_data))
3158 dt_iop_gui_blend_data_t *blend_data = (dt_iop_gui_blend_data_t *)module->blend_data;
3162 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->masks_edit),
FALSE);
3163 gtk_widget_queue_draw(blend_data->masks_edit);
3195 if(
value && mask_form)
3201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(blend_data->
masks_edit),
3212 module->blend_params->mask_id = 0;
3277 for(GList *group_node = source_group->
points; group_node; group_node = g_list_next(group_node))
3304 const guint forms_count = g_list_length(module->dev->forms);
3305 const guint iop_count = g_list_length(module->dev->iop);
3306 guint combo_capacity = 5 + forms_count + iop_count;
3319 int combo_index = 0;
3320 combo_ids[combo_index] = 0;
3325 for(GList *form_node = module->dev->forms; form_node; form_node = g_list_next(form_node))
3329 || mask_form->
formid == module->blend_params->mask_id)
3339 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3352 combo_ids[combo_index] = mask_form->
formid;
3360 for(GList *module_node = module->dev->iop; module_node; module_node = g_list_next(module_node))
3372 combo_ids[combo_index] = -1 * iop_index;
3386 if(selection_index == 0)
return;
3387 if(selection_index > 0)
3390 const guint iop_count = g_list_length(module->
dev->
iop);
3392 if(selection_value == -1000000)
3397 else if(selection_value == -2000001)
3402 else if(selection_value == -2000002)
3407 else if(selection_value == -2000016)
3412 else if(selection_value == -2000032)
3417 else if(selection_value == -2000064)
3422 else if(selection_value < 0)
3425 selection_value = -1 * selection_value - 1;
3426 if(selection_value < (
int)iop_count)
3437 else if(selection_value > 0)
3454 int form_id = mask_form->
formid;
3462 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3465 if(group_entry->
formid == form_id)
3468 group_form->
points = g_list_remove(group_form->
points, group_entry);
3521 GList *shapes = iop_group->
points;
3525 if(group_entry->
formid == form_id)
3529 iop_group->
points = g_list_remove(iop_group->
points, group_entry);
3531 shapes = iop_group->
points;
3534 shapes = g_list_next(shapes);
3550 if(existing_form->
formid == form_id)
3588 float center_point[2];
3593 mask_form->
area = area;
3597 "[masks] gravity center updated: form=%p id=%d type=0x%x ok=%d center=(%f,%f), area=%f\n",
3598 (
void *)mask_form, mask_form->
formid, mask_form->
type, ok,
3623 float center[2] = { 0.0f, 0.0f };
3631 dev->
roi.
x = center[0];
3632 dev->
roi.
y = center[1];
3667 flow, mask_gui, module);
3668 if(isnan(result))
return NAN;
3708 return current * powf(amount, (
float)flow);
3710 return current + amount * (float)flow;
3723 return current * scale_amount;
3725 return current + offset_amount;
3733 float value_min,
float value_max,
3736 gchar *config_key = NULL;
3737 if(!strcmp(feature,
"opacity"))
3738 config_key = g_strdup_printf(
"plugins/darkroom/%s_opacity",
_get_mask_plugin(mask_form));
3740 config_key = g_strdup_printf(
"plugins/darkroom/%s/%s/%s",
3743 if(!g_strcmp0(feature,
"rotation")) flow = (flow > 1) ? (flow - 1) * 5 : flow;
3747 if(!g_strcmp0(feature,
"rotation"))
3750 if(updated_value > value_max) updated_value = fmodf(updated_value, value_max);
3751 else if(updated_value < value_min)
3752 updated_value = value_max - fmodf(value_min - updated_value, value_max);
3754 else updated_value =
MAX(value_min,
MIN(updated_value, value_max));
3759 return updated_value;
3763 float value_min,
float value_max,
3765 const char *toast_fmt,
float toast_scale)
3768 value_min, value_max, increment, flow);
3769 if(!
IS_NULL_PTR(toast_fmt) && toast_fmt[0] !=
'\0')
3779 for(
const GList *point_node = base_form->
points; point_node; point_node = g_list_next(point_node))
3781 const void *point_data = point_node->data;
3783 void *point_copy = malloc(node_size);
3785 memcpy(point_copy, point_data, node_size);
3786 dest_form->
points = g_list_append(dest_form->
points, point_copy);
3806 float amount = scroll_up ? 0.02f : -0.02f;
3809 return !isnan(changed);
3818 guint group_index = 0;
3819 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3822 if(entry->
formid == form_id)
3824 group_entry = entry;
3833 const guint group_length = g_list_length(group_form->
points);
3834 if(!move_up && group_index == 0)
return;
3835 if(move_up && group_index == group_length - 1)
return;
3837 group_form->
points = g_list_remove(group_form->
points, group_entry);
3842 group_form->
points = g_list_insert(group_form->
points, group_entry, group_index);
3850 if(group_form->
formid == form_id)
return 1;
3851 int nested_count = 0;
3852 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3861 return nested_count;
3878 group_form->
points = g_list_append(group_form->
points, group_entry);
3897 for(GList *group_node = group_form->
points; group_node; group_node = g_list_next(group_node))
3914 dest_group->
points = g_list_append(dest_group->
points, new_entry);
3928 hash =
dt_hash(hash, (
char *)&mask_form->
formid,
sizeof(
int));
3930 hash =
dt_hash(hash, (
char *)&mask_form->
source,
sizeof(
float) * 2);
3932 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
3941 hash =
dt_hash(hash, (
char *)&group_entry->
state,
sizeof(
int));
3942 hash =
dt_hash(hash, (
char *)&group_entry->
opacity,
sizeof(
float));
3962 hash =
dt_hash(hash, (
const char *)&mask_form->
formid,
sizeof(
int));
3963 hash =
dt_hash(hash, (
const char *)&mask_form->
version,
sizeof(
int));
3964 hash =
dt_hash(hash, (
const char *)&mask_form->
source,
sizeof(
float) * 2);
3966 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
3976 hash =
dt_hash(hash, (
const char *)&group_entry->
formid,
sizeof(
int));
3977 hash =
dt_hash(hash, (
const char *)&group_entry->
state,
sizeof(
int));
3978 hash =
dt_hash(hash, (
const char *)&group_entry->
opacity,
sizeof(
float));
3994 for(
int used_index = 0; used_index < used_count; used_index++)
3996 if(used_form_ids[used_index] == 0)
3999 used_form_ids[used_index] = form_id;
4002 if(used_form_ids[used_index] == form_id)
break;
4009 for(GList *group_node = mask_form->
points; group_node; group_node = g_list_next(group_node))
4020 int masks_removed = 0;
4021 GList *forms = *forms_list;
4024 guint form_count = g_list_length(forms);
4025 int *used_form_ids = calloc(form_count,
sizeof(
int));
4028 int history_index = 0;
4029 for(GList *history_node = history_list; history_node && history_index < history_end;
4030 history_node = g_list_next(history_node))
4046 GList *shape_node = forms;
4051 for(
int used_index = 0; used_index < form_count; used_index++)
4053 if(used_form_ids[used_index] == mask_form->
formid)
4058 if(used_form_ids[used_index] == 0)
break;
4061 shape_node = g_list_next(shape_node);
4065 forms = g_list_remove(forms, mask_form);
4074 *forms_list = forms;
4076 return masks_removed;
4090 int history_count = g_list_length(history_list);
4091 int history_end = history_count;
4092 for(
const GList *history_node = g_list_last(history_list); history_node;
4093 history_node = g_list_previous(history_node))
4099 history_end = history_count - 1;
4118 GList *forms = NULL;
4119 int history_index = 0;
4120 for(
const GList *history_node = g_list_first(develop->
history);
4122 history_node = g_list_next(history_node))
4126 if(history_item->
forms) forms = history_item->
forms;
4146 const float *form_points,
int form_points_start,
int form_points_count)
4149 if(form_points_count <= 2 + form_points_start)
return -1;
4150 if(form_points_start < 0 || form_points_start >= form_points_count)
return -1;
4152 const int start_index = form_points_start;
4153 for(
int test_index = 0; test_index < test_point_count; test_index++)
4155 int intersection_count = 0;
4156 const float point_x = test_points[test_index * 2];
4157 const float point_y = test_points[test_index * 2 + 1];
4158 int visited_points = 0;
4160 for(
int i = form_points_start, next = start_index + 1;
i < form_points_count;)
4165 if(next < start_index || next >= form_points_count)
break;
4166 if(++visited_points > form_points_count - start_index + 1)
break;
4168 const float y1 = form_points[
i * 2 + 1];
4169 const float y2 = form_points[next * 2 + 1];
4172 if(isnan(form_points[next * 2]))
4174 const int jump_index = isnan(y2) ? start_index : (int)y2;
4175 if(jump_index == next || jump_index < start_index || jump_index >= form_points_count)
break;
4180 if(((point_y <= y2 && point_y > y1) || (point_y >= y2 && point_y < y1))
4181 && (form_points[
i * 2] > point_x))
4182 intersection_count++;
4184 if(next == start_index)
break;
4187 if(next >= form_points_count) next = start_index;
4190 if(intersection_count & 1)
return test_index;
4203 const int selected_formid =
IS_NULL_PTR(selected_form) ? 0 : selected_form->
formid;
4206 module = darktable.develop->gui_module;
4208 if(!
IS_NULL_PTR(module) && module->masks_selection_changed)
4209 module->masks_selection_changed(module, selected_formid);
4226 fprintf(stderr,
"[dt_masks_set_source_pos_initial_state] unknown state for setting masks position type\n");
4249 const float xx = mask_gui->
pos[0];
4250 const float yy = mask_gui->
pos[1];
4264 fprintf(stderr,
"[dt_masks_set_source_pos_initial_value] unsupported masks type when calculating source position initial value\n");
4280 mask_form->
source[0] = source_points[0];
4281 mask_form->
source[1] = source_points[1];
4302 mask_form->
source[0] = source_points[0];
4303 mask_form->
source[1] = source_points[1];
4306 fprintf(stderr,
"[dt_masks_set_source_pos_initial_value] unknown source position type\n");
4315 const float initial_ypos,
const float xpos,
const float ypos,
4316 float *pos_x,
float *pos_y,
const int adding)
4318 float source_x = 0.0f;
4319 float source_y = 0.0f;
4339 fprintf(stderr,
"[dt_masks_calculate_source_pos_origin] unsupported masks type when calculating source position value\n");
4352 source_x = xpos + mask_gui->
pos_source[0] - initial_xpos;
4353 source_y = ypos + mask_gui->
pos_source[1] - initial_ypos;
4363 fprintf(stderr,
"[dt_masks_calculate_source_pos_origin] unknown source position type for setting source position value\n");
4380 const float center_x = center[0];
4381 const float center_y = center[1];
4384 const float anchor_x = anchor[0];
4385 const float anchor_y = anchor[1];
4386 const float angle_current = atan2f(anchor_y - center_y, anchor_x - center_x);
4389 const float delta_x = mask_gui->
delta[0];
4390 const float delta_y = mask_gui->
delta[1];
4391 const float angle_prev = atan2f(delta_y - center_y, delta_x - center_x);
4394 float delta_angle = angle_current - angle_prev;
4395 float angle = atan2f(sinf(delta_angle), cosf(delta_angle));
4398 float test_points[8] = { center_x, center_y, anchor_x , anchor_y,
4399 center_x + 10.0f, center_y, center_x, center_y + 10.0f };
4401 float check_angle = atan2f(test_points[7] - test_points[1], test_points[6] - test_points[0])
4402 - atan2f(test_points[5] - test_points[1], test_points[4] - test_points[0]);
4404 check_angle = atan2f(sinf(check_angle), cosf(check_angle));
4407 if(check_angle < 0.0f) angle = -angle;
4410 mask_gui->
delta[0] = anchor_x;
4411 mask_gui->
delta[1] = anchor_y;
4413 return angle /
M_PI * 180.0f;
4470 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)
void dt_atomic_set_int(dt_atomic_int *var, int value)
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_control_mouse_is_dragging(gboolean state)
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.
uint64_t dt_masks_form_get_own_hash(uint64_t hash, const dt_masks_form_t *mask_form)
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)
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)
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.
static gboolean _masks_remove_or_delete_finish(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id, gboolean keep_unused)
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)
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)
gboolean dt_masks_remove_shape_from_group(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
Remove the form from its current group only, keeping it around unused for potential reuse....
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)
void dt_masks_form_invalidate_gravity_center(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)
gboolean dt_masks_delete_shape(struct dt_iop_module_t *module, dt_masks_form_t *sel, int parent_id, dt_masks_form_gui_t *mask_gui, int form_id)
Permanently delete the form from every group and from dev->forms. Gated by dt_masks_gui_confirm_perma...
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)
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)
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock)
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock)
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock)
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.
gboolean dt_masks_gui_confirm_permanent_delete(const char *form_name)
Ask the user to confirm a permanent shape deletion, gated by the "ask_before_delete_mask_shape" pref....
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)
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)
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
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
void dt_supervisor_form(const dt_sv_op_t op, const dt_masks_form_t *form)
static gboolean dt_supervisor_active(void)