47#ifndef DT_DEVELOP_MASKS_GUI_H
48#define DT_DEVELOP_MASKS_GUI_H
225 const float *first = (
const float *)points->data;
226 float min_x = first[0];
227 float max_x = first[0];
228 float min_y = first[1];
229 float max_y = first[1];
233 const float *node = (
const float *)
point_node->data;
235 min_x =
fminf(min_x, node[0]);
236 max_x =
fmaxf(max_x, node[0]);
237 min_y =
fminf(min_y, node[1]);
238 max_y =
fmaxf(max_y, node[1]);
241 return fmaxf(max_x - min_x, max_y - min_y);
302 const int node_index,
303 float node[2],
float ctrl1[2],
float ctrl2[2],
312 const int node_index,
318 const float handle[2],
float point[2])
320 const float dx_line = handle[0] - node[0];
321 const float dy_line = handle[1] - node[1];
326 point[1] = cursor[1];
331 const float b = node[1] - a * node[0];
332 const float denom = a * a + 1.0f;
333 const float xproj = (a * cursor[1] + cursor[0] - b * a) /
denom;
347 float node_raw[2] = { node[0], node[1] };
360 float *points,
const int points_count,
361 float *
border,
const int border_count,
366 const gboolean source)
369 if(points && points_count > 0)
372 if(
border && border_count > 0)
722 const float cx,
const float cy,
731 int node_index,
float *handle_x,
float *handle_y,
void *user_data);
738 float *handle_x,
float *handle_y,
void *user_data);
785#define menu_item_set_fake_accel(menu_item, keyval, mods) \
788 GtkWidget *child = gtk_bin_get_child(GTK_BIN(menu_item)); \
789 if(GTK_IS_ACCEL_LABEL(child)) \
790 gtk_accel_label_set_accel(GTK_ACCEL_LABEL(child), keyval, mods); \
798 const float pzx,
const float pzy);
808 float min,
float max,
float step,
float value,
int digits,
809 const char *format,
float factor,
static double dist(double x1, double y1, double x2, double y2)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
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)
static void dt_draw_cross(cairo_t *cr, const float zoom_scale, const float x, const float y)
static void dt_draw_shape_lines(struct dt_develop_t *dev, const dt_draw_dash_type_t dash_type, const gboolean source, cairo_t *cr, const int nb, const gboolean selected, const float zoom_scale, const float *points, const int points_count, const shape_draw_function_t *draw_shape_func, const cairo_line_cap_t line_cap)
Draw the lines of a mask shape.
void(* shape_draw_function_t)(struct dt_develop_t *dev, cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source)
_lib_location_type_t type
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
dt_masks_pressure_sensitivity_t
GdkModifierType dt_masks_get_accel_mods(dt_masks_interaction_t interaction)
static void dt_masks_gui_delta_to_image_abs(const dt_masks_form_gui_t *gui, float point[2])
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 apply_operation(struct dt_masks_form_group_t *pt, const dt_masks_state_t apply_state)
Apply a mask state operation on a group entry.
dt_masks_form_group_t * dt_masks_group_add_form(dt_develop_t *dev, dt_masks_form_t *grp, dt_masks_form_t *form)
void dt_masks_group_ungroup(dt_develop_t *dev, dt_masks_form_t *dest_grp, dt_masks_form_t *grp)
void dt_masks_gui_form_remove(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index)
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
dt_masks_type_t(* dt_masks_shape_buttons_type_f)(dt_iop_module_t *module, dt_masks_type_t type, gpointer user_data)
gboolean(* dt_masks_shape_buttons_start_f)(GtkWidget *button, dt_iop_module_t *module, dt_masks_type_t type, gpointer user_data)
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
void dt_masks_gui_set_dragging(dt_masks_form_gui_t *gui)
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
void dt_masks_calculate_source_pos_origin(dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, float *px, float *py, const int adding)
Compute preview-space source position for drawing the clone indicator.
void dt_masks_change_form_gui(dt_develop_t *dev, dt_masks_form_t *newform)
void dt_masks_remove_form(dt_develop_t *dev, dt_masks_form_t *form)
dt_masks_shape_button_index_t
@ DT_MASKS_SHAPE_INDEX_BRUSH
@ DT_MASKS_SHAPE_INDEX_ELLIPSE
@ DT_MASKS_SHAPE_INDEX_CIRCLE
@ DT_MASKS_SHAPE_INDEX_GRADIENT
@ DT_MASKS_SHAPE_INDEX_POLYGON
void dt_masks_gui_form_create(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, struct dt_iop_module_t *module)
void dt_masks_creation_mode_quit(dt_masks_form_gui_t *gui)
Exit mask creation mode, restoring cursor visibility and resetting GUI state.
gboolean dt_masks_reset_bezier_ctrl_points(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, dt_masks_points_states_t *state)
gboolean dt_masks_gui_remove(struct dt_iop_module_t *module, dt_masks_form_t *form, dt_masks_form_gui_t *gui, const int parentid)
remove a mask shape or node form from the GUI. This function is used with a popupmenu "Delete" action...
void dt_masks_events_post_expose(dt_develop_t *dev, struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gpt, const int 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_events_button_pressed(dt_develop_t *dev, struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state)
gboolean dt_masks_toggle_bezier_node_type(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, float node[2], float ctrl1[2], float ctrl2[2], dt_masks_points_states_t *state)
static void dt_masks_draw_source_preview(cairo_t *cr, const float zoom_scale, dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, const int adding)
int dt_masks_point_in_form_exact(const float *pts, int num_pts, const float *points, int points_start, int points_count)
Check whether any 2D point in pts[] lies inside the form points[].
void dt_masks_gui_reset_dragging(dt_masks_form_gui_t *gui)
void dt_masks_init_form_gui(dt_develop_t *dev, dt_masks_form_gui_t *gui)
void dt_masks_gui_init(struct dt_develop_t *dev)
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....
dt_masks_form_group_t * dt_masks_form_group_from_parentid(dt_develop_t *dev, int parentid, int formid)
Return the group entry for a (parent, form) pair.
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_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_handle, int *inside_source, float *dist, void *user_data)
Shape-specific callback for inside/border/segment hit testing.
static float dt_masks_get_form_size_from_nodes(const GList *points)
int dt_masks_events_mouse_moved(dt_develop_t *dev, struct dt_iop_module_t *module, double x, double y, double pressure, int which)
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, struct dt_iop_module_t *module, float posx, float posy)
void dt_masks_gui_form_save_creation(dt_develop_t *dev, struct dt_iop_module_t *module, dt_masks_form_t *form, dt_masks_form_gui_t *gui)
Save the form creation right after a shape has been finished drawing.
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
void dt_masks_select_form(dt_develop_t *dev, struct dt_iop_module_t *module, dt_masks_form_t *sel)
Select or clear the current mask form, notifying the owning module if needed.
static int dt_masks_gui_selected_handle_border_index(const dt_masks_form_gui_t *gui)
GtkWidget * dt_masks_shape_buttons_create(const dt_masks_shape_buttons_config_t *config)
Build a synchronized toolbar for creating masks shapes.
float dt_masks_get_set_conf_value(dt_masks_form_t *form, char *feature, float new_value, float v_min, float v_max, dt_masks_increment_t increment, const int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
void dt_masks_clear_form_gui(dt_develop_t *dev)
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *gui)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *gui, const int index, const int nb, 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.
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.
int dt_masks_group_index_from_formid(const dt_masks_form_t *group_form, int formid)
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
int dt_masks_center_view_on_form(struct dt_develop_t *dev, const struct dt_masks_form_t *form)
float dt_masks_form_get_interaction_value(dt_develop_t *dev, dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction)
dt_masks_form_group_t * dt_masks_form_get_selected_group(const struct dt_masks_form_t *form, const struct dt_masks_form_gui_t *gui)
static void dt_masks_draw_preview_shape(struct dt_develop_t *dev, cairo_t *cr, const float zoom_scale, const int num_points, float *points, const int points_count, float *border, const int border_count, const shape_draw_function_t *draw_shape, const cairo_line_cap_t shape_cap, const cairo_line_cap_t border_cap, const gboolean save_restore, const gboolean source)
static float dt_masks_border_from_projected_handle(dt_develop_t *dev, const float node[2], const float projected_image_pos[2], const float scale_ref)
int dt_masks_events_mouse_leave(struct dt_iop_module_t *module)
void dt_masks_form_invalidate_gravity_center(struct dt_masks_form_t *form)
void(* dt_masks_shape_buttons_notify_f)(GtkWidget *button, dt_iop_module_t *module, dt_masks_type_t type, gpointer user_data)
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....
void dt_masks_reset_show_masks_icons(dt_develop_t *dev)
gboolean dt_masks_point_is_within_radius(const float px, const float py, const float cx, const float cy, const float radius)
Check if a point (px,py) is inside a radius from a center point (cx,cy)
static void dt_masks_preview_buffers_cleanup(dt_masks_preview_buffers_t *buffers)
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)
static gboolean dt_masks_gui_change_affects_selected_node_or_all(const dt_masks_form_gui_t *gui, const int index)
static gboolean dt_masks_gui_should_hit_test(dt_masks_form_gui_t *gui)
static void dt_masks_gui_delta_from_raw_anchor(dt_develop_t *dev, const dt_masks_form_gui_t *gui, const float anchor[2], float *delta_x, float *delta_y)
int dt_masks_events_mouse_scrolled(dt_develop_t *dev, struct dt_iop_module_t *module, double x, double y, int up, uint32_t state, int delta_y)
float dt_masks_apply_increment(float current, float amount, dt_masks_increment_t increment, int flow)
Apply a scroll increment to a scalar value.
float dt_masks_get_set_conf_value_with_toast(dt_masks_form_t *form, const char *feature, float amount, float v_min, float v_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.
int dt_masks_events_mouse_enter(struct dt_iop_module_t *module)
float dt_masks_rotate_with_anchor(dt_develop_t *dev, const float anchor[2], const float center[2], dt_masks_form_gui_t *gui)
Rotate a mask shape around its center. WARNING: gui->delta will be updated with the new position afte...
dt_masks_shape_buttons_flags_t
@ DT_MASKS_SHAPE_BUTTONS_GRADIENT
@ DT_MASKS_SHAPE_BUTTONS_NONE
@ DT_MASKS_SHAPE_BUTTONS_CIRCLE
@ DT_MASKS_SHAPE_BUTTONS_BRUSH
@ DT_MASKS_SHAPE_BUTTONS_ALL
@ DT_MASKS_SHAPE_BUTTONS_ELLIPSE
@ DT_MASKS_SHAPE_BUTTONS_POLYGON
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.
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, struct dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
dt_masks_form_group_t * dt_masks_form_group_find_entry(dt_masks_form_t *group_form, int formid, int *out_index)
Find the group-membership entry for formid inside group_form's own points list (not recursive into su...
int dt_masks_events_button_released(dt_develop_t *dev, struct dt_iop_module_t *module, double x, double y, int which, uint32_t state)
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_reset_form_gui(dt_develop_t *dev)
void dt_masks_append_form(dt_develop_t *dev, dt_masks_form_t *form)
void dt_masks_soft_reset_form_gui(dt_masks_form_gui_t *gui)
dt_masks_form_group_t * dt_masks_form_get_selected_group_live(const struct dt_masks_form_t *form, const struct dt_masks_form_gui_t *gui)
Return the currently selected group entry, resolving to the live form group when the GUI is operating...
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *gui, dt_masks_form_t *form)
Initialize the clone source position based on current GUI state.
int dt_masks_events_key_pressed(dt_develop_t *dev, struct dt_iop_module_t *module, GdkEventKey *event)
void dt_masks_iop_value_changed_callback(GtkWidget *widget, struct dt_iop_module_t *module)
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)
void dt_masks_gui_cleanup(struct dt_develop_t *dev)
gboolean dt_masks_creation_mode_enter(dt_develop_t *dev, dt_iop_module_t *module, const dt_masks_type_t type)
Enter mask creation mode for a given shape type.
static void dt_masks_project_on_line(const float cursor[2], const float node[2], const float handle[2], float point[2])
void dt_masks_gui_form_test_create(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
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.
gboolean dt_masks_is_anything_hovered(const dt_masks_form_gui_t *mask_gui)
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)
Shared selection logic for node/handle/segment hit testing.
gboolean dt_masks_form_get_gravity_center(dt_develop_t *dev, const struct dt_masks_form_t *form, float center[2], float *area)
void dt_masks_gui_populate_interaction_sliders(GtkWidget *menu, dt_develop_t *dev, dt_masks_form_t *form, dt_masks_form_group_t *op_form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
Append the full set of shape-parameter sliders (size/fading/rotation or curvature/fade/rotation depen...
void dt_masks_form_update_gravity_center(dt_develop_t *dev, struct dt_masks_form_t *form)
void dt_masks_group_update_name(dt_iop_module_t *module)
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_remove_node(struct dt_iop_module_t *module, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index, int node_index)
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.
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *gui, const int index, const float *points, const int points_count, const int node_count, const float zoom_scale)
static void dt_masks_gui_delta_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *gui, const uint32_t state)
Decide initial source positioning mode for clone masks.
void dt_masks_iop_combo_populate(GtkWidget *w, void *module)
dt_masks_form_group_t * dt_masks_form_group_find_any(dt_develop_t *dev, int formid, int *out_parentid)
Find any group that currently references formid, searching every top-level group in dev->forms and th...
void dt_masks_set_visible_form(struct dt_develop_t *dev, dt_masks_form_t *form)
GtkWidget * dt_masks_gui_add_interaction_slider(GtkWidget *menu, const char *label, dt_masks_form_group_t *form_group, dt_masks_interaction_t interaction, dt_masks_increment_t increment, float min, float max, float step, float value, int digits, const char *format, float factor, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
Append a bauhaus-slider menu item to a mask context menu, bound to one shape interaction (size,...
#define dt_pixelpipe_cache_free_align(mem)
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
unsigned __int64 uint64_t
struct dt_masks_gui_center_point_t::@27 main
struct dt_masks_gui_center_point_t::@28 source