1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25
26 #include "tools-types.h"
27
28 #include "core/gimp-transform-utils.h"
29 #include "core/gimpimage.h"
30
31 #include "paint/gimpperspectiveclone.h"
32 #include "paint/gimpperspectivecloneoptions.h"
33
34 #include "widgets/gimphelp-ids.h"
35 #include "widgets/gimpviewablebox.h"
36 #include "widgets/gimpwidgets-utils.h"
37
38 #include "display/gimpcanvasgroup.h"
39 #include "display/gimpdisplay.h"
40 #include "display/gimptooltransformgrid.h"
41
42 #include "gimpperspectiveclonetool.h"
43 #include "gimpcloneoptions-gui.h"
44 #include "gimptoolcontrol.h"
45
46 #include "gimp-intl.h"
47
48
49 /* index into trans_info array */
50 enum
51 {
52 X0,
53 Y0,
54 X1,
55 Y1,
56 X2,
57 Y2,
58 X3,
59 Y3,
60 PIVOT_X,
61 PIVOT_Y
62 };
63
64
65 static void gimp_perspective_clone_tool_constructed (GObject *object);
66
67 static gboolean gimp_perspective_clone_tool_initialize (GimpTool *tool,
68 GimpDisplay *display,
69 GError **error);
70
71 static gboolean gimp_perspective_clone_tool_has_display (GimpTool *tool,
72 GimpDisplay *display);
73 static GimpDisplay *
74 gimp_perspective_clone_tool_has_image (GimpTool *tool,
75 GimpImage *image);
76 static void gimp_perspective_clone_tool_control (GimpTool *tool,
77 GimpToolAction action,
78 GimpDisplay *display);
79 static void gimp_perspective_clone_tool_button_press (GimpTool *tool,
80 const GimpCoords *coords,
81 guint32 time,
82 GdkModifierType state,
83 GimpButtonPressType press_type,
84 GimpDisplay *display);
85 static void gimp_perspective_clone_tool_button_release (GimpTool *tool,
86 const GimpCoords *coords,
87 guint32 time,
88 GdkModifierType state,
89 GimpButtonReleaseType release_type,
90 GimpDisplay *display);
91 static void gimp_perspective_clone_tool_motion (GimpTool *tool,
92 const GimpCoords *coords,
93 guint32 time,
94 GdkModifierType state,
95 GimpDisplay *display);
96 static void gimp_perspective_clone_tool_modifier_key (GimpTool *tool,
97 GdkModifierType key,
98 gboolean press,
99 GdkModifierType state,
100 GimpDisplay *display);
101 static void gimp_perspective_clone_tool_cursor_update (GimpTool *tool,
102 const GimpCoords *coords,
103 GdkModifierType state,
104 GimpDisplay *display);
105 static void gimp_perspective_clone_tool_oper_update (GimpTool *tool,
106 const GimpCoords *coords,
107 GdkModifierType state,
108 gboolean proximity,
109 GimpDisplay *display);
110 static void gimp_perspective_clone_tool_options_notify (GimpTool *tool,
111 GimpToolOptions *options,
112 const GParamSpec *pspec);
113
114 static void gimp_perspective_clone_tool_draw (GimpDrawTool *draw_tool);
115
116 static void gimp_perspective_clone_tool_halt (GimpPerspectiveCloneTool *clone_tool);
117 static void gimp_perspective_clone_tool_bounds (GimpPerspectiveCloneTool *clone_tool,
118 GimpDisplay *display);
119 static void gimp_perspective_clone_tool_prepare (GimpPerspectiveCloneTool *clone_tool);
120 static void gimp_perspective_clone_tool_recalc_matrix (GimpPerspectiveCloneTool *clone_tool,
121 GimpToolWidget *widget);
122
123 static void gimp_perspective_clone_tool_widget_changed (GimpToolWidget *widget,
124 GimpPerspectiveCloneTool *clone_tool);
125 static void gimp_perspective_clone_tool_widget_status (GimpToolWidget *widget,
126 const gchar *status,
127 GimpPerspectiveCloneTool *clone_tool);
128
129 static GtkWidget *
130 gimp_perspective_clone_options_gui (GimpToolOptions *tool_options);
131
132
G_DEFINE_TYPE(GimpPerspectiveCloneTool,gimp_perspective_clone_tool,GIMP_TYPE_BRUSH_TOOL)133 G_DEFINE_TYPE (GimpPerspectiveCloneTool, gimp_perspective_clone_tool,
134 GIMP_TYPE_BRUSH_TOOL)
135
136 #define parent_class gimp_perspective_clone_tool_parent_class
137
138
139 void
140 gimp_perspective_clone_tool_register (GimpToolRegisterCallback callback,
141 gpointer data)
142 {
143 (* callback) (GIMP_TYPE_PERSPECTIVE_CLONE_TOOL,
144 GIMP_TYPE_PERSPECTIVE_CLONE_OPTIONS,
145 gimp_perspective_clone_options_gui,
146 GIMP_PAINT_OPTIONS_CONTEXT_MASK |
147 GIMP_CONTEXT_PROP_MASK_PATTERN,
148 "gimp-perspective-clone-tool",
149 _("Perspective Clone"),
150 _("Perspective Clone Tool: Clone from an image source "
151 "after applying a perspective transformation"),
152 N_("_Perspective Clone"), NULL,
153 NULL, GIMP_HELP_TOOL_PERSPECTIVE_CLONE,
154 GIMP_ICON_TOOL_PERSPECTIVE_CLONE,
155 data);
156 }
157
158 static void
gimp_perspective_clone_tool_class_init(GimpPerspectiveCloneToolClass * klass)159 gimp_perspective_clone_tool_class_init (GimpPerspectiveCloneToolClass *klass)
160 {
161 GObjectClass *object_class = G_OBJECT_CLASS (klass);
162 GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
163 GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
164
165 object_class->constructed = gimp_perspective_clone_tool_constructed;
166
167 tool_class->initialize = gimp_perspective_clone_tool_initialize;
168 tool_class->has_display = gimp_perspective_clone_tool_has_display;
169 tool_class->has_image = gimp_perspective_clone_tool_has_image;
170 tool_class->control = gimp_perspective_clone_tool_control;
171 tool_class->button_press = gimp_perspective_clone_tool_button_press;
172 tool_class->button_release = gimp_perspective_clone_tool_button_release;
173 tool_class->motion = gimp_perspective_clone_tool_motion;
174 tool_class->modifier_key = gimp_perspective_clone_tool_modifier_key;
175 tool_class->cursor_update = gimp_perspective_clone_tool_cursor_update;
176 tool_class->oper_update = gimp_perspective_clone_tool_oper_update;
177 tool_class->options_notify = gimp_perspective_clone_tool_options_notify;
178
179 draw_tool_class->draw = gimp_perspective_clone_tool_draw;
180 }
181
182 static void
gimp_perspective_clone_tool_init(GimpPerspectiveCloneTool * clone_tool)183 gimp_perspective_clone_tool_init (GimpPerspectiveCloneTool *clone_tool)
184 {
185 GimpTool *tool = GIMP_TOOL (clone_tool);
186
187 gimp_tool_control_set_action_object_2 (tool->control,
188 "context/context-pattern-select-set");
189
190 gimp_matrix3_identity (&clone_tool->transform);
191 }
192
193 static void
gimp_perspective_clone_tool_constructed(GObject * object)194 gimp_perspective_clone_tool_constructed (GObject *object)
195 {
196 GimpPerspectiveCloneOptions *options;
197
198 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (object);
199
200 G_OBJECT_CLASS (parent_class)->constructed (object);
201
202 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
203 gimp_paint_tool_set_active (GIMP_PAINT_TOOL (object), FALSE);
204 }
205
206 static gboolean
gimp_perspective_clone_tool_initialize(GimpTool * tool,GimpDisplay * display,GError ** error)207 gimp_perspective_clone_tool_initialize (GimpTool *tool,
208 GimpDisplay *display,
209 GError **error)
210 {
211 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
212
213 if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error))
214 {
215 return FALSE;
216 }
217
218 if (display != tool->display)
219 {
220 GimpDisplayShell *shell = gimp_display_get_shell (display);
221 GimpImage *image = gimp_display_get_image (display);
222 gint i;
223
224 tool->display = display;
225 tool->drawable = gimp_image_get_active_drawable (image);
226
227 /* Find the transform bounds initializing */
228 gimp_perspective_clone_tool_bounds (clone_tool, display);
229
230 gimp_perspective_clone_tool_prepare (clone_tool);
231
232 /* Recalculate the transform tool */
233 gimp_perspective_clone_tool_recalc_matrix (clone_tool, NULL);
234
235 clone_tool->widget =
236 gimp_tool_transform_grid_new (shell,
237 &clone_tool->transform,
238 clone_tool->x1,
239 clone_tool->y1,
240 clone_tool->x2,
241 clone_tool->y2);
242
243 g_object_set (clone_tool->widget,
244 "pivot-x", (clone_tool->x1 + clone_tool->x2) / 2.0,
245 "pivot-y", (clone_tool->y1 + clone_tool->y2) / 2.0,
246 "inside-function", GIMP_TRANSFORM_FUNCTION_MOVE,
247 "outside-function", GIMP_TRANSFORM_FUNCTION_ROTATE,
248 "use-corner-handles", TRUE,
249 "use-perspective-handles", TRUE,
250 "use-side-handles", TRUE,
251 "use-shear-handles", TRUE,
252 "use-pivot-handle", TRUE,
253 NULL);
254
255 g_signal_connect (clone_tool->widget, "changed",
256 G_CALLBACK (gimp_perspective_clone_tool_widget_changed),
257 clone_tool);
258 g_signal_connect (clone_tool->widget, "status",
259 G_CALLBACK (gimp_perspective_clone_tool_widget_status),
260 clone_tool);
261
262 /* start drawing the bounding box and handles... */
263 if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
264 gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
265
266 gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
267
268 /* Save the current transformation info */
269 for (i = 0; i < TRANS_INFO_SIZE; i++)
270 clone_tool->old_trans_info[i] = clone_tool->trans_info[i];
271 }
272
273 return TRUE;
274 }
275
276 static gboolean
gimp_perspective_clone_tool_has_display(GimpTool * tool,GimpDisplay * display)277 gimp_perspective_clone_tool_has_display (GimpTool *tool,
278 GimpDisplay *display)
279 {
280 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
281
282 return (display == clone_tool->src_display ||
283 GIMP_TOOL_CLASS (parent_class)->has_display (tool, display));
284 }
285
286 static GimpDisplay *
gimp_perspective_clone_tool_has_image(GimpTool * tool,GimpImage * image)287 gimp_perspective_clone_tool_has_image (GimpTool *tool,
288 GimpImage *image)
289 {
290 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
291 GimpDisplay *display;
292
293 display = GIMP_TOOL_CLASS (parent_class)->has_image (tool, image);
294
295 if (! display && clone_tool->src_display)
296 {
297 if (image && gimp_display_get_image (clone_tool->src_display) == image)
298 display = clone_tool->src_display;
299
300 /* NULL image means any display */
301 if (! image)
302 display = clone_tool->src_display;
303 }
304
305 return display;
306 }
307
308 static void
gimp_perspective_clone_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)309 gimp_perspective_clone_tool_control (GimpTool *tool,
310 GimpToolAction action,
311 GimpDisplay *display)
312 {
313 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
314
315 switch (action)
316 {
317 case GIMP_TOOL_ACTION_PAUSE:
318 case GIMP_TOOL_ACTION_RESUME:
319 break;
320
321 case GIMP_TOOL_ACTION_HALT:
322 gimp_perspective_clone_tool_halt (clone_tool);
323 break;
324
325 case GIMP_TOOL_ACTION_COMMIT:
326 break;
327 }
328
329 GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
330 }
331
332 static void
gimp_perspective_clone_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)333 gimp_perspective_clone_tool_button_press (GimpTool *tool,
334 const GimpCoords *coords,
335 guint32 time,
336 GdkModifierType state,
337 GimpButtonPressType press_type,
338 GimpDisplay *display)
339 {
340 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
341 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
342 GimpPerspectiveClone *clone = GIMP_PERSPECTIVE_CLONE (paint_tool->core);
343 GimpSourceCore *source_core = GIMP_SOURCE_CORE (clone);
344 GimpPerspectiveCloneOptions *options;
345
346 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
347
348 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
349 {
350 if (clone_tool->widget)
351 {
352 gimp_tool_widget_hover (clone_tool->widget, coords, state, TRUE);
353
354 if (gimp_tool_widget_button_press (clone_tool->widget, coords,
355 time, state, press_type))
356 {
357 clone_tool->grab_widget = clone_tool->widget;
358 }
359 }
360
361 gimp_tool_control_activate (tool->control);
362 }
363 else
364 {
365 GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
366 GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();
367 gdouble nnx, nny;
368
369 gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
370
371 if ((state & (toggle_mask | extend_mask)) == toggle_mask)
372 {
373 source_core->set_source = TRUE;
374
375 clone_tool->src_display = display;
376 }
377 else
378 {
379 source_core->set_source = FALSE;
380 }
381
382 GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
383 press_type, display);
384
385 /* Set the coordinates for the reference cross */
386 gimp_perspective_clone_get_source_point (clone,
387 coords->x, coords->y,
388 &nnx, &nny);
389
390 clone_tool->src_x = floor (nnx);
391 clone_tool->src_y = floor (nny);
392
393 gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
394 }
395 }
396
397 static void
gimp_perspective_clone_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)398 gimp_perspective_clone_tool_button_release (GimpTool *tool,
399 const GimpCoords *coords,
400 guint32 time,
401 GdkModifierType state,
402 GimpButtonReleaseType release_type,
403 GimpDisplay *display)
404 {
405 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
406 GimpPerspectiveCloneOptions *options;
407
408 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
409
410 switch (options->clone_mode)
411 {
412 case GIMP_PERSPECTIVE_CLONE_MODE_ADJUST:
413 gimp_tool_control_halt (tool->control);
414
415 if (clone_tool->grab_widget)
416 {
417 gimp_tool_widget_button_release (clone_tool->grab_widget,
418 coords, time, state, release_type);
419 clone_tool->grab_widget = NULL;
420 }
421 break;
422
423 case GIMP_PERSPECTIVE_CLONE_MODE_PAINT:
424 GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
425 release_type, display);
426 break;
427 }
428 }
429
430 static void
gimp_perspective_clone_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)431 gimp_perspective_clone_tool_motion (GimpTool *tool,
432 const GimpCoords *coords,
433 guint32 time,
434 GdkModifierType state,
435 GimpDisplay *display)
436 {
437 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
438 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
439 GimpPerspectiveClone *clone = GIMP_PERSPECTIVE_CLONE (paint_tool->core);
440 GimpPerspectiveCloneOptions *options;
441
442 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
443
444 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
445 {
446 if (clone_tool->grab_widget)
447 {
448 gimp_tool_widget_motion (clone_tool->grab_widget, coords, time, state);
449 }
450 }
451 else if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT)
452 {
453 gdouble nnx, nny;
454
455 gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
456
457 GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state,
458 display);
459
460 /* Set the coordinates for the reference cross */
461 gimp_perspective_clone_get_source_point (clone,
462 coords->x, coords->y,
463 &nnx, &nny);
464
465 clone_tool->src_x = floor (nnx);
466 clone_tool->src_y = floor (nny);
467
468 gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
469 }
470 }
471
472 static void
gimp_perspective_clone_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)473 gimp_perspective_clone_tool_modifier_key (GimpTool *tool,
474 GdkModifierType key,
475 gboolean press,
476 GdkModifierType state,
477 GimpDisplay *display)
478 {
479 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
480 GimpPerspectiveCloneOptions *options;
481
482 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
483
484 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT &&
485 key == gimp_get_toggle_behavior_mask ())
486 {
487 if (press)
488 {
489 clone_tool->saved_precision =
490 gimp_tool_control_get_precision (tool->control);
491 gimp_tool_control_set_precision (tool->control,
492 GIMP_CURSOR_PRECISION_PIXEL_CENTER);
493 }
494 else
495 {
496 gimp_tool_control_set_precision (tool->control,
497 clone_tool->saved_precision);
498 }
499 }
500
501 GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
502 display);
503 }
504
505 static void
gimp_perspective_clone_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)506 gimp_perspective_clone_tool_cursor_update (GimpTool *tool,
507 const GimpCoords *coords,
508 GdkModifierType state,
509 GimpDisplay *display)
510 {
511 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
512 GimpPerspectiveCloneOptions *options;
513 GimpImage *image;
514 GimpToolClass *tool_class;
515 GimpCursorType cursor = GIMP_CURSOR_MOUSE;
516 GimpToolCursorType tool_cursor = GIMP_TOOL_CURSOR_NONE;
517 GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
518
519 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
520
521 image = gimp_display_get_image (display);
522
523 if (gimp_image_coords_in_active_pickable (image, coords,
524 FALSE, FALSE, TRUE))
525 {
526 cursor = GIMP_CURSOR_MOUSE;
527 }
528
529 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
530 {
531 if (clone_tool->widget)
532 {
533 if (display == tool->display)
534 {
535 gimp_tool_widget_get_cursor (clone_tool->widget,
536 coords, state,
537 &cursor, &tool_cursor, &modifier);
538 }
539 }
540 }
541 else
542 {
543 GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
544 GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();
545
546 if ((state & (toggle_mask | extend_mask)) == toggle_mask)
547 {
548 cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
549 }
550 else if (! GIMP_SOURCE_CORE (GIMP_PAINT_TOOL (tool)->core)->src_drawable)
551 {
552 modifier = GIMP_CURSOR_MODIFIER_BAD;
553 }
554
555 tool_cursor = GIMP_TOOL_CURSOR_CLONE;
556 }
557
558 gimp_tool_control_set_cursor (tool->control, cursor);
559 gimp_tool_control_set_tool_cursor (tool->control, tool_cursor);
560 gimp_tool_control_set_cursor_modifier (tool->control, modifier);
561
562 /* If we are in adjust mode, skip the GimpBrushClass when chaining up.
563 * This ensures that the cursor will be set regardless of
564 * GimpBrushTool::show_cursor (see bug #354933).
565 */
566 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
567 tool_class = GIMP_TOOL_CLASS (g_type_class_peek_parent (parent_class));
568 else
569 tool_class = GIMP_TOOL_CLASS (parent_class);
570
571 tool_class->cursor_update (tool, coords, state, display);
572 }
573
574 static void
gimp_perspective_clone_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)575 gimp_perspective_clone_tool_oper_update (GimpTool *tool,
576 const GimpCoords *coords,
577 GdkModifierType state,
578 gboolean proximity,
579 GimpDisplay *display)
580 {
581 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
582 GimpPerspectiveCloneOptions *options;
583
584 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
585
586 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
587 {
588 if (clone_tool->widget)
589 {
590 if (display == tool->display)
591 {
592 gimp_tool_widget_hover (clone_tool->widget, coords, state,
593 proximity);
594 }
595 }
596 }
597 else
598 {
599 GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
600 proximity, display);
601
602 if (proximity)
603 {
604 GimpPaintCore *core = GIMP_PAINT_TOOL (tool)->core;
605 GimpPerspectiveClone *clone = GIMP_PERSPECTIVE_CLONE (core);
606 GimpSourceCore *source_core = GIMP_SOURCE_CORE (core);
607
608 if (source_core->src_drawable == NULL)
609 {
610 gimp_tool_replace_status (tool, display,
611 _("Ctrl-Click to set a clone source"));
612 }
613 else
614 {
615 gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
616
617 clone_tool->src_x = source_core->src_x;
618 clone_tool->src_y = source_core->src_y;
619
620 if (! source_core->first_stroke)
621 {
622 if (GIMP_SOURCE_OPTIONS (options)->align_mode ==
623 GIMP_SOURCE_ALIGN_YES)
624 {
625 gdouble nnx, nny;
626
627 /* Set the coordinates for the reference cross */
628 gimp_perspective_clone_get_source_point (clone,
629 coords->x,
630 coords->y,
631 &nnx, &nny);
632
633 clone_tool->src_x = floor (nnx);
634 clone_tool->src_y = floor (nny);
635 }
636 }
637
638 gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
639 }
640 }
641 }
642 }
643
644 static void
gimp_perspective_clone_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)645 gimp_perspective_clone_tool_options_notify (GimpTool *tool,
646 GimpToolOptions *options,
647 const GParamSpec *pspec)
648 {
649 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
650 GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool);
651 GimpPerspectiveCloneOptions *clone_options;
652
653 clone_options = GIMP_PERSPECTIVE_CLONE_OPTIONS (options);
654
655 GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
656
657 if (! strcmp (pspec->name, "clone-mode"))
658 {
659 GimpPerspectiveClone *clone;
660
661 clone = GIMP_PERSPECTIVE_CLONE (GIMP_PAINT_TOOL (tool)->core);
662
663 gimp_draw_tool_pause (GIMP_DRAW_TOOL (clone_tool));
664
665 if (clone_options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT)
666 {
667 gimp_perspective_clone_set_transform (clone, &clone_tool->transform);
668
669 gimp_paint_tool_set_active (paint_tool, TRUE);
670 }
671 else
672 {
673 gimp_paint_tool_set_active (paint_tool, FALSE);
674
675 gimp_tool_control_set_precision (tool->control,
676 GIMP_CURSOR_PRECISION_SUBPIXEL);
677
678 /* start drawing the bounding box and handles... */
679 if (tool->display &&
680 ! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (clone_tool)))
681 {
682 gimp_draw_tool_start (GIMP_DRAW_TOOL (clone_tool), tool->display);
683 }
684 }
685
686 gimp_draw_tool_resume (GIMP_DRAW_TOOL (clone_tool));
687 }
688 }
689
690 static void
gimp_perspective_clone_tool_draw(GimpDrawTool * draw_tool)691 gimp_perspective_clone_tool_draw (GimpDrawTool *draw_tool)
692 {
693 GimpTool *tool = GIMP_TOOL (draw_tool);
694 GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (draw_tool);
695 GimpPerspectiveClone *clone = GIMP_PERSPECTIVE_CLONE (GIMP_PAINT_TOOL (tool)->core);
696 GimpSourceCore *source_core = GIMP_SOURCE_CORE (clone);
697 GimpPerspectiveCloneOptions *options;
698
699 options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
700
701 if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
702 {
703 if (clone_tool->widget)
704 {
705 GimpCanvasItem *item = gimp_tool_widget_get_item (clone_tool->widget);
706
707 gimp_draw_tool_add_item (draw_tool, item);
708 }
709 }
710 else
711 {
712 GimpCanvasGroup *stroke_group;
713
714 stroke_group = gimp_draw_tool_add_stroke_group (draw_tool);
715
716 /* draw the bounding box */
717 gimp_draw_tool_push_group (draw_tool, stroke_group);
718
719 gimp_draw_tool_add_line (draw_tool,
720 clone_tool->trans_info[X0],
721 clone_tool->trans_info[Y0],
722 clone_tool->trans_info[X1],
723 clone_tool->trans_info[Y1]);
724 gimp_draw_tool_add_line (draw_tool,
725 clone_tool->trans_info[X1],
726 clone_tool->trans_info[Y1],
727 clone_tool->trans_info[X3],
728 clone_tool->trans_info[Y3]);
729 gimp_draw_tool_add_line (draw_tool,
730 clone_tool->trans_info[X2],
731 clone_tool->trans_info[Y2],
732 clone_tool->trans_info[X3],
733 clone_tool->trans_info[Y3]);
734 gimp_draw_tool_add_line (draw_tool,
735 clone_tool->trans_info[X2],
736 clone_tool->trans_info[Y2],
737 clone_tool->trans_info[X0],
738 clone_tool->trans_info[Y0]);
739
740 gimp_draw_tool_pop_group (draw_tool);
741 }
742
743 if (source_core->src_drawable && clone_tool->src_display)
744 {
745 GimpDisplay *tmp_display;
746
747 tmp_display = draw_tool->display;
748 draw_tool->display = clone_tool->src_display;
749
750 gimp_draw_tool_add_handle (draw_tool,
751 GIMP_HANDLE_CROSS,
752 clone_tool->src_x + 0.5,
753 clone_tool->src_y + 0.5,
754 GIMP_TOOL_HANDLE_SIZE_CROSS,
755 GIMP_TOOL_HANDLE_SIZE_CROSS,
756 GIMP_HANDLE_ANCHOR_CENTER);
757
758 draw_tool->display = tmp_display;
759 }
760
761 GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
762 }
763
764 static void
gimp_perspective_clone_tool_halt(GimpPerspectiveCloneTool * clone_tool)765 gimp_perspective_clone_tool_halt (GimpPerspectiveCloneTool *clone_tool)
766 {
767 GimpTool *tool = GIMP_TOOL (clone_tool);
768
769 clone_tool->src_display = NULL;
770
771 g_object_set (GIMP_PAINT_TOOL (tool)->core,
772 "src-drawable", NULL,
773 NULL);
774
775 if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
776 gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
777
778 g_clear_object (&clone_tool->widget);
779
780 tool->display = NULL;
781 tool->drawable = NULL;
782 }
783
784 static void
gimp_perspective_clone_tool_bounds(GimpPerspectiveCloneTool * clone_tool,GimpDisplay * display)785 gimp_perspective_clone_tool_bounds (GimpPerspectiveCloneTool *clone_tool,
786 GimpDisplay *display)
787 {
788 GimpImage *image = gimp_display_get_image (display);
789
790 clone_tool->x1 = 0;
791 clone_tool->y1 = 0;
792 clone_tool->x2 = gimp_image_get_width (image);
793 clone_tool->y2 = gimp_image_get_height (image);
794 }
795
796 static void
gimp_perspective_clone_tool_prepare(GimpPerspectiveCloneTool * clone_tool)797 gimp_perspective_clone_tool_prepare (GimpPerspectiveCloneTool *clone_tool)
798 {
799 clone_tool->trans_info[PIVOT_X] = (gdouble) (clone_tool->x1 + clone_tool->x2) / 2.0;
800 clone_tool->trans_info[PIVOT_Y] = (gdouble) (clone_tool->y1 + clone_tool->y2) / 2.0;
801
802 clone_tool->trans_info[X0] = clone_tool->x1;
803 clone_tool->trans_info[Y0] = clone_tool->y1;
804 clone_tool->trans_info[X1] = clone_tool->x2;
805 clone_tool->trans_info[Y1] = clone_tool->y1;
806 clone_tool->trans_info[X2] = clone_tool->x1;
807 clone_tool->trans_info[Y2] = clone_tool->y2;
808 clone_tool->trans_info[X3] = clone_tool->x2;
809 clone_tool->trans_info[Y3] = clone_tool->y2;
810 }
811
812 static void
gimp_perspective_clone_tool_recalc_matrix(GimpPerspectiveCloneTool * clone_tool,GimpToolWidget * widget)813 gimp_perspective_clone_tool_recalc_matrix (GimpPerspectiveCloneTool *clone_tool,
814 GimpToolWidget *widget)
815 {
816 gimp_matrix3_identity (&clone_tool->transform);
817 gimp_transform_matrix_perspective (&clone_tool->transform,
818 clone_tool->x1,
819 clone_tool->y1,
820 clone_tool->x2 - clone_tool->x1,
821 clone_tool->y2 - clone_tool->y1,
822 clone_tool->trans_info[X0],
823 clone_tool->trans_info[Y0],
824 clone_tool->trans_info[X1],
825 clone_tool->trans_info[Y1],
826 clone_tool->trans_info[X2],
827 clone_tool->trans_info[Y2],
828 clone_tool->trans_info[X3],
829 clone_tool->trans_info[Y3]);
830
831 if (widget)
832 g_object_set (widget,
833 "transform", &clone_tool->transform,
834 "x1", (gdouble) clone_tool->x1,
835 "y1", (gdouble) clone_tool->y1,
836 "x2", (gdouble) clone_tool->x2,
837 "y2", (gdouble) clone_tool->y2,
838 "pivot-x", clone_tool->trans_info[PIVOT_X],
839 "pivot-y", clone_tool->trans_info[PIVOT_Y],
840 NULL);
841 }
842
843 static void
gimp_perspective_clone_tool_widget_changed(GimpToolWidget * widget,GimpPerspectiveCloneTool * clone_tool)844 gimp_perspective_clone_tool_widget_changed (GimpToolWidget *widget,
845 GimpPerspectiveCloneTool *clone_tool)
846 {
847 GimpMatrix3 *transform;
848
849 g_object_get (widget,
850 "transform", &transform,
851 "pivot-x", &clone_tool->trans_info[PIVOT_X],
852 "pivot-y", &clone_tool->trans_info[PIVOT_Y],
853 NULL);
854
855 gimp_matrix3_transform_point (transform,
856 clone_tool->x1, clone_tool->y1,
857 &clone_tool->trans_info[X0],
858 &clone_tool->trans_info[Y0]);
859 gimp_matrix3_transform_point (transform,
860 clone_tool->x2, clone_tool->y1,
861 &clone_tool->trans_info[X1],
862 &clone_tool->trans_info[Y1]);
863 gimp_matrix3_transform_point (transform,
864 clone_tool->x1, clone_tool->y2,
865 &clone_tool->trans_info[X2],
866 &clone_tool->trans_info[Y2]);
867 gimp_matrix3_transform_point (transform,
868 clone_tool->x2, clone_tool->y2,
869 &clone_tool->trans_info[X3],
870 &clone_tool->trans_info[Y3]);
871
872 g_free (transform);
873
874 gimp_perspective_clone_tool_recalc_matrix (clone_tool, NULL);
875 }
876
877 static void
gimp_perspective_clone_tool_widget_status(GimpToolWidget * widget,const gchar * status,GimpPerspectiveCloneTool * clone_tool)878 gimp_perspective_clone_tool_widget_status (GimpToolWidget *widget,
879 const gchar *status,
880 GimpPerspectiveCloneTool *clone_tool)
881 {
882 GimpTool *tool = GIMP_TOOL (clone_tool);
883
884 if (status)
885 {
886 gimp_tool_replace_status (tool, tool->display, "%s", status);
887 }
888 else
889 {
890 gimp_tool_pop_status (tool, tool->display);
891 }
892 }
893
894
895 /* tool options stuff */
896
897 static GtkWidget *
gimp_perspective_clone_options_gui(GimpToolOptions * tool_options)898 gimp_perspective_clone_options_gui (GimpToolOptions *tool_options)
899 {
900 GObject *config = G_OBJECT (tool_options);
901 GtkWidget *vbox = gimp_clone_options_gui (tool_options);
902 GtkWidget *mode;
903
904 /* radio buttons to set if you are modifying the perspective plane
905 * or painting
906 */
907 mode = gimp_prop_enum_radio_box_new (config, "clone-mode", 0, 0);
908 gtk_box_pack_start (GTK_BOX (vbox), mode, FALSE, FALSE, 0);
909 gtk_box_reorder_child (GTK_BOX (vbox), mode, 0);
910 gtk_widget_show (mode);
911
912 return vbox;
913 }
914