/* 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): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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 "libpikamath/pikamath.h" #include "libpikawidgets/pikawidgets.h" #include "tools-types.h" #include "core/pika-transform-utils.h" #include "core/pikaimage.h" #include "paint/pikaperspectiveclone.h" #include "paint/pikaperspectivecloneoptions.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikaviewablebox.h" #include "widgets/pikawidgets-utils.h" #include "display/pikacanvasgroup.h" #include "display/pikadisplay.h" #include "display/pikatooltransformgrid.h" #include "pikaperspectiveclonetool.h" #include "pikacloneoptions-gui.h" #include "pikatoolcontrol.h" #include "pika-intl.h" /* index into trans_info array */ enum { X0, Y0, X1, Y1, X2, Y2, X3, Y3, PIVOT_X, PIVOT_Y }; static void pika_perspective_clone_tool_constructed (GObject *object); static gboolean pika_perspective_clone_tool_initialize (PikaTool *tool, PikaDisplay *display, GError **error); static gboolean pika_perspective_clone_tool_has_display (PikaTool *tool, PikaDisplay *display); static PikaDisplay * pika_perspective_clone_tool_has_image (PikaTool *tool, PikaImage *image); static void pika_perspective_clone_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display); static void pika_perspective_clone_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display); static void pika_perspective_clone_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display); static void pika_perspective_clone_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display); static void pika_perspective_clone_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display); static void pika_perspective_clone_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display); static void pika_perspective_clone_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display); static void pika_perspective_clone_tool_options_notify (PikaTool *tool, PikaToolOptions *options, const GParamSpec *pspec); static void pika_perspective_clone_tool_draw (PikaDrawTool *draw_tool); static void pika_perspective_clone_tool_halt (PikaPerspectiveCloneTool *clone_tool); static void pika_perspective_clone_tool_bounds (PikaPerspectiveCloneTool *clone_tool, PikaDisplay *display); static void pika_perspective_clone_tool_prepare (PikaPerspectiveCloneTool *clone_tool); static void pika_perspective_clone_tool_recalc_matrix (PikaPerspectiveCloneTool *clone_tool, PikaToolWidget *widget); static void pika_perspective_clone_tool_widget_changed (PikaToolWidget *widget, PikaPerspectiveCloneTool *clone_tool); static void pika_perspective_clone_tool_widget_status (PikaToolWidget *widget, const gchar *status, PikaPerspectiveCloneTool *clone_tool); static GtkWidget * pika_perspective_clone_options_gui (PikaToolOptions *tool_options); G_DEFINE_TYPE (PikaPerspectiveCloneTool, pika_perspective_clone_tool, PIKA_TYPE_BRUSH_TOOL) #define parent_class pika_perspective_clone_tool_parent_class void pika_perspective_clone_tool_register (PikaToolRegisterCallback callback, gpointer data) { (* callback) (PIKA_TYPE_PERSPECTIVE_CLONE_TOOL, PIKA_TYPE_PERSPECTIVE_CLONE_OPTIONS, pika_perspective_clone_options_gui, PIKA_PAINT_OPTIONS_CONTEXT_MASK | PIKA_CONTEXT_PROP_MASK_PATTERN, "pika-perspective-clone-tool", _("Perspective Clone"), _("Perspective Clone Tool: Clone from an image source " "after applying a perspective transformation"), N_("_Perspective Clone"), NULL, NULL, PIKA_HELP_TOOL_PERSPECTIVE_CLONE, PIKA_ICON_TOOL_PERSPECTIVE_CLONE, data); } static void pika_perspective_clone_tool_class_init (PikaPerspectiveCloneToolClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass); PikaDrawToolClass *draw_tool_class = PIKA_DRAW_TOOL_CLASS (klass); object_class->constructed = pika_perspective_clone_tool_constructed; tool_class->initialize = pika_perspective_clone_tool_initialize; tool_class->has_display = pika_perspective_clone_tool_has_display; tool_class->has_image = pika_perspective_clone_tool_has_image; tool_class->control = pika_perspective_clone_tool_control; tool_class->button_press = pika_perspective_clone_tool_button_press; tool_class->button_release = pika_perspective_clone_tool_button_release; tool_class->motion = pika_perspective_clone_tool_motion; tool_class->modifier_key = pika_perspective_clone_tool_modifier_key; tool_class->cursor_update = pika_perspective_clone_tool_cursor_update; tool_class->oper_update = pika_perspective_clone_tool_oper_update; tool_class->options_notify = pika_perspective_clone_tool_options_notify; draw_tool_class->draw = pika_perspective_clone_tool_draw; } static void pika_perspective_clone_tool_init (PikaPerspectiveCloneTool *clone_tool) { PikaTool *tool = PIKA_TOOL (clone_tool); pika_tool_control_set_action_object_2 (tool->control, "context-pattern-select-set"); pika_matrix3_identity (&clone_tool->transform); pika_paint_tool_enable_multi_paint (PIKA_PAINT_TOOL (clone_tool)); } static void pika_perspective_clone_tool_constructed (GObject *object) { PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (object); G_OBJECT_CLASS (parent_class)->constructed (object); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) pika_paint_tool_set_active (PIKA_PAINT_TOOL (object), FALSE); } static gboolean pika_perspective_clone_tool_initialize (PikaTool *tool, PikaDisplay *display, GError **error) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaImage *image = pika_display_get_image (display); GList *drawables; if (! PIKA_TOOL_CLASS (parent_class)->initialize (tool, display, error)) { return FALSE; } drawables = pika_image_get_selected_drawables (image); if (g_list_length (drawables) == 0) { pika_tool_message_literal (tool, display, _("No selected drawables.")); g_list_free (drawables); return FALSE; } if (display != tool->display) { PikaDisplayShell *shell = pika_display_get_shell (display); gint i; tool->display = display; tool->drawables = drawables; /* Find the transform bounds initializing */ pika_perspective_clone_tool_bounds (clone_tool, display); pika_perspective_clone_tool_prepare (clone_tool); /* Recalculate the transform tool */ pika_perspective_clone_tool_recalc_matrix (clone_tool, NULL); clone_tool->widget = pika_tool_transform_grid_new (shell, &clone_tool->transform, clone_tool->x1, clone_tool->y1, clone_tool->x2, clone_tool->y2); g_object_set (clone_tool->widget, "pivot-x", (clone_tool->x1 + clone_tool->x2) / 2.0, "pivot-y", (clone_tool->y1 + clone_tool->y2) / 2.0, "inside-function", PIKA_TRANSFORM_FUNCTION_MOVE, "outside-function", PIKA_TRANSFORM_FUNCTION_ROTATE, "use-corner-handles", TRUE, "use-perspective-handles", TRUE, "use-side-handles", TRUE, "use-shear-handles", TRUE, "use-pivot-handle", TRUE, NULL); g_signal_connect (clone_tool->widget, "changed", G_CALLBACK (pika_perspective_clone_tool_widget_changed), clone_tool); g_signal_connect (clone_tool->widget, "status", G_CALLBACK (pika_perspective_clone_tool_widget_status), clone_tool); /* start drawing the bounding box and handles... */ if (pika_draw_tool_is_active (PIKA_DRAW_TOOL (tool))) pika_draw_tool_stop (PIKA_DRAW_TOOL (tool)); pika_draw_tool_start (PIKA_DRAW_TOOL (tool), display); /* Save the current transformation info */ for (i = 0; i < TRANS_INFO_SIZE; i++) clone_tool->old_trans_info[i] = clone_tool->trans_info[i]; } else { g_list_free (drawables); } return TRUE; } static gboolean pika_perspective_clone_tool_has_display (PikaTool *tool, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); return (display == clone_tool->src_display || PIKA_TOOL_CLASS (parent_class)->has_display (tool, display)); } static PikaDisplay * pika_perspective_clone_tool_has_image (PikaTool *tool, PikaImage *image) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaDisplay *display; display = PIKA_TOOL_CLASS (parent_class)->has_image (tool, image); if (! display && clone_tool->src_display) { if (image && pika_display_get_image (clone_tool->src_display) == image) display = clone_tool->src_display; /* NULL image means any display */ if (! image) display = clone_tool->src_display; } return display; } static void pika_perspective_clone_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); switch (action) { case PIKA_TOOL_ACTION_PAUSE: case PIKA_TOOL_ACTION_RESUME: break; case PIKA_TOOL_ACTION_HALT: pika_perspective_clone_tool_halt (clone_tool); break; case PIKA_TOOL_ACTION_COMMIT: break; } PIKA_TOOL_CLASS (parent_class)->control (tool, action, display); } static void pika_perspective_clone_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display) { PikaPaintTool *paint_tool = PIKA_PAINT_TOOL (tool); PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPerspectiveClone *clone = PIKA_PERSPECTIVE_CLONE (paint_tool->core); PikaSourceCore *source_core = PIKA_SOURCE_CORE (clone); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) { if (clone_tool->widget) { pika_tool_widget_hover (clone_tool->widget, coords, state, TRUE); if (pika_tool_widget_button_press (clone_tool->widget, coords, time, state, press_type)) { clone_tool->grab_widget = clone_tool->widget; } } pika_tool_control_activate (tool->control); } else { GdkModifierType extend_mask = pika_get_extend_selection_mask (); GdkModifierType toggle_mask = pika_get_toggle_behavior_mask (); gdouble nnx, nny; pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); if ((state & (toggle_mask | extend_mask)) == toggle_mask) { source_core->set_source = TRUE; clone_tool->src_display = display; } else { source_core->set_source = FALSE; } PIKA_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state, press_type, display); /* Set the coordinates for the reference cross */ pika_perspective_clone_get_source_point (clone, coords->x, coords->y, &nnx, &nny); clone_tool->src_x = floor (nnx); clone_tool->src_y = floor (nny); pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); } } static void pika_perspective_clone_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); switch (options->clone_mode) { case PIKA_PERSPECTIVE_CLONE_MODE_ADJUST: pika_tool_control_halt (tool->control); if (clone_tool->grab_widget) { pika_tool_widget_button_release (clone_tool->grab_widget, coords, time, state, release_type); clone_tool->grab_widget = NULL; } break; case PIKA_PERSPECTIVE_CLONE_MODE_PAINT: PIKA_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state, release_type, display); break; } } static void pika_perspective_clone_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPaintTool *paint_tool = PIKA_PAINT_TOOL (tool); PikaPerspectiveClone *clone = PIKA_PERSPECTIVE_CLONE (paint_tool->core); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) { if (clone_tool->grab_widget) { pika_tool_widget_motion (clone_tool->grab_widget, coords, time, state); } } else if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_PAINT) { gdouble nnx, nny; pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); PIKA_TOOL_CLASS (parent_class)->motion (tool, coords, time, state, display); /* Set the coordinates for the reference cross */ pika_perspective_clone_get_source_point (clone, coords->x, coords->y, &nnx, &nny); clone_tool->src_x = floor (nnx); clone_tool->src_y = floor (nny); pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); } } static void pika_perspective_clone_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_PAINT && key == pika_get_toggle_behavior_mask ()) { if (press) { clone_tool->saved_precision = pika_tool_control_get_precision (tool->control); pika_tool_control_set_precision (tool->control, PIKA_CURSOR_PRECISION_PIXEL_CENTER); } else { pika_tool_control_set_precision (tool->control, clone_tool->saved_precision); } } PIKA_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state, display); } static void pika_perspective_clone_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPerspectiveCloneOptions *options; PikaImage *image; PikaToolClass *tool_class; PikaCursorType cursor = PIKA_CURSOR_MOUSE; PikaToolCursorType tool_cursor = PIKA_TOOL_CURSOR_NONE; PikaCursorModifier modifier = PIKA_CURSOR_MODIFIER_NONE; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); image = pika_display_get_image (display); if (pika_image_coords_in_active_pickable (image, coords, FALSE, FALSE, TRUE)) { cursor = PIKA_CURSOR_MOUSE; } if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) { if (clone_tool->widget) { if (display == tool->display) { pika_tool_widget_get_cursor (clone_tool->widget, coords, state, &cursor, &tool_cursor, &modifier); } } } else { GdkModifierType extend_mask = pika_get_extend_selection_mask (); GdkModifierType toggle_mask = pika_get_toggle_behavior_mask (); if ((state & (toggle_mask | extend_mask)) == toggle_mask) { cursor = PIKA_CURSOR_CROSSHAIR_SMALL; } else if (! PIKA_SOURCE_OPTIONS (options)->src_drawables) { modifier = PIKA_CURSOR_MODIFIER_BAD; } tool_cursor = PIKA_TOOL_CURSOR_CLONE; } pika_tool_control_set_cursor (tool->control, cursor); pika_tool_control_set_tool_cursor (tool->control, tool_cursor); pika_tool_control_set_cursor_modifier (tool->control, modifier); /* If we are in adjust mode, skip the PikaBrushClass when chaining up. * This ensures that the cursor will be set regardless of * PikaBrushTool::show_cursor (see bug #354933). */ if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) tool_class = PIKA_TOOL_CLASS (g_type_class_peek_parent (parent_class)); else tool_class = PIKA_TOOL_CLASS (parent_class); tool_class->cursor_update (tool, coords, state, display); } static void pika_perspective_clone_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) { if (clone_tool->widget) { if (display == tool->display) { pika_tool_widget_hover (clone_tool->widget, coords, state, proximity); } } } else { PIKA_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity, display); if (proximity) { PikaPaintCore *core = PIKA_PAINT_TOOL (tool)->core; PikaPerspectiveClone *clone = PIKA_PERSPECTIVE_CLONE (core); PikaSourceCore *source_core = PIKA_SOURCE_CORE (core); if (PIKA_SOURCE_OPTIONS (options)->src_drawables == NULL) { pika_tool_replace_status (tool, display, _("Ctrl-Click to set a clone source")); } else { pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); g_object_get (options, "src-x", &clone_tool->src_x, "src-y", &clone_tool->src_y, NULL); if (! source_core->first_stroke) { if (PIKA_SOURCE_OPTIONS (options)->align_mode == PIKA_SOURCE_ALIGN_YES) { gdouble nnx, nny; /* Set the coordinates for the reference cross */ pika_perspective_clone_get_source_point (clone, coords->x, coords->y, &nnx, &nny); clone_tool->src_x = floor (nnx); clone_tool->src_y = floor (nny); } } pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); } } } } static void pika_perspective_clone_tool_options_notify (PikaTool *tool, PikaToolOptions *options, const GParamSpec *pspec) { PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (tool); PikaPaintTool *paint_tool = PIKA_PAINT_TOOL (tool); PikaPerspectiveCloneOptions *clone_options; clone_options = PIKA_PERSPECTIVE_CLONE_OPTIONS (options); PIKA_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec); if (! strcmp (pspec->name, "clone-mode")) { PikaPerspectiveClone *clone; clone = PIKA_PERSPECTIVE_CLONE (PIKA_PAINT_TOOL (tool)->core); pika_draw_tool_pause (PIKA_DRAW_TOOL (clone_tool)); if (clone_options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_PAINT) { pika_perspective_clone_set_transform (clone, &clone_tool->transform); pika_paint_tool_set_active (paint_tool, TRUE); } else { pika_paint_tool_set_active (paint_tool, FALSE); pika_tool_control_set_precision (tool->control, PIKA_CURSOR_PRECISION_SUBPIXEL); /* start drawing the bounding box and handles... */ if (tool->display && ! pika_draw_tool_is_active (PIKA_DRAW_TOOL (clone_tool))) { pika_draw_tool_start (PIKA_DRAW_TOOL (clone_tool), tool->display); } } pika_draw_tool_resume (PIKA_DRAW_TOOL (clone_tool)); } } static void pika_perspective_clone_tool_draw (PikaDrawTool *draw_tool) { PikaTool *tool = PIKA_TOOL (draw_tool); PikaPerspectiveCloneTool *clone_tool = PIKA_PERSPECTIVE_CLONE_TOOL (draw_tool); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); if (options->clone_mode == PIKA_PERSPECTIVE_CLONE_MODE_ADJUST) { if (clone_tool->widget) { PikaCanvasItem *item = pika_tool_widget_get_item (clone_tool->widget); pika_draw_tool_add_item (draw_tool, item); } } else { PikaCanvasGroup *stroke_group; stroke_group = pika_draw_tool_add_stroke_group (draw_tool); /* draw the bounding box */ pika_draw_tool_push_group (draw_tool, stroke_group); pika_draw_tool_add_line (draw_tool, clone_tool->trans_info[X0], clone_tool->trans_info[Y0], clone_tool->trans_info[X1], clone_tool->trans_info[Y1]); pika_draw_tool_add_line (draw_tool, clone_tool->trans_info[X1], clone_tool->trans_info[Y1], clone_tool->trans_info[X3], clone_tool->trans_info[Y3]); pika_draw_tool_add_line (draw_tool, clone_tool->trans_info[X2], clone_tool->trans_info[Y2], clone_tool->trans_info[X3], clone_tool->trans_info[Y3]); pika_draw_tool_add_line (draw_tool, clone_tool->trans_info[X2], clone_tool->trans_info[Y2], clone_tool->trans_info[X0], clone_tool->trans_info[Y0]); pika_draw_tool_pop_group (draw_tool); } if (PIKA_SOURCE_OPTIONS (options)->src_drawables && clone_tool->src_display) { PikaDisplay *tmp_display; tmp_display = draw_tool->display; draw_tool->display = clone_tool->src_display; pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_CROSS, clone_tool->src_x + 0.5, clone_tool->src_y + 0.5, PIKA_TOOL_HANDLE_SIZE_CROSS, PIKA_TOOL_HANDLE_SIZE_CROSS, PIKA_HANDLE_ANCHOR_CENTER); draw_tool->display = tmp_display; } PIKA_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool); } static void pika_perspective_clone_tool_halt (PikaPerspectiveCloneTool *clone_tool) { PikaTool *tool = PIKA_TOOL (clone_tool); PikaPerspectiveCloneOptions *options; options = PIKA_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool); clone_tool->src_display = NULL; g_object_set (options, "src-drawables", NULL, NULL); if (pika_draw_tool_is_active (PIKA_DRAW_TOOL (tool))) pika_draw_tool_stop (PIKA_DRAW_TOOL (tool)); g_clear_object (&clone_tool->widget); tool->display = NULL; g_list_free (tool->drawables); tool->drawables = NULL; } static void pika_perspective_clone_tool_bounds (PikaPerspectiveCloneTool *clone_tool, PikaDisplay *display) { PikaImage *image = pika_display_get_image (display); clone_tool->x1 = 0; clone_tool->y1 = 0; clone_tool->x2 = pika_image_get_width (image); clone_tool->y2 = pika_image_get_height (image); } static void pika_perspective_clone_tool_prepare (PikaPerspectiveCloneTool *clone_tool) { clone_tool->trans_info[PIVOT_X] = (gdouble) (clone_tool->x1 + clone_tool->x2) / 2.0; clone_tool->trans_info[PIVOT_Y] = (gdouble) (clone_tool->y1 + clone_tool->y2) / 2.0; clone_tool->trans_info[X0] = clone_tool->x1; clone_tool->trans_info[Y0] = clone_tool->y1; clone_tool->trans_info[X1] = clone_tool->x2; clone_tool->trans_info[Y1] = clone_tool->y1; clone_tool->trans_info[X2] = clone_tool->x1; clone_tool->trans_info[Y2] = clone_tool->y2; clone_tool->trans_info[X3] = clone_tool->x2; clone_tool->trans_info[Y3] = clone_tool->y2; } static void pika_perspective_clone_tool_recalc_matrix (PikaPerspectiveCloneTool *clone_tool, PikaToolWidget *widget) { pika_matrix3_identity (&clone_tool->transform); pika_transform_matrix_perspective (&clone_tool->transform, clone_tool->x1, clone_tool->y1, clone_tool->x2 - clone_tool->x1, clone_tool->y2 - clone_tool->y1, clone_tool->trans_info[X0], clone_tool->trans_info[Y0], clone_tool->trans_info[X1], clone_tool->trans_info[Y1], clone_tool->trans_info[X2], clone_tool->trans_info[Y2], clone_tool->trans_info[X3], clone_tool->trans_info[Y3]); if (widget) g_object_set (widget, "transform", &clone_tool->transform, "x1", (gdouble) clone_tool->x1, "y1", (gdouble) clone_tool->y1, "x2", (gdouble) clone_tool->x2, "y2", (gdouble) clone_tool->y2, "pivot-x", clone_tool->trans_info[PIVOT_X], "pivot-y", clone_tool->trans_info[PIVOT_Y], NULL); } static void pika_perspective_clone_tool_widget_changed (PikaToolWidget *widget, PikaPerspectiveCloneTool *clone_tool) { PikaMatrix3 *transform; g_object_get (widget, "transform", &transform, "pivot-x", &clone_tool->trans_info[PIVOT_X], "pivot-y", &clone_tool->trans_info[PIVOT_Y], NULL); pika_matrix3_transform_point (transform, clone_tool->x1, clone_tool->y1, &clone_tool->trans_info[X0], &clone_tool->trans_info[Y0]); pika_matrix3_transform_point (transform, clone_tool->x2, clone_tool->y1, &clone_tool->trans_info[X1], &clone_tool->trans_info[Y1]); pika_matrix3_transform_point (transform, clone_tool->x1, clone_tool->y2, &clone_tool->trans_info[X2], &clone_tool->trans_info[Y2]); pika_matrix3_transform_point (transform, clone_tool->x2, clone_tool->y2, &clone_tool->trans_info[X3], &clone_tool->trans_info[Y3]); g_free (transform); pika_perspective_clone_tool_recalc_matrix (clone_tool, NULL); } static void pika_perspective_clone_tool_widget_status (PikaToolWidget *widget, const gchar *status, PikaPerspectiveCloneTool *clone_tool) { PikaTool *tool = PIKA_TOOL (clone_tool); if (status) { pika_tool_replace_status (tool, tool->display, "%s", status); } else { pika_tool_pop_status (tool, tool->display); } } /* tool options stuff */ static GtkWidget * pika_perspective_clone_options_gui (PikaToolOptions *tool_options) { GObject *config = G_OBJECT (tool_options); GtkWidget *vbox = pika_clone_options_gui (tool_options); GtkWidget *mode; /* radio buttons to set if you are modifying the perspective plane * or painting */ mode = pika_prop_enum_radio_box_new (config, "clone-mode", 0, 0); gtk_box_pack_start (GTK_BOX (vbox), mode, FALSE, FALSE, 0); gtk_box_reorder_child (GTK_BOX (vbox), mode, 0); return vbox; }