/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * * pikanpointdeformationtool.c * Copyright (C) 2013 Marek Dvoroznak * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include #include #include "libpikamath/pikamath.h" #include "libpikawidgets/pikawidgets.h" #include "tools-types.h" #include "config/pikaguiconfig.h" /* playground */ #include "gegl/pika-gegl-utils.h" #include "gegl/pika-gegl-apply-operation.h" #include "core/pika.h" #include "core/pikadrawable.h" #include "core/pikaimage.h" #include "core/pikaprogress.h" #include "core/pikaprojection.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikawidgets-utils.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikacanvasbufferpreview.h" #include "pikanpointdeformationtool.h" #include "pikanpointdeformationoptions.h" #include "pikatoolcontrol.h" #include "pikatooloptions-gui.h" #include "pika-intl.h" //#define PIKA_NPD_DEBUG #define PIKA_NPD_MAXIMUM_DEFORMATION_DELAY 100000 /* 100000 microseconds == 10 FPS */ #define PIKA_NPD_DRAW_INTERVAL 50 /* 50 milliseconds == 20 FPS */ static void pika_n_point_deformation_tool_start (PikaNPointDeformationTool *npd_tool, PikaDisplay *display); static void pika_n_point_deformation_tool_halt (PikaNPointDeformationTool *npd_tool); static void pika_n_point_deformation_tool_commit (PikaNPointDeformationTool *npd_tool); static void pika_n_point_deformation_tool_set_options (PikaNPointDeformationTool *npd_tool, PikaNPointDeformationOptions *npd_options); static void pika_n_point_deformation_tool_options_notify (PikaTool *tool, PikaToolOptions *options, const GParamSpec *pspec); static void pika_n_point_deformation_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display); static void pika_n_point_deformation_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display); static gboolean pika_n_point_deformation_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display); static void pika_n_point_deformation_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display); static void pika_n_point_deformation_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display); static void pika_n_point_deformation_tool_draw (PikaDrawTool *draw_tool); static void pika_n_point_deformation_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display); static void pika_n_point_deformation_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display); static void pika_n_point_deformation_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display); static void pika_n_point_deformation_tool_clear_selected_points_list (PikaNPointDeformationTool *npd_tool); static gboolean pika_n_point_deformation_tool_add_cp_to_selection (PikaNPointDeformationTool *npd_tool, NPDControlPoint *cp); static gboolean pika_n_point_deformation_tool_is_cp_in_area (NPDControlPoint *cp, gfloat x0, gfloat y0, gfloat x1, gfloat y1, gfloat offset_x, gfloat offset_y, gfloat cp_radius); static void pika_n_point_deformation_tool_remove_cp_from_selection (PikaNPointDeformationTool *npd_tool, NPDControlPoint *cp); static gpointer pika_n_point_deformation_tool_deform_thread_func (gpointer data); static gboolean pika_n_point_deformation_tool_canvas_update_timeout (PikaNPointDeformationTool *npd_tool); static void pika_n_point_deformation_tool_perform_deformation (PikaNPointDeformationTool *npd_tool); static void pika_n_point_deformation_tool_halt_threads (PikaNPointDeformationTool *npd_tool); static void pika_n_point_deformation_tool_apply_deformation (PikaNPointDeformationTool *npd_tool); #ifdef PIKA_NPD_DEBUG #define pika_npd_debug(x) g_printf x #else #define pika_npd_debug(x) #endif G_DEFINE_TYPE (PikaNPointDeformationTool, pika_n_point_deformation_tool, PIKA_TYPE_DRAW_TOOL) #define parent_class pika_n_point_deformation_tool_parent_class void pika_n_point_deformation_tool_register (PikaToolRegisterCallback callback, gpointer data) { /* we should not know that "data" is a Gimp*, but what the heck this * is experimental playground stuff */ if (PIKA_GUI_CONFIG (PIKA (data)->config)->playground_npd_tool) (* callback) (PIKA_TYPE_N_POINT_DEFORMATION_TOOL, PIKA_TYPE_N_POINT_DEFORMATION_OPTIONS, pika_n_point_deformation_options_gui, 0, "pika-n-point-deformation-tool", _("N-Point Deformation"), _("N-Point Deformation Tool: Rubber-like deformation of " "image using points"), N_("_N-Point Deformation"), "N", NULL, PIKA_HELP_TOOL_N_POINT_DEFORMATION, PIKA_ICON_TOOL_N_POINT_DEFORMATION, data); } static void pika_n_point_deformation_tool_class_init (PikaNPointDeformationToolClass *klass) { PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass); PikaDrawToolClass *draw_tool_class = PIKA_DRAW_TOOL_CLASS (klass); tool_class->options_notify = pika_n_point_deformation_tool_options_notify; tool_class->button_press = pika_n_point_deformation_tool_button_press; tool_class->button_release = pika_n_point_deformation_tool_button_release; tool_class->key_press = pika_n_point_deformation_tool_key_press; tool_class->modifier_key = pika_n_point_deformation_tool_modifier_key; tool_class->control = pika_n_point_deformation_tool_control; tool_class->motion = pika_n_point_deformation_tool_motion; tool_class->oper_update = pika_n_point_deformation_tool_oper_update; tool_class->cursor_update = pika_n_point_deformation_tool_cursor_update; draw_tool_class->draw = pika_n_point_deformation_tool_draw; } static void pika_n_point_deformation_tool_init (PikaNPointDeformationTool *npd_tool) { PikaTool *tool = PIKA_TOOL (npd_tool); pika_tool_control_set_precision (tool->control, PIKA_CURSOR_PRECISION_SUBPIXEL); pika_tool_control_set_tool_cursor (tool->control, PIKA_TOOL_CURSOR_PERSPECTIVE); pika_tool_control_set_preserve (tool->control, FALSE); pika_tool_control_set_wants_click (tool->control, TRUE); pika_tool_control_set_wants_all_key_events (tool->control, TRUE); pika_tool_control_set_handle_empty_image (tool->control, FALSE); pika_tool_control_set_dirty_mask (tool->control, PIKA_DIRTY_IMAGE | PIKA_DIRTY_IMAGE_STRUCTURE | PIKA_DIRTY_DRAWABLE | PIKA_DIRTY_SELECTION | PIKA_DIRTY_ACTIVE_DRAWABLE); } static void pika_n_point_deformation_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); switch (action) { case PIKA_TOOL_ACTION_PAUSE: case PIKA_TOOL_ACTION_RESUME: break; case PIKA_TOOL_ACTION_HALT: pika_n_point_deformation_tool_halt (npd_tool); break; case PIKA_TOOL_ACTION_COMMIT: pika_n_point_deformation_tool_commit (npd_tool); break; } PIKA_TOOL_CLASS (parent_class)->control (tool, action, display); } static void pika_n_point_deformation_tool_start (PikaNPointDeformationTool *npd_tool, PikaDisplay *display) { PikaTool *tool = PIKA_TOOL (npd_tool); PikaNPointDeformationOptions *npd_options; PikaImage *image; GeglBuffer *source_buffer; GeglBuffer *preview_buffer; NPDModel *model; npd_options = PIKA_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); image = pika_display_get_image (display); tool->display = display; g_list_free (tool->drawables); tool->drawables = pika_image_get_selected_drawables (image); npd_tool->active = TRUE; g_return_if_fail (g_list_length (tool->drawables) == 1); /* create GEGL graph */ source_buffer = pika_drawable_get_buffer (tool->drawables->data); preview_buffer = gegl_buffer_new (gegl_buffer_get_extent (source_buffer), babl_format ("cairo-ARGB32")); npd_tool->graph = gegl_node_new (); npd_tool->source = gegl_node_new_child (npd_tool->graph, "operation", "gegl:buffer-source", "buffer", source_buffer, NULL); npd_tool->npd_node = gegl_node_new_child (npd_tool->graph, "operation", "gegl:npd", NULL); npd_tool->sink = gegl_node_new_child (npd_tool->graph, "operation", "gegl:write-buffer", "buffer", preview_buffer, NULL); gegl_node_link_many (npd_tool->source, npd_tool->npd_node, npd_tool->sink, NULL); /* initialize some options */ g_object_set (G_OBJECT (npd_options), "mesh-visible", TRUE, NULL); pika_n_point_deformation_options_set_sensitivity (npd_options, TRUE); /* compute and get model */ gegl_node_process (npd_tool->npd_node); gegl_node_get (npd_tool->npd_node, "model", &model, NULL); npd_tool->model = model; npd_tool->preview_buffer = preview_buffer; npd_tool->selected_cp = NULL; npd_tool->hovering_cp = NULL; npd_tool->selected_cps = NULL; npd_tool->rubber_band = FALSE; npd_tool->lattice_points = g_new (PikaVector2, 5 * model->hidden_model->num_of_bones); pika_item_get_offset (PIKA_ITEM (tool->drawables->data), &npd_tool->offset_x, &npd_tool->offset_y); pika_npd_debug (("offset: %f %f\n", npd_tool->offset_x, npd_tool->offset_y)); pika_draw_tool_start (PIKA_DRAW_TOOL (npd_tool), display); pika_n_point_deformation_tool_perform_deformation (npd_tool); /* hide original image */ pika_item_set_visible (PIKA_ITEM (tool->drawables->data), FALSE, FALSE); pika_image_flush (image); /* create and start a deformation thread */ npd_tool->deform_thread = g_thread_new ("deform thread", (GThreadFunc) pika_n_point_deformation_tool_deform_thread_func, npd_tool); /* create and start canvas update timeout */ npd_tool->draw_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, PIKA_NPD_DRAW_INTERVAL, (GSourceFunc) pika_n_point_deformation_tool_canvas_update_timeout, npd_tool, NULL); } static void pika_n_point_deformation_tool_commit (PikaNPointDeformationTool *npd_tool) { PikaTool *tool = PIKA_TOOL (npd_tool); if (npd_tool->active) { pika_n_point_deformation_tool_halt_threads (npd_tool); pika_tool_control_push_preserve (tool->control, TRUE); pika_n_point_deformation_tool_apply_deformation (npd_tool); pika_tool_control_pop_preserve (tool->control); /* show original/deformed image */ pika_item_set_visible (PIKA_ITEM (tool->drawables->data), TRUE, FALSE); pika_image_flush (pika_display_get_image (tool->display)); npd_tool->active = FALSE; } } static void pika_n_point_deformation_tool_halt (PikaNPointDeformationTool *npd_tool) { PikaTool *tool = PIKA_TOOL (npd_tool); PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (npd_tool); PikaNPointDeformationOptions *npd_options; npd_options = PIKA_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); if (npd_tool->active) { pika_n_point_deformation_tool_halt_threads (npd_tool); /* show original/deformed image */ pika_item_set_visible (PIKA_ITEM (tool->drawables->data), TRUE, FALSE); pika_image_flush (pika_display_get_image (tool->display)); /* disable some options */ pika_n_point_deformation_options_set_sensitivity (npd_options, FALSE); npd_tool->active = FALSE; } if (pika_draw_tool_is_active (draw_tool)) pika_draw_tool_stop (draw_tool); pika_n_point_deformation_tool_clear_selected_points_list (npd_tool); g_clear_object (&npd_tool->graph); npd_tool->source = NULL; npd_tool->npd_node = NULL; npd_tool->sink = NULL; g_clear_object (&npd_tool->preview_buffer); g_clear_pointer (&npd_tool->lattice_points, g_free); tool->display = NULL; g_list_free (tool->drawables); tool->drawables = NULL; } static void pika_n_point_deformation_tool_set_options (PikaNPointDeformationTool *npd_tool, PikaNPointDeformationOptions *npd_options) { gegl_node_set (npd_tool->npd_node, "square-size", (gint) npd_options->square_size, "rigidity", (gint) npd_options->rigidity, "asap-deformation", npd_options->asap_deformation, "mls-weights", npd_options->mls_weights, "mls-weights-alpha", npd_options->mls_weights_alpha, NULL); } static void pika_n_point_deformation_tool_options_notify (PikaTool *tool, PikaToolOptions *options, const GParamSpec *pspec) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); PikaNPointDeformationOptions *npd_options = PIKA_N_POINT_DEFORMATION_OPTIONS (options); PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); PIKA_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec); if (! npd_tool->active) return; pika_draw_tool_pause (draw_tool); pika_npd_debug (("npd options notify\n")); pika_n_point_deformation_tool_set_options (npd_tool, npd_options); pika_draw_tool_resume (draw_tool); } static gboolean pika_n_point_deformation_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); switch (kevent->keyval) { case GDK_KEY_BackSpace: /* if there is at least one control point, remove last added * control point */ if (npd_tool->model && npd_tool->model->control_points && npd_tool->model->control_points->len > 0) { GArray *cps = npd_tool->model->control_points; NPDControlPoint *cp = &g_array_index (cps, NPDControlPoint, cps->len - 1); pika_npd_debug (("removing last cp %p\n", cp)); pika_n_point_deformation_tool_remove_cp_from_selection (npd_tool, cp); npd_remove_control_point (npd_tool->model, cp); } break; case GDK_KEY_Delete: if (npd_tool->model && npd_tool->selected_cps) { /* if there is at least one selected control point, remove it */ npd_remove_control_points (npd_tool->model, npd_tool->selected_cps); pika_n_point_deformation_tool_clear_selected_points_list (npd_tool); } break; case GDK_KEY_Return: case GDK_KEY_KP_Enter: case GDK_KEY_ISO_Enter: pika_tool_control (tool, PIKA_TOOL_ACTION_COMMIT, display); break; case GDK_KEY_Escape: pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); break; default: return FALSE; } return TRUE; } static void pika_n_point_deformation_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display) { } static void pika_n_point_deformation_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); PikaCursorModifier modifier = PIKA_CURSOR_MODIFIER_PLUS; if (! npd_tool->active) { modifier = PIKA_CURSOR_MODIFIER_NONE; } else if (npd_tool->hovering_cp) { modifier = PIKA_CURSOR_MODIFIER_MOVE; } pika_tool_control_set_cursor_modifier (tool->control, modifier); PIKA_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); } static void pika_n_point_deformation_tool_clear_selected_points_list (PikaNPointDeformationTool *npd_tool) { if (npd_tool->selected_cps) { g_list_free (npd_tool->selected_cps); npd_tool->selected_cps = NULL; } } static gboolean pika_n_point_deformation_tool_add_cp_to_selection (PikaNPointDeformationTool *npd_tool, NPDControlPoint *cp) { if (! g_list_find (npd_tool->selected_cps, cp)) { npd_tool->selected_cps = g_list_append (npd_tool->selected_cps, cp); return TRUE; } return FALSE; } static void pika_n_point_deformation_tool_remove_cp_from_selection (PikaNPointDeformationTool *npd_tool, NPDControlPoint *cp) { npd_tool->selected_cps = g_list_remove (npd_tool->selected_cps, cp); } static void pika_n_point_deformation_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); if (display != tool->display) { /* this is the first click on the drawable - just start the tool */ pika_n_point_deformation_tool_start (npd_tool, display); } npd_tool->selected_cp = NULL; if (press_type == PIKA_BUTTON_PRESS_NORMAL) { NPDControlPoint *cp = npd_tool->hovering_cp; if (cp) { /* there is a control point at cursor's position */ npd_tool->selected_cp = cp; if (! g_list_find (npd_tool->selected_cps, cp)) { /* control point isn't selected, so we can add it to the * list of selected control points */ if (! (state & pika_get_extend_selection_mask ())) { /* isn't pressed, so this isn't a * multiselection - clear the list of selected * control points */ pika_n_point_deformation_tool_clear_selected_points_list (npd_tool); } pika_n_point_deformation_tool_add_cp_to_selection (npd_tool, cp); } else if (state & pika_get_extend_selection_mask ()) { /* control point is selected and is pressed - * remove control point from selected points */ pika_n_point_deformation_tool_remove_cp_from_selection (npd_tool, cp); } } npd_tool->start_x = coords->x; npd_tool->start_y = coords->y; npd_tool->last_x = coords->x; npd_tool->last_y = coords->y; } pika_tool_control_activate (tool->control); } static gboolean pika_n_point_deformation_tool_is_cp_in_area (NPDControlPoint *cp, gfloat x0, gfloat y0, gfloat x1, gfloat y1, gfloat offset_x, gfloat offset_y, gfloat cp_radius) { NPDPoint p = cp->point; p.x += offset_x; p.y += offset_y; return p.x >= x0 - cp_radius && p.x <= x1 + cp_radius && p.y >= y0 - cp_radius && p.y <= y1 + cp_radius; } static void pika_n_point_deformation_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); pika_tool_control_halt (tool->control); pika_draw_tool_pause (PIKA_DRAW_TOOL (npd_tool)); if (release_type == PIKA_BUTTON_RELEASE_CLICK) { if (! npd_tool->hovering_cp) { NPDPoint p; pika_npd_debug (("cp doesn't exist, adding\n")); p.x = coords->x - npd_tool->offset_x; p.y = coords->y - npd_tool->offset_y; npd_add_control_point (npd_tool->model, &p); } } else if (release_type == PIKA_BUTTON_RELEASE_NORMAL) { if (npd_tool->rubber_band) { GArray *cps = npd_tool->model->control_points; gint x0 = MIN (npd_tool->start_x, npd_tool->cursor_x); gint y0 = MIN (npd_tool->start_y, npd_tool->cursor_y); gint x1 = MAX (npd_tool->start_x, npd_tool->cursor_x); gint y1 = MAX (npd_tool->start_y, npd_tool->cursor_y); gint i; if (! (state & pika_get_extend_selection_mask ())) { /* isn't pressed, so we want a clear selection */ pika_n_point_deformation_tool_clear_selected_points_list (npd_tool); } for (i = 0; i < cps->len; i++) { NPDControlPoint *cp = &g_array_index (cps, NPDControlPoint, i); if (pika_n_point_deformation_tool_is_cp_in_area (cp, x0, y0, x1, y1, npd_tool->offset_x, npd_tool->offset_y, npd_tool->cp_scaled_radius)) { /* control point is situated in an area defined by * rubber band */ pika_n_point_deformation_tool_add_cp_to_selection (npd_tool, cp); pika_npd_debug (("%p selected\n", cp)); } } } } else if (release_type == PIKA_BUTTON_RELEASE_CANCEL) { pika_npd_debug (("pika_button_release_cancel\n")); } npd_tool->rubber_band = FALSE; pika_draw_tool_resume (PIKA_DRAW_TOOL (npd_tool)); } static void pika_n_point_deformation_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); pika_draw_tool_pause (draw_tool); if (npd_tool->active) { NPDModel *model = npd_tool->model; PikaDisplayShell *shell = pika_display_get_shell (display); NPDPoint p; npd_tool->cp_scaled_radius = model->control_point_radius / shell->scale_x; p.x = coords->x - npd_tool->offset_x; p.y = coords->y - npd_tool->offset_y; npd_tool->hovering_cp = npd_get_control_point_with_radius_at (model, &p, npd_tool->cp_scaled_radius); } npd_tool->cursor_x = coords->x; npd_tool->cursor_y = coords->y; pika_draw_tool_resume (draw_tool); } static void pika_n_point_deformation_tool_prepare_lattice (PikaNPointDeformationTool *npd_tool) { NPDHiddenModel *hm = npd_tool->model->hidden_model; PikaVector2 *points = npd_tool->lattice_points; gint i, j; for (i = 0; i < hm->num_of_bones; i++) { NPDBone *bone = &hm->current_bones[i]; for (j = 0; j < 4; j++) pika_vector2_set (&points[5 * i + j], bone->points[j].x, bone->points[j].y); pika_vector2_set (&points[5 * i + j], bone->points[0].x, bone->points[0].y); } } static void pika_n_point_deformation_tool_draw_lattice (PikaNPointDeformationTool *npd_tool) { PikaVector2 *points = npd_tool->lattice_points; gint n_squares = npd_tool->model->hidden_model->num_of_bones; gint i; for (i = 0; i < n_squares; i++) pika_draw_tool_add_lines (PIKA_DRAW_TOOL (npd_tool), &points[5 * i], 5, NULL, FALSE); } static void pika_n_point_deformation_tool_draw (PikaDrawTool *draw_tool) { PikaNPointDeformationTool *npd_tool; PikaNPointDeformationOptions *npd_options; NPDModel *model; gint x0, y0, x1, y1; gint i; npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (draw_tool); npd_options = PIKA_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); model = npd_tool->model; g_return_if_fail (model != NULL); /* draw lattice */ if (npd_options->mesh_visible) pika_n_point_deformation_tool_draw_lattice (npd_tool); x0 = MIN (npd_tool->start_x, npd_tool->cursor_x); y0 = MIN (npd_tool->start_y, npd_tool->cursor_y); x1 = MAX (npd_tool->start_x, npd_tool->cursor_x); y1 = MAX (npd_tool->start_y, npd_tool->cursor_y); for (i = 0; i < model->control_points->len; i++) { NPDControlPoint *cp = &g_array_index (model->control_points, NPDControlPoint, i); NPDPoint p = cp->point; PikaHandleType handle_type; p.x += npd_tool->offset_x; p.y += npd_tool->offset_y; handle_type = PIKA_HANDLE_CIRCLE; /* check if cursor is hovering over a control point or if a * control point is situated in an area defined by rubber band */ if (cp == npd_tool->hovering_cp || (npd_tool->rubber_band && pika_n_point_deformation_tool_is_cp_in_area (cp, x0, y0, x1, y1, npd_tool->offset_x, npd_tool->offset_y, npd_tool->cp_scaled_radius))) { handle_type = PIKA_HANDLE_FILLED_CIRCLE; } pika_draw_tool_add_handle (draw_tool, handle_type, p.x, p.y, PIKA_TOOL_HANDLE_SIZE_CIRCLE, PIKA_TOOL_HANDLE_SIZE_CIRCLE, PIKA_HANDLE_ANCHOR_CENTER); if (g_list_find (npd_tool->selected_cps, cp)) { pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_SQUARE, p.x, p.y, PIKA_TOOL_HANDLE_SIZE_CIRCLE, PIKA_TOOL_HANDLE_SIZE_CIRCLE, PIKA_HANDLE_ANCHOR_CENTER); } } if (npd_tool->rubber_band) { /* draw a rubber band */ pika_draw_tool_add_rectangle (draw_tool, FALSE, x0, y0, x1 - x0, y1 - y0); } if (npd_tool->preview_buffer) { PikaCanvasItem *item; item = pika_canvas_buffer_preview_new (pika_display_get_shell (draw_tool->display), npd_tool->preview_buffer); pika_draw_tool_add_preview (draw_tool, item); g_object_unref (item); } PIKA_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool); } static void pika_n_point_deformation_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display) { PikaNPointDeformationTool *npd_tool = PIKA_N_POINT_DEFORMATION_TOOL (tool); PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); pika_draw_tool_pause (draw_tool); if (npd_tool->selected_cp) { GList *list; gdouble shift_x = coords->x - npd_tool->last_x; gdouble shift_y = coords->y - npd_tool->last_y; for (list = npd_tool->selected_cps; list; list = g_list_next (list)) { NPDControlPoint *cp = list->data; cp->point.x += shift_x; cp->point.y += shift_y; } } else { /* activate a rubber band selection */ npd_tool->rubber_band = TRUE; } npd_tool->cursor_x = coords->x; npd_tool->cursor_y = coords->y; npd_tool->last_x = coords->x; npd_tool->last_y = coords->y; pika_draw_tool_resume (draw_tool); } static gboolean pika_n_point_deformation_tool_canvas_update_timeout (PikaNPointDeformationTool *npd_tool) { if (! PIKA_TOOL (npd_tool)->drawables->data) return FALSE; pika_npd_debug (("canvas update thread\n")); pika_draw_tool_pause (PIKA_DRAW_TOOL(npd_tool)); pika_draw_tool_resume (PIKA_DRAW_TOOL(npd_tool)); pika_npd_debug (("canvas update thread stop\n")); return TRUE; } static gpointer pika_n_point_deformation_tool_deform_thread_func (gpointer data) { PikaNPointDeformationTool *npd_tool = data; PikaNPointDeformationOptions *npd_options; guint64 start, duration; npd_options = PIKA_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); npd_tool->deformation_active = TRUE; while (npd_tool->deformation_active) { start = g_get_monotonic_time (); pika_n_point_deformation_tool_perform_deformation (npd_tool); if (npd_options->mesh_visible) pika_n_point_deformation_tool_prepare_lattice (npd_tool); duration = g_get_monotonic_time () - start; if (duration < PIKA_NPD_MAXIMUM_DEFORMATION_DELAY) { g_usleep (PIKA_NPD_MAXIMUM_DEFORMATION_DELAY - duration); } } pika_npd_debug (("deform thread exit\n")); return NULL; } static void pika_n_point_deformation_tool_perform_deformation (PikaNPointDeformationTool *npd_tool) { GObject *operation; g_object_get (npd_tool->npd_node, "gegl-operation", &operation, NULL); pika_npd_debug (("gegl_operation_invalidate\n")); gegl_operation_invalidate (GEGL_OPERATION (operation), NULL, FALSE); g_object_unref (operation); pika_npd_debug (("gegl_node_process\n")); gegl_node_process (npd_tool->sink); } static void pika_n_point_deformation_tool_halt_threads (PikaNPointDeformationTool *npd_tool) { if (! npd_tool->deformation_active) return; pika_npd_debug (("waiting for deform thread to finish\n")); npd_tool->deformation_active = FALSE; /* wait for deformation thread to finish its work */ if (npd_tool->deform_thread) { g_thread_join (npd_tool->deform_thread); npd_tool->deform_thread = NULL; } /* remove canvas update timeout */ if (npd_tool->draw_timeout_id) { g_source_remove (npd_tool->draw_timeout_id); npd_tool->draw_timeout_id = 0; } pika_npd_debug (("finished\n")); } static void pika_n_point_deformation_tool_apply_deformation (PikaNPointDeformationTool *npd_tool) { PikaTool *tool = PIKA_TOOL (npd_tool); PikaNPointDeformationOptions *npd_options; GeglBuffer *buffer; PikaImage *image; gint width, height, prev; npd_options = PIKA_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); image = pika_display_get_image (tool->display); buffer = pika_drawable_get_buffer (tool->drawables->data); width = gegl_buffer_get_width (buffer); height = gegl_buffer_get_height (buffer); prev = npd_options->rigidity; npd_options->rigidity = 0; pika_n_point_deformation_tool_set_options (npd_tool, npd_options); npd_options->rigidity = prev; pika_drawable_push_undo (tool->drawables->data, _("N-Point Deformation"), NULL, 0, 0, width, height); pika_gegl_apply_operation (NULL, NULL, _("N-Point Deformation"), npd_tool->npd_node, pika_drawable_get_buffer (tool->drawables->data), NULL, FALSE); pika_drawable_update (tool->drawables->data, 0, 0, width, height); pika_projection_flush (pika_image_get_projection (image)); } #undef pika_npd_debug #ifdef PIKA_NPD_DEBUG #undef PIKA_NPD_DEBUG #endif