1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
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 #include <gdk/gdkkeysyms.h>
23
24 #include "libgimpmath/gimpmath.h"
25 #include "libgimpwidgets/gimpwidgets.h"
26
27 #include "tools-types.h"
28
29 #include "gegl/gimpapplicator.h"
30 #include "gegl/gimp-gegl-nodes.h"
31 #include "gegl/gimp-gegl-utils.h"
32
33 #include "core/gimp.h"
34 #include "core/gimp-transform-resize.h"
35 #include "core/gimp-transform-utils.h"
36 #include "core/gimpboundary.h"
37 #include "core/gimpcontainer.h"
38 #include "core/gimpdrawablefilter.h"
39 #include "core/gimperror.h"
40 #include "core/gimpfilter.h"
41 #include "core/gimpgrouplayer.h"
42 #include "core/gimpimage.h"
43 #include "core/gimpimage-item-list.h"
44 #include "core/gimpimage-undo.h"
45 #include "core/gimpimage-undo-push.h"
46 #include "core/gimplayer.h"
47 #include "core/gimplayermask.h"
48 #include "core/gimppickable.h"
49 #include "core/gimpprojection.h"
50 #include "core/gimptoolinfo.h"
51 #include "core/gimpviewable.h"
52
53 #include "vectors/gimpvectors.h"
54 #include "vectors/gimpstroke.h"
55
56 #include "widgets/gimpwidgets-utils.h"
57
58 #include "display/gimpcanvasitem.h"
59 #include "display/gimpdisplay.h"
60 #include "display/gimpdisplayshell.h"
61 #include "display/gimptoolgui.h"
62 #include "display/gimptoolwidget.h"
63
64 #include "gimptoolcontrol.h"
65 #include "gimptransformgridoptions.h"
66 #include "gimptransformgridtool.h"
67 #include "gimptransformgridtoolundo.h"
68 #include "gimptransformoptions.h"
69
70 #include "gimp-intl.h"
71
72
73 #define EPSILON 1e-6
74
75
76 #define RESPONSE_RESET 1
77 #define RESPONSE_READJUST 2
78
79 #define UNDO_COMPRESS_TIME (0.5 * G_TIME_SPAN_SECOND)
80
81
82 typedef struct
83 {
84 GimpTransformGridTool *tg_tool;
85
86 GimpDrawable *drawable;
87 GimpDrawableFilter *filter;
88
89 GimpDrawable *root_drawable;
90
91 GeglNode *transform_node;
92 GeglNode *crop_node;
93
94 GimpMatrix3 transform;
95 GeglRectangle bounds;
96 } Filter;
97
98 typedef struct
99 {
100 GimpTransformGridTool *tg_tool;
101 GimpDrawable *root_drawable;
102 } AddFilterData;
103
104 typedef struct
105 {
106 gint64 time;
107 GimpTransformDirection direction;
108 TransInfo trans_infos[2];
109 } UndoInfo;
110
111
112 static void gimp_transform_grid_tool_finalize (GObject *object);
113
114 static gboolean gimp_transform_grid_tool_initialize (GimpTool *tool,
115 GimpDisplay *display,
116 GError **error);
117 static void gimp_transform_grid_tool_control (GimpTool *tool,
118 GimpToolAction action,
119 GimpDisplay *display);
120 static void gimp_transform_grid_tool_button_press (GimpTool *tool,
121 const GimpCoords *coords,
122 guint32 time,
123 GdkModifierType state,
124 GimpButtonPressType press_type,
125 GimpDisplay *display);
126 static void gimp_transform_grid_tool_button_release (GimpTool *tool,
127 const GimpCoords *coords,
128 guint32 time,
129 GdkModifierType state,
130 GimpButtonReleaseType release_type,
131 GimpDisplay *display);
132 static void gimp_transform_grid_tool_motion (GimpTool *tool,
133 const GimpCoords *coords,
134 guint32 time,
135 GdkModifierType state,
136 GimpDisplay *display);
137 static void gimp_transform_grid_tool_modifier_key (GimpTool *tool,
138 GdkModifierType key,
139 gboolean press,
140 GdkModifierType state,
141 GimpDisplay *display);
142 static void gimp_transform_grid_tool_cursor_update (GimpTool *tool,
143 const GimpCoords *coords,
144 GdkModifierType state,
145 GimpDisplay *display);
146 static const gchar * gimp_transform_grid_tool_can_undo (GimpTool *tool,
147 GimpDisplay *display);
148 static const gchar * gimp_transform_grid_tool_can_redo (GimpTool *tool,
149 GimpDisplay *display);
150 static gboolean gimp_transform_grid_tool_undo (GimpTool *tool,
151 GimpDisplay *display);
152 static gboolean gimp_transform_grid_tool_redo (GimpTool *tool,
153 GimpDisplay *display);
154 static void gimp_transform_grid_tool_options_notify (GimpTool *tool,
155 GimpToolOptions *options,
156 const GParamSpec *pspec);
157
158 static void gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool);
159
160 static void gimp_transform_grid_tool_recalc_matrix (GimpTransformTool *tr_tool);
161 static gchar * gimp_transform_grid_tool_get_undo_desc (GimpTransformTool *tr_tool);
162 static GimpTransformDirection gimp_transform_grid_tool_get_direction
163 (GimpTransformTool *tr_tool);
164 static GeglBuffer * gimp_transform_grid_tool_transform (GimpTransformTool *tr_tool,
165 GimpObject *object,
166 GeglBuffer *orig_buffer,
167 gint orig_offset_x,
168 gint orig_offset_y,
169 GimpColorProfile **buffer_profile,
170 gint *new_offset_x,
171 gint *new_offset_y);
172
173 static void gimp_transform_grid_tool_real_apply_info (GimpTransformGridTool *tg_tool,
174 const TransInfo info);
175 static gchar * gimp_transform_grid_tool_real_get_undo_desc (GimpTransformGridTool *tg_tool);
176 static void gimp_transform_grid_tool_real_update_widget (GimpTransformGridTool *tg_tool);
177 static void gimp_transform_grid_tool_real_widget_changed (GimpTransformGridTool *tg_tool);
178 static GeglBuffer * gimp_transform_grid_tool_real_transform (GimpTransformGridTool *tg_tool,
179 GimpObject *object,
180 GeglBuffer *orig_buffer,
181 gint orig_offset_x,
182 gint orig_offset_y,
183 GimpColorProfile **buffer_profile,
184 gint *new_offset_x,
185 gint *new_offset_y);
186
187 static void gimp_transform_grid_tool_widget_changed (GimpToolWidget *widget,
188 GimpTransformGridTool *tg_tool);
189 static void gimp_transform_grid_tool_widget_response (GimpToolWidget *widget,
190 gint response_id,
191 GimpTransformGridTool *tg_tool);
192
193 static void gimp_transform_grid_tool_filter_flush (GimpDrawableFilter *filter,
194 GimpTransformGridTool *tg_tool);
195
196 static void gimp_transform_grid_tool_image_linked_items_changed
197 (GimpImage *image,
198 GimpTransformGridTool *tg_tool);
199
200 static void gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool);
201 static void gimp_transform_grid_tool_commit (GimpTransformGridTool *tg_tool);
202
203 static void gimp_transform_grid_tool_dialog (GimpTransformGridTool *tg_tool);
204 static void gimp_transform_grid_tool_dialog_update (GimpTransformGridTool *tg_tool);
205 static void gimp_transform_grid_tool_prepare (GimpTransformGridTool *tg_tool,
206 GimpDisplay *display);
207 static GimpToolWidget * gimp_transform_grid_tool_get_widget (GimpTransformGridTool *tg_tool);
208 static void gimp_transform_grid_tool_update_widget (GimpTransformGridTool *tg_tool);
209
210 static void gimp_transform_grid_tool_response (GimpToolGui *gui,
211 gint response_id,
212 GimpTransformGridTool *tg_tool);
213
214 static gboolean gimp_transform_grid_tool_composited_preview (GimpTransformGridTool *tg_tool);
215 static void gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool);
216 static void gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool);
217 static void gimp_transform_grid_tool_update_filters (GimpTransformGridTool *tg_tool);
218 static void gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
219 GimpObject *object);
220 static void gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool);
221
222 static void gimp_transform_grid_tool_add_filter (GimpDrawable *drawable,
223 AddFilterData *data);
224 static void gimp_transform_grid_tool_remove_filter (GimpDrawable *drawable,
225 GimpTransformGridTool *tg_tool);
226
227 static void gimp_transform_grid_tool_effective_mode_changed
228 (GimpLayer *layer,
229 GimpTransformGridTool *tg_tool);
230
231 static Filter * filter_new (GimpTransformGridTool *tg_tool,
232 GimpDrawable *drawable,
233 GimpDrawable *root_drawable,
234 gboolean add_filter);
235 static void filter_free (Filter *filter);
236
237 static UndoInfo * undo_info_new (void);
238 static void undo_info_free (UndoInfo *info);
239
240 static gboolean trans_info_equal (const TransInfo trans_info1,
241 const TransInfo trans_info2);
242 static gboolean trans_infos_equal (const TransInfo *trans_infos1,
243 const TransInfo *trans_infos2);
244
245
G_DEFINE_TYPE(GimpTransformGridTool,gimp_transform_grid_tool,GIMP_TYPE_TRANSFORM_TOOL)246 G_DEFINE_TYPE (GimpTransformGridTool, gimp_transform_grid_tool, GIMP_TYPE_TRANSFORM_TOOL)
247
248 #define parent_class gimp_transform_grid_tool_parent_class
249
250
251 static void
252 gimp_transform_grid_tool_class_init (GimpTransformGridToolClass *klass)
253 {
254 GObjectClass *object_class = G_OBJECT_CLASS (klass);
255 GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
256 GimpDrawToolClass *draw_class = GIMP_DRAW_TOOL_CLASS (klass);
257 GimpTransformToolClass *tr_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
258
259 object_class->finalize = gimp_transform_grid_tool_finalize;
260
261 tool_class->initialize = gimp_transform_grid_tool_initialize;
262 tool_class->control = gimp_transform_grid_tool_control;
263 tool_class->button_press = gimp_transform_grid_tool_button_press;
264 tool_class->button_release = gimp_transform_grid_tool_button_release;
265 tool_class->motion = gimp_transform_grid_tool_motion;
266 tool_class->modifier_key = gimp_transform_grid_tool_modifier_key;
267 tool_class->cursor_update = gimp_transform_grid_tool_cursor_update;
268 tool_class->can_undo = gimp_transform_grid_tool_can_undo;
269 tool_class->can_redo = gimp_transform_grid_tool_can_redo;
270 tool_class->undo = gimp_transform_grid_tool_undo;
271 tool_class->redo = gimp_transform_grid_tool_redo;
272 tool_class->options_notify = gimp_transform_grid_tool_options_notify;
273
274 draw_class->draw = gimp_transform_grid_tool_draw;
275
276 tr_class->recalc_matrix = gimp_transform_grid_tool_recalc_matrix;
277 tr_class->get_undo_desc = gimp_transform_grid_tool_get_undo_desc;
278 tr_class->get_direction = gimp_transform_grid_tool_get_direction;
279 tr_class->transform = gimp_transform_grid_tool_transform;
280
281 klass->info_to_matrix = NULL;
282 klass->matrix_to_info = NULL;
283 klass->apply_info = gimp_transform_grid_tool_real_apply_info;
284 klass->get_undo_desc = gimp_transform_grid_tool_real_get_undo_desc;
285 klass->dialog = NULL;
286 klass->dialog_update = NULL;
287 klass->prepare = NULL;
288 klass->readjust = NULL;
289 klass->get_widget = NULL;
290 klass->update_widget = gimp_transform_grid_tool_real_update_widget;
291 klass->widget_changed = gimp_transform_grid_tool_real_widget_changed;
292 klass->transform = gimp_transform_grid_tool_real_transform;
293
294 klass->ok_button_label = _("_Transform");
295 }
296
297 static void
gimp_transform_grid_tool_init(GimpTransformGridTool * tg_tool)298 gimp_transform_grid_tool_init (GimpTransformGridTool *tg_tool)
299 {
300 GimpTool *tool = GIMP_TOOL (tg_tool);
301
302 gimp_tool_control_set_scroll_lock (tool->control, TRUE);
303 gimp_tool_control_set_preserve (tool->control, FALSE);
304 gimp_tool_control_set_dirty_mask (tool->control,
305 GIMP_DIRTY_IMAGE_SIZE |
306 GIMP_DIRTY_IMAGE_STRUCTURE |
307 GIMP_DIRTY_DRAWABLE |
308 GIMP_DIRTY_SELECTION |
309 GIMP_DIRTY_ACTIVE_DRAWABLE);
310 gimp_tool_control_set_active_modifiers (tool->control,
311 GIMP_TOOL_ACTIVE_MODIFIERS_SAME);
312 gimp_tool_control_set_precision (tool->control,
313 GIMP_CURSOR_PRECISION_SUBPIXEL);
314 gimp_tool_control_set_cursor (tool->control,
315 GIMP_CURSOR_CROSSHAIR_SMALL);
316 gimp_tool_control_set_action_opacity (tool->control,
317 "tools/tools-transform-preview-opacity-set");
318
319 tg_tool->strokes = g_ptr_array_new ();
320 }
321
322 static void
gimp_transform_grid_tool_finalize(GObject * object)323 gimp_transform_grid_tool_finalize (GObject *object)
324 {
325 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (object);
326
327 g_clear_object (&tg_tool->gui);
328 g_clear_pointer (&tg_tool->strokes, g_ptr_array_unref);
329
330 G_OBJECT_CLASS (parent_class)->finalize (object);
331 }
332
333 static gboolean
gimp_transform_grid_tool_initialize(GimpTool * tool,GimpDisplay * display,GError ** error)334 gimp_transform_grid_tool_initialize (GimpTool *tool,
335 GimpDisplay *display,
336 GError **error)
337 {
338 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
339 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
340 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
341 GimpImage *image = gimp_display_get_image (display);
342 GimpDrawable *drawable = gimp_image_get_active_drawable (image);
343 GimpObject *object;
344 UndoInfo *undo_info;
345
346 object = gimp_transform_tool_check_active_object (tr_tool, display, error);
347
348 if (! object)
349 return FALSE;
350
351 tool->display = display;
352 tool->drawable = drawable;
353
354 tr_tool->object = object;
355
356 if (GIMP_IS_DRAWABLE (object))
357 gimp_viewable_preview_freeze (GIMP_VIEWABLE (object));
358
359 /* Initialize the transform_grid tool dialog */
360 if (! tg_tool->gui)
361 gimp_transform_grid_tool_dialog (tg_tool);
362
363 /* Find the transform bounds for some tools (like scale,
364 * perspective) that actually need the bounds for initializing
365 */
366 gimp_transform_tool_bounds (tr_tool, display);
367
368 /* Initialize the tool-specific trans_info, and adjust the tool dialog */
369 gimp_transform_grid_tool_prepare (tg_tool, display);
370
371 /* Recalculate the tool's transformation matrix */
372 gimp_transform_tool_recalc_matrix (tr_tool, display);
373
374 /* Get the on-canvas gui */
375 tg_tool->widget = gimp_transform_grid_tool_get_widget (tg_tool);
376
377 gimp_transform_grid_tool_hide_active_object (tg_tool, object);
378
379 /* start drawing the bounding box and handles... */
380 gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
381
382 /* Initialize undo and redo lists */
383 undo_info = undo_info_new ();
384 tg_tool->undo_list = g_list_prepend (NULL, undo_info);
385 tg_tool->redo_list = NULL;
386
387 /* Save the current transformation info */
388 memcpy (undo_info->trans_infos, tg_tool->trans_infos,
389 sizeof (tg_tool->trans_infos));
390
391 if (tg_options->direction_chain_button)
392 gtk_widget_set_sensitive (tg_options->direction_chain_button, TRUE);
393
394 g_signal_connect (
395 image, "linked-items-changed",
396 G_CALLBACK (gimp_transform_grid_tool_image_linked_items_changed),
397 tg_tool);
398
399 return TRUE;
400 }
401
402 static void
gimp_transform_grid_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)403 gimp_transform_grid_tool_control (GimpTool *tool,
404 GimpToolAction action,
405 GimpDisplay *display)
406 {
407 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
408
409 switch (action)
410 {
411 case GIMP_TOOL_ACTION_PAUSE:
412 case GIMP_TOOL_ACTION_RESUME:
413 break;
414
415 case GIMP_TOOL_ACTION_HALT:
416 gimp_transform_grid_tool_halt (tg_tool);
417 break;
418
419 case GIMP_TOOL_ACTION_COMMIT:
420 if (tool->display)
421 gimp_transform_grid_tool_commit (tg_tool);
422 break;
423 }
424
425 GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
426 }
427
428 static void
gimp_transform_grid_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)429 gimp_transform_grid_tool_button_press (GimpTool *tool,
430 const GimpCoords *coords,
431 guint32 time,
432 GdkModifierType state,
433 GimpButtonPressType press_type,
434 GimpDisplay *display)
435 {
436 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
437
438 if (tg_tool->widget)
439 {
440 gimp_tool_widget_hover (tg_tool->widget, coords, state, TRUE);
441
442 if (gimp_tool_widget_button_press (tg_tool->widget, coords, time, state,
443 press_type))
444 {
445 tg_tool->grab_widget = tg_tool->widget;
446 }
447 }
448
449 gimp_tool_control_activate (tool->control);
450 }
451
452 static void
gimp_transform_grid_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)453 gimp_transform_grid_tool_button_release (GimpTool *tool,
454 const GimpCoords *coords,
455 guint32 time,
456 GdkModifierType state,
457 GimpButtonReleaseType release_type,
458 GimpDisplay *display)
459 {
460 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
461 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
462
463 gimp_tool_control_halt (tool->control);
464
465 if (tg_tool->grab_widget)
466 {
467 gimp_tool_widget_button_release (tg_tool->grab_widget,
468 coords, time, state, release_type);
469 tg_tool->grab_widget = NULL;
470 }
471
472 if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
473 {
474 /* We're done with an interaction, save it on the undo list */
475 gimp_transform_grid_tool_push_internal_undo (tg_tool, FALSE);
476 }
477 else
478 {
479 UndoInfo *undo_info = tg_tool->undo_list->data;
480
481 /* Restore the last saved state */
482 memcpy (tg_tool->trans_infos, undo_info->trans_infos,
483 sizeof (tg_tool->trans_infos));
484
485 /* recalculate the tool's transformation matrix */
486 gimp_transform_tool_recalc_matrix (tr_tool, display);
487 }
488 }
489
490 static void
gimp_transform_grid_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)491 gimp_transform_grid_tool_motion (GimpTool *tool,
492 const GimpCoords *coords,
493 guint32 time,
494 GdkModifierType state,
495 GimpDisplay *display)
496 {
497 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
498
499 if (tg_tool->grab_widget)
500 {
501 gimp_tool_widget_motion (tg_tool->grab_widget, coords, time, state);
502 }
503 }
504
505 static void
gimp_transform_grid_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)506 gimp_transform_grid_tool_modifier_key (GimpTool *tool,
507 GdkModifierType key,
508 gboolean press,
509 GdkModifierType state,
510 GimpDisplay *display)
511 {
512 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
513
514 if (tg_tool->widget)
515 {
516 GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press,
517 state, display);
518 }
519 else
520 {
521 GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
522
523 if (key == gimp_get_constrain_behavior_mask ())
524 {
525 g_object_set (options,
526 "frompivot-scale", ! options->frompivot_scale,
527 "frompivot-shear", ! options->frompivot_shear,
528 "frompivot-perspective", ! options->frompivot_perspective,
529 NULL);
530 }
531 else if (key == gimp_get_extend_selection_mask ())
532 {
533 g_object_set (options,
534 "cornersnap", ! options->cornersnap,
535 "constrain-move", ! options->constrain_move,
536 "constrain-scale", ! options->constrain_scale,
537 "constrain-rotate", ! options->constrain_rotate,
538 "constrain-shear", ! options->constrain_shear,
539 "constrain-perspective", ! options->constrain_perspective,
540 NULL);
541 }
542 }
543 }
544
545 static void
gimp_transform_grid_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)546 gimp_transform_grid_tool_cursor_update (GimpTool *tool,
547 const GimpCoords *coords,
548 GdkModifierType state,
549 GimpDisplay *display)
550 {
551 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
552
553 if (display != tool->display &&
554 ! gimp_transform_tool_check_active_object (tr_tool, display, NULL))
555 {
556 gimp_tool_set_cursor (tool, display,
557 gimp_tool_control_get_cursor (tool->control),
558 gimp_tool_control_get_tool_cursor (tool->control),
559 GIMP_CURSOR_MODIFIER_BAD);
560 return;
561 }
562
563 GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
564 }
565
566 static const gchar *
gimp_transform_grid_tool_can_undo(GimpTool * tool,GimpDisplay * display)567 gimp_transform_grid_tool_can_undo (GimpTool *tool,
568 GimpDisplay *display)
569 {
570 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
571
572 if (! tg_tool->undo_list || ! tg_tool->undo_list->next)
573 return NULL;
574
575 return _("Transform Step");
576 }
577
578 static const gchar *
gimp_transform_grid_tool_can_redo(GimpTool * tool,GimpDisplay * display)579 gimp_transform_grid_tool_can_redo (GimpTool *tool,
580 GimpDisplay *display)
581 {
582 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
583
584 if (! tg_tool->redo_list)
585 return NULL;
586
587 return _("Transform Step");
588 }
589
590 static gboolean
gimp_transform_grid_tool_undo(GimpTool * tool,GimpDisplay * display)591 gimp_transform_grid_tool_undo (GimpTool *tool,
592 GimpDisplay *display)
593 {
594 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
595 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
596 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
597 UndoInfo *undo_info;
598 GimpTransformDirection direction;
599
600 undo_info = tg_tool->undo_list->data;
601 direction = undo_info->direction;
602
603 /* Move undo_info from undo_list to redo_list */
604 tg_tool->redo_list = g_list_prepend (tg_tool->redo_list, undo_info);
605 tg_tool->undo_list = g_list_remove (tg_tool->undo_list, undo_info);
606
607 undo_info = tg_tool->undo_list->data;
608
609 /* Restore the previous transformation info */
610 memcpy (tg_tool->trans_infos, undo_info->trans_infos,
611 sizeof (tg_tool->trans_infos));
612
613 /* Restore the previous transformation direction */
614 if (direction != tr_options->direction)
615 {
616 g_object_set (tr_options,
617 "direction", direction,
618 NULL);
619 }
620
621 /* recalculate the tool's transformation matrix */
622 gimp_transform_tool_recalc_matrix (tr_tool, display);
623
624 return TRUE;
625 }
626
627 static gboolean
gimp_transform_grid_tool_redo(GimpTool * tool,GimpDisplay * display)628 gimp_transform_grid_tool_redo (GimpTool *tool,
629 GimpDisplay *display)
630 {
631 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
632 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
633 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
634 UndoInfo *undo_info;
635 GimpTransformDirection direction;
636
637 undo_info = tg_tool->redo_list->data;
638 direction = undo_info->direction;
639
640 /* Move undo_info from redo_list to undo_list */
641 tg_tool->undo_list = g_list_prepend (tg_tool->undo_list, undo_info);
642 tg_tool->redo_list = g_list_remove (tg_tool->redo_list, undo_info);
643
644 /* Restore the previous transformation info */
645 memcpy (tg_tool->trans_infos, undo_info->trans_infos,
646 sizeof (tg_tool->trans_infos));
647
648 /* Restore the previous transformation direction */
649 if (direction != tr_options->direction)
650 {
651 g_object_set (tr_options,
652 "direction", direction,
653 NULL);
654 }
655
656 /* recalculate the tool's transformation matrix */
657 gimp_transform_tool_recalc_matrix (tr_tool, display);
658
659 return TRUE;
660 }
661
662 static void
gimp_transform_grid_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)663 gimp_transform_grid_tool_options_notify (GimpTool *tool,
664 GimpToolOptions *options,
665 const GParamSpec *pspec)
666 {
667 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
668 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
669 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_OPTIONS (options);
670
671 GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
672
673 if (! strcmp (pspec->name, "type"))
674 {
675 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
676 return;
677 }
678
679 if (! tg_tool->widget)
680 return;
681
682 if (! strcmp (pspec->name, "direction"))
683 {
684 /* recalculate the tool's transformation matrix */
685 gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
686 }
687 else if (! strcmp (pspec->name, "show-preview") ||
688 ! strcmp (pspec->name, "composited-preview"))
689 {
690 if (tg_tool->preview)
691 {
692 GimpDisplay *display;
693 GimpObject *object;
694
695 display = tool->display;
696 object = gimp_transform_tool_get_active_object (tr_tool, display);
697
698 if (object)
699 {
700 if (tg_options->show_preview &&
701 ! gimp_transform_grid_tool_composited_preview (tg_tool))
702 {
703 gimp_transform_grid_tool_hide_active_object (tg_tool, object);
704 }
705 else
706 {
707 gimp_transform_grid_tool_show_active_object (tg_tool);
708 }
709 }
710
711 gimp_transform_grid_tool_update_preview (tg_tool);
712 }
713 }
714 else if (! strcmp (pspec->name, "preview-linked") &&
715 tg_tool->filters)
716 {
717 gimp_transform_grid_tool_update_filters (tg_tool);
718 gimp_transform_grid_tool_update_preview (tg_tool);
719 }
720 else if (! strcmp (pspec->name, "interpolation") ||
721 ! strcmp (pspec->name, "clip") ||
722 ! strcmp (pspec->name, "preview-opacity"))
723 {
724 gimp_transform_grid_tool_update_preview (tg_tool);
725 }
726 else if (g_str_has_prefix (pspec->name, "constrain-") ||
727 g_str_has_prefix (pspec->name, "frompivot-") ||
728 ! strcmp (pspec->name, "fixedpivot") ||
729 ! strcmp (pspec->name, "cornersnap"))
730 {
731 gimp_transform_grid_tool_dialog_update (tg_tool);
732 }
733 }
734
735 static void
gimp_transform_grid_tool_draw(GimpDrawTool * draw_tool)736 gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
737 {
738 GimpTool *tool = GIMP_TOOL (draw_tool);
739 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
740 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (draw_tool);
741 GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
742 GimpTransformOptions *tr_options = GIMP_TRANSFORM_OPTIONS (options);
743 GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
744 GimpImage *image = gimp_display_get_image (tool->display);
745 GimpMatrix3 matrix = tr_tool->transform;
746 GimpCanvasItem *item;
747
748 if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
749 gimp_matrix3_invert (&matrix);
750
751 if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER ||
752 tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
753 {
754 GimpPickable *pickable;
755
756 if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
757 {
758 if (! shell->show_all)
759 pickable = GIMP_PICKABLE (image);
760 else
761 pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
762 }
763 else
764 {
765 pickable = GIMP_PICKABLE (tool->drawable);
766 }
767
768 tg_tool->preview =
769 gimp_draw_tool_add_transform_preview (draw_tool,
770 pickable,
771 &matrix,
772 tr_tool->x1,
773 tr_tool->y1,
774 tr_tool->x2,
775 tr_tool->y2);
776 g_object_add_weak_pointer (G_OBJECT (tg_tool->preview),
777 (gpointer) &tg_tool->preview);
778 }
779
780 if (tr_options->type == GIMP_TRANSFORM_TYPE_SELECTION)
781 {
782 const GimpBoundSeg *segs_in;
783 const GimpBoundSeg *segs_out;
784 gint n_segs_in;
785 gint n_segs_out;
786
787 gimp_channel_boundary (gimp_image_get_mask (image),
788 &segs_in, &segs_out,
789 &n_segs_in, &n_segs_out,
790 0, 0, 0, 0);
791
792 if (segs_in)
793 {
794 tg_tool->boundary_in =
795 gimp_draw_tool_add_boundary (draw_tool,
796 segs_in, n_segs_in,
797 &matrix,
798 0, 0);
799 g_object_add_weak_pointer (G_OBJECT (tg_tool->boundary_in),
800 (gpointer) &tg_tool->boundary_in);
801
802 gimp_canvas_item_set_visible (tg_tool->boundary_in,
803 tr_tool->transform_valid);
804 }
805
806 if (segs_out)
807 {
808 tg_tool->boundary_out =
809 gimp_draw_tool_add_boundary (draw_tool,
810 segs_out, n_segs_out,
811 &matrix,
812 0, 0);
813 g_object_add_weak_pointer (G_OBJECT (tg_tool->boundary_out),
814 (gpointer) &tg_tool->boundary_out);
815
816 gimp_canvas_item_set_visible (tg_tool->boundary_out,
817 tr_tool->transform_valid);
818 }
819 }
820 else if (tr_options->type == GIMP_TRANSFORM_TYPE_PATH)
821 {
822 GimpVectors *vectors = gimp_image_get_active_vectors (image);
823
824 if (vectors)
825 {
826 GimpStroke *stroke = NULL;
827
828 while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
829 {
830 GArray *coords;
831 gboolean closed;
832
833 coords = gimp_stroke_interpolate (stroke, 1.0, &closed);
834
835 if (coords && coords->len)
836 {
837 item =
838 gimp_draw_tool_add_strokes (draw_tool,
839 &g_array_index (coords,
840 GimpCoords, 0),
841 coords->len, &matrix, FALSE);
842
843 g_ptr_array_add (tg_tool->strokes, item);
844 g_object_weak_ref (G_OBJECT (item),
845 (GWeakNotify) g_ptr_array_remove,
846 tg_tool->strokes);
847
848 gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
849 }
850
851 if (coords)
852 g_array_free (coords, TRUE);
853 }
854 }
855 }
856
857 GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
858
859 gimp_transform_grid_tool_update_preview (tg_tool);
860 }
861
862 static void
gimp_transform_grid_tool_recalc_matrix(GimpTransformTool * tr_tool)863 gimp_transform_grid_tool_recalc_matrix (GimpTransformTool *tr_tool)
864 {
865 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
866 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
867 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tr_tool);
868
869 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->info_to_matrix)
870 {
871 GimpMatrix3 forward_transform;
872 GimpMatrix3 backward_transform;
873 gboolean forward_transform_valid;
874 gboolean backward_transform_valid;
875
876 tg_tool->trans_info = tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD];
877 forward_transform_valid = gimp_transform_grid_tool_info_to_matrix (
878 tg_tool, &forward_transform);
879
880 tg_tool->trans_info = tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD];
881 backward_transform_valid = gimp_transform_grid_tool_info_to_matrix (
882 tg_tool, &backward_transform);
883
884 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info &&
885 tg_options->direction_linked)
886 {
887 GimpMatrix3 transform = tr_tool->transform;
888
889 switch (tr_options->direction)
890 {
891 case GIMP_TRANSFORM_FORWARD:
892 if (forward_transform_valid)
893 {
894 gimp_matrix3_invert (&transform);
895
896 backward_transform = forward_transform;
897 gimp_matrix3_mult (&transform, &backward_transform);
898
899 tg_tool->trans_info =
900 tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD];
901 gimp_transform_grid_tool_matrix_to_info (tg_tool,
902 &backward_transform);
903 backward_transform_valid =
904 gimp_transform_grid_tool_info_to_matrix (
905 tg_tool, &backward_transform);
906 }
907 break;
908
909 case GIMP_TRANSFORM_BACKWARD:
910 if (backward_transform_valid)
911 {
912 forward_transform = backward_transform;
913 gimp_matrix3_mult (&transform, &forward_transform);
914
915 tg_tool->trans_info =
916 tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD];
917 gimp_transform_grid_tool_matrix_to_info (tg_tool,
918 &forward_transform);
919 forward_transform_valid =
920 gimp_transform_grid_tool_info_to_matrix (
921 tg_tool, &forward_transform);
922 }
923 break;
924 }
925 }
926 else if (forward_transform_valid && backward_transform_valid)
927 {
928 tr_tool->transform = backward_transform;
929 gimp_matrix3_invert (&tr_tool->transform);
930 gimp_matrix3_mult (&forward_transform, &tr_tool->transform);
931 }
932
933 tr_tool->transform_valid = forward_transform_valid &&
934 backward_transform_valid;
935 }
936
937 tg_tool->trans_info = tg_tool->trans_infos[tr_options->direction];
938
939 gimp_transform_grid_tool_dialog_update (tg_tool);
940 gimp_transform_grid_tool_update_sensitivity (tg_tool);
941 gimp_transform_grid_tool_update_widget (tg_tool);
942 gimp_transform_grid_tool_update_preview (tg_tool);
943
944 if (tg_tool->gui)
945 gimp_tool_gui_show (tg_tool->gui);
946 }
947
948 static gchar *
gimp_transform_grid_tool_get_undo_desc(GimpTransformTool * tr_tool)949 gimp_transform_grid_tool_get_undo_desc (GimpTransformTool *tr_tool)
950 {
951 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
952 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
953 gchar *result;
954
955 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info)
956 {
957 TransInfo trans_info;
958
959 memcpy (&trans_info, &tg_tool->init_trans_info, sizeof (TransInfo));
960
961 tg_tool->trans_info = trans_info;
962 gimp_transform_grid_tool_matrix_to_info (tg_tool, &tr_tool->transform);
963 result = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_undo_desc (
964 tg_tool);
965 }
966 else if (trans_info_equal (tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD],
967 tg_tool->init_trans_info))
968 {
969 tg_tool->trans_info = tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD];
970 result = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_undo_desc (
971 tg_tool);
972 }
973 else if (trans_info_equal (tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD],
974 tg_tool->init_trans_info))
975 {
976 gchar *desc;
977
978 tg_tool->trans_info = tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD];
979 desc = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_undo_desc (
980 tg_tool);
981
982 result = g_strdup_printf (_("%s (Corrective)"), desc);
983
984 g_free (desc);
985 }
986 else
987 {
988 result = GIMP_TRANSFORM_TOOL_CLASS (parent_class)->get_undo_desc (
989 tr_tool);
990 }
991
992 tg_tool->trans_info = tg_tool->trans_infos[tr_options->direction];
993
994 return result;
995 }
996
997 static GimpTransformDirection
gimp_transform_grid_tool_get_direction(GimpTransformTool * tr_tool)998 gimp_transform_grid_tool_get_direction (GimpTransformTool *tr_tool)
999 {
1000 return GIMP_TRANSFORM_FORWARD;
1001 }
1002
1003 static GeglBuffer *
gimp_transform_grid_tool_transform(GimpTransformTool * tr_tool,GimpObject * object,GeglBuffer * orig_buffer,gint orig_offset_x,gint orig_offset_y,GimpColorProfile ** buffer_profile,gint * new_offset_x,gint * new_offset_y)1004 gimp_transform_grid_tool_transform (GimpTransformTool *tr_tool,
1005 GimpObject *object,
1006 GeglBuffer *orig_buffer,
1007 gint orig_offset_x,
1008 gint orig_offset_y,
1009 GimpColorProfile **buffer_profile,
1010 gint *new_offset_x,
1011 gint *new_offset_y)
1012 {
1013 GimpTool *tool = GIMP_TOOL (tr_tool);
1014 GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
1015 GimpDisplay *display = tool->display;
1016 GimpImage *image = gimp_display_get_image (display);
1017 GeglBuffer *new_buffer;
1018
1019 /* Send the request for the transformation to the tool...
1020 */
1021 new_buffer =
1022 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->transform (tg_tool,
1023 object,
1024 orig_buffer,
1025 orig_offset_x,
1026 orig_offset_y,
1027 buffer_profile,
1028 new_offset_x,
1029 new_offset_y);
1030
1031 gimp_image_undo_push (image, GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO,
1032 GIMP_UNDO_TRANSFORM_GRID, NULL,
1033 0,
1034 "transform-tool", tg_tool,
1035 NULL);
1036
1037 return new_buffer;
1038 }
1039
1040 static void
gimp_transform_grid_tool_real_apply_info(GimpTransformGridTool * tg_tool,const TransInfo info)1041 gimp_transform_grid_tool_real_apply_info (GimpTransformGridTool *tg_tool,
1042 const TransInfo info)
1043 {
1044 memcpy (tg_tool->trans_info, info, sizeof (TransInfo));
1045 }
1046
1047 static gchar *
gimp_transform_grid_tool_real_get_undo_desc(GimpTransformGridTool * tg_tool)1048 gimp_transform_grid_tool_real_get_undo_desc (GimpTransformGridTool *tg_tool)
1049 {
1050 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1051
1052 return GIMP_TRANSFORM_TOOL_CLASS (parent_class)->get_undo_desc (tr_tool);
1053 }
1054
1055 static void
gimp_transform_grid_tool_real_update_widget(GimpTransformGridTool * tg_tool)1056 gimp_transform_grid_tool_real_update_widget (GimpTransformGridTool *tg_tool)
1057 {
1058 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->info_to_matrix)
1059 {
1060 GimpMatrix3 transform;
1061
1062 gimp_transform_grid_tool_info_to_matrix (tg_tool, &transform);
1063
1064 g_object_set (tg_tool->widget,
1065 "transform", &transform,
1066 NULL);
1067 }
1068 }
1069
1070 static void
gimp_transform_grid_tool_real_widget_changed(GimpTransformGridTool * tg_tool)1071 gimp_transform_grid_tool_real_widget_changed (GimpTransformGridTool *tg_tool)
1072 {
1073 GimpTool *tool = GIMP_TOOL (tg_tool);
1074 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1075 GimpToolWidget *widget = tg_tool->widget;
1076
1077 /* suppress the call to GimpTransformGridTool::update_widget() when
1078 * recalculating the matrix
1079 */
1080 tg_tool->widget = NULL;
1081
1082 gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
1083
1084 tg_tool->widget = widget;
1085 }
1086
1087 static GeglBuffer *
gimp_transform_grid_tool_real_transform(GimpTransformGridTool * tg_tool,GimpObject * object,GeglBuffer * orig_buffer,gint orig_offset_x,gint orig_offset_y,GimpColorProfile ** buffer_profile,gint * new_offset_x,gint * new_offset_y)1088 gimp_transform_grid_tool_real_transform (GimpTransformGridTool *tg_tool,
1089 GimpObject *object,
1090 GeglBuffer *orig_buffer,
1091 gint orig_offset_x,
1092 gint orig_offset_y,
1093 GimpColorProfile **buffer_profile,
1094 gint *new_offset_x,
1095 gint *new_offset_y)
1096 {
1097 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1098
1099 return GIMP_TRANSFORM_TOOL_CLASS (parent_class)->transform (tr_tool,
1100 object,
1101 orig_buffer,
1102 orig_offset_x,
1103 orig_offset_y,
1104 buffer_profile,
1105 new_offset_x,
1106 new_offset_y);
1107 }
1108
1109 static void
gimp_transform_grid_tool_widget_changed(GimpToolWidget * widget,GimpTransformGridTool * tg_tool)1110 gimp_transform_grid_tool_widget_changed (GimpToolWidget *widget,
1111 GimpTransformGridTool *tg_tool)
1112 {
1113 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->widget_changed)
1114 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->widget_changed (tg_tool);
1115 }
1116
1117 static void
gimp_transform_grid_tool_widget_response(GimpToolWidget * widget,gint response_id,GimpTransformGridTool * tg_tool)1118 gimp_transform_grid_tool_widget_response (GimpToolWidget *widget,
1119 gint response_id,
1120 GimpTransformGridTool *tg_tool)
1121 {
1122 switch (response_id)
1123 {
1124 case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
1125 gimp_transform_grid_tool_response (NULL, GTK_RESPONSE_OK, tg_tool);
1126 break;
1127
1128 case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
1129 gimp_transform_grid_tool_response (NULL, GTK_RESPONSE_CANCEL, tg_tool);
1130 break;
1131
1132 case GIMP_TOOL_WIDGET_RESPONSE_RESET:
1133 gimp_transform_grid_tool_response (NULL, RESPONSE_RESET, tg_tool);
1134 break;
1135 }
1136 }
1137
1138 static void
gimp_transform_grid_tool_filter_flush(GimpDrawableFilter * filter,GimpTransformGridTool * tg_tool)1139 gimp_transform_grid_tool_filter_flush (GimpDrawableFilter *filter,
1140 GimpTransformGridTool *tg_tool)
1141 {
1142 GimpTool *tool = GIMP_TOOL (tg_tool);
1143 GimpImage *image = gimp_display_get_image (tool->display);
1144
1145 gimp_projection_flush (gimp_image_get_projection (image));
1146 }
1147
1148 static void
gimp_transform_grid_tool_image_linked_items_changed(GimpImage * image,GimpTransformGridTool * tg_tool)1149 gimp_transform_grid_tool_image_linked_items_changed (GimpImage *image,
1150 GimpTransformGridTool *tg_tool)
1151 {
1152 if (tg_tool->filters)
1153 {
1154 gimp_transform_grid_tool_update_filters (tg_tool);
1155 gimp_transform_grid_tool_update_preview (tg_tool);
1156 }
1157 }
1158
1159 static void
gimp_transform_grid_tool_halt(GimpTransformGridTool * tg_tool)1160 gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
1161 {
1162 GimpTool *tool = GIMP_TOOL (tg_tool);
1163 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1164 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1165
1166 if (tool->display)
1167 {
1168 GimpImage *image = gimp_display_get_image (tool->display);
1169
1170 g_signal_handlers_disconnect_by_func (
1171 image,
1172 gimp_transform_grid_tool_image_linked_items_changed,
1173 tg_tool);
1174 }
1175
1176 if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tg_tool)))
1177 gimp_draw_tool_stop (GIMP_DRAW_TOOL (tg_tool));
1178
1179 gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), NULL);
1180 g_clear_object (&tg_tool->widget);
1181
1182 g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
1183 g_clear_pointer (&tg_tool->preview_drawables, g_list_free);
1184
1185 if (tg_tool->gui)
1186 gimp_tool_gui_hide (tg_tool->gui);
1187
1188 if (tg_tool->redo_list)
1189 {
1190 g_list_free_full (tg_tool->redo_list, (GDestroyNotify) undo_info_free);
1191 tg_tool->redo_list = NULL;
1192 }
1193
1194 if (tg_tool->undo_list)
1195 {
1196 g_list_free_full (tg_tool->undo_list, (GDestroyNotify) undo_info_free);
1197 tg_tool->undo_list = NULL;
1198 }
1199
1200 gimp_transform_grid_tool_show_active_object (tg_tool);
1201
1202 if (tg_options->direction_chain_button)
1203 {
1204 g_object_set (tg_options,
1205 "direction-linked", FALSE,
1206 NULL);
1207
1208 gtk_widget_set_sensitive (tg_options->direction_chain_button, FALSE);
1209 }
1210
1211 tool->display = NULL;
1212 tool->drawable = NULL;
1213
1214 if (tr_tool->object)
1215 {
1216 if (GIMP_IS_DRAWABLE (tr_tool->object))
1217 gimp_viewable_preview_thaw (GIMP_VIEWABLE (tr_tool->object));
1218
1219 tr_tool->object = NULL;
1220 }
1221 }
1222
1223 static void
gimp_transform_grid_tool_commit(GimpTransformGridTool * tg_tool)1224 gimp_transform_grid_tool_commit (GimpTransformGridTool *tg_tool)
1225 {
1226 GimpTool *tool = GIMP_TOOL (tg_tool);
1227 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1228 GimpDisplay *display = tool->display;
1229
1230 /* undraw the tool before we muck around with the transform matrix */
1231 gimp_draw_tool_stop (GIMP_DRAW_TOOL (tg_tool));
1232
1233 gimp_transform_tool_transform (tr_tool, display);
1234 }
1235
1236 static void
gimp_transform_grid_tool_dialog(GimpTransformGridTool * tg_tool)1237 gimp_transform_grid_tool_dialog (GimpTransformGridTool *tg_tool)
1238 {
1239 GimpTool *tool = GIMP_TOOL (tg_tool);
1240 GimpToolInfo *tool_info = tool->tool_info;
1241 GimpDisplayShell *shell;
1242 const gchar *ok_button_label;
1243
1244 if (! GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog)
1245 return;
1246
1247 g_return_if_fail (tool->display != NULL);
1248
1249 shell = gimp_display_get_shell (tool->display);
1250
1251 ok_button_label = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->ok_button_label;
1252
1253 tg_tool->gui = gimp_tool_gui_new (tool_info,
1254 NULL, NULL, NULL, NULL,
1255 gtk_widget_get_screen (GTK_WIDGET (shell)),
1256 gimp_widget_get_monitor (GTK_WIDGET (shell)),
1257 TRUE,
1258 NULL);
1259
1260 gimp_tool_gui_add_button (tg_tool->gui, _("_Reset"), RESPONSE_RESET);
1261 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->readjust)
1262 gimp_tool_gui_add_button (tg_tool->gui, _("Re_adjust"), RESPONSE_READJUST);
1263 gimp_tool_gui_add_button (tg_tool->gui, _("_Cancel"), GTK_RESPONSE_CANCEL);
1264 gimp_tool_gui_add_button (tg_tool->gui, ok_button_label, GTK_RESPONSE_OK);
1265
1266 gimp_tool_gui_set_auto_overlay (tg_tool->gui, TRUE);
1267 gimp_tool_gui_set_default_response (tg_tool->gui, GTK_RESPONSE_OK);
1268
1269 gimp_tool_gui_set_alternative_button_order (tg_tool->gui,
1270 RESPONSE_RESET,
1271 RESPONSE_READJUST,
1272 GTK_RESPONSE_OK,
1273 GTK_RESPONSE_CANCEL,
1274 -1);
1275
1276 g_signal_connect (tg_tool->gui, "response",
1277 G_CALLBACK (gimp_transform_grid_tool_response),
1278 tg_tool);
1279
1280 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog (tg_tool);
1281 }
1282
1283 static void
gimp_transform_grid_tool_dialog_update(GimpTransformGridTool * tg_tool)1284 gimp_transform_grid_tool_dialog_update (GimpTransformGridTool *tg_tool)
1285 {
1286 if (tg_tool->gui &&
1287 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog_update)
1288 {
1289 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog_update (tg_tool);
1290 }
1291 }
1292
1293 static void
gimp_transform_grid_tool_prepare(GimpTransformGridTool * tg_tool,GimpDisplay * display)1294 gimp_transform_grid_tool_prepare (GimpTransformGridTool *tg_tool,
1295 GimpDisplay *display)
1296 {
1297 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1298
1299 if (tg_tool->gui)
1300 {
1301 GimpObject *object = gimp_transform_tool_get_active_object (tr_tool,
1302 display);
1303
1304 gimp_tool_gui_set_shell (tg_tool->gui, gimp_display_get_shell (display));
1305 gimp_tool_gui_set_viewable (tg_tool->gui, GIMP_VIEWABLE (object));
1306 }
1307
1308 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->prepare)
1309 {
1310 tg_tool->trans_info = tg_tool->init_trans_info;
1311 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->prepare (tg_tool);
1312
1313 memcpy (tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD],
1314 tg_tool->init_trans_info, sizeof (TransInfo));
1315 memcpy (tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD],
1316 tg_tool->init_trans_info, sizeof (TransInfo));
1317 }
1318
1319 gimp_matrix3_identity (&tr_tool->transform);
1320 tr_tool->transform_valid = TRUE;
1321 }
1322
1323 static GimpToolWidget *
gimp_transform_grid_tool_get_widget(GimpTransformGridTool * tg_tool)1324 gimp_transform_grid_tool_get_widget (GimpTransformGridTool *tg_tool)
1325 {
1326 static const gchar *properties[] =
1327 {
1328 "constrain-move",
1329 "constrain-scale",
1330 "constrain-rotate",
1331 "constrain-shear",
1332 "constrain-perspective",
1333 "frompivot-scale",
1334 "frompivot-shear",
1335 "frompivot-perspective",
1336 "cornersnap",
1337 "fixedpivot"
1338 };
1339
1340 GimpToolWidget *widget = NULL;
1341
1342 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_widget)
1343 {
1344 GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1345 gint i;
1346
1347 widget = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_widget (tg_tool);
1348
1349 gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), widget);
1350
1351 g_object_bind_property (G_OBJECT (options), "grid-type",
1352 G_OBJECT (widget), "guide-type",
1353 G_BINDING_SYNC_CREATE |
1354 G_BINDING_BIDIRECTIONAL);
1355 g_object_bind_property (G_OBJECT (options), "grid-size",
1356 G_OBJECT (widget), "n-guides",
1357 G_BINDING_SYNC_CREATE |
1358 G_BINDING_BIDIRECTIONAL);
1359
1360 for (i = 0; i < G_N_ELEMENTS (properties); i++)
1361 g_object_bind_property (G_OBJECT (options), properties[i],
1362 G_OBJECT (widget), properties[i],
1363 G_BINDING_SYNC_CREATE |
1364 G_BINDING_BIDIRECTIONAL);
1365
1366 g_signal_connect (widget, "changed",
1367 G_CALLBACK (gimp_transform_grid_tool_widget_changed),
1368 tg_tool);
1369 g_signal_connect (widget, "response",
1370 G_CALLBACK (gimp_transform_grid_tool_widget_response),
1371 tg_tool);
1372 }
1373
1374 return widget;
1375 }
1376
1377 static void
gimp_transform_grid_tool_update_widget(GimpTransformGridTool * tg_tool)1378 gimp_transform_grid_tool_update_widget (GimpTransformGridTool *tg_tool)
1379 {
1380 if (tg_tool->widget &&
1381 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->update_widget)
1382 {
1383 g_signal_handlers_block_by_func (
1384 tg_tool->widget,
1385 G_CALLBACK (gimp_transform_grid_tool_widget_changed),
1386 tg_tool);
1387
1388 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->update_widget (tg_tool);
1389
1390 g_signal_handlers_unblock_by_func (
1391 tg_tool->widget,
1392 G_CALLBACK (gimp_transform_grid_tool_widget_changed),
1393 tg_tool);
1394 }
1395 }
1396
1397 static void
gimp_transform_grid_tool_response(GimpToolGui * gui,gint response_id,GimpTransformGridTool * tg_tool)1398 gimp_transform_grid_tool_response (GimpToolGui *gui,
1399 gint response_id,
1400 GimpTransformGridTool *tg_tool)
1401 {
1402 GimpTool *tool = GIMP_TOOL (tg_tool);
1403 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1404 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
1405 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1406 GimpDisplay *display = tool->display;
1407
1408 /* we can get here while already committing a transformation. just return in
1409 * this case. see issue #4734.
1410 */
1411 if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tg_tool)))
1412 return;
1413
1414 switch (response_id)
1415 {
1416 case RESPONSE_RESET:
1417 {
1418 gboolean direction_linked;
1419
1420 /* restore the initial transformation info */
1421 memcpy (tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD],
1422 tg_tool->init_trans_info,
1423 sizeof (TransInfo));
1424 memcpy (tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD],
1425 tg_tool->init_trans_info,
1426 sizeof (TransInfo));
1427
1428 /* recalculate the tool's transformation matrix */
1429 direction_linked = tg_options->direction_linked;
1430 tg_options->direction_linked = FALSE;
1431 gimp_transform_tool_recalc_matrix (tr_tool, display);
1432 tg_options->direction_linked = direction_linked;
1433
1434 /* push the restored info to the undo stack */
1435 gimp_transform_grid_tool_push_internal_undo (tg_tool, FALSE);
1436 }
1437 break;
1438
1439 case RESPONSE_READJUST:
1440 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->readjust &&
1441 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info &&
1442 tr_tool->transform_valid)
1443 {
1444 TransInfo old_trans_infos[2];
1445 gboolean direction_linked;
1446 gboolean transform_valid;
1447
1448 /* save the current transformation info */
1449 memcpy (old_trans_infos, tg_tool->trans_infos,
1450 sizeof (old_trans_infos));
1451
1452 /* readjust the transformation info to view */
1453 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->readjust (tg_tool);
1454
1455 /* recalculate the tool's transformation matrix, preserving the
1456 * overall transformation
1457 */
1458 direction_linked = tg_options->direction_linked;
1459 tg_options->direction_linked = TRUE;
1460 gimp_transform_tool_recalc_matrix (tr_tool, display);
1461 tg_options->direction_linked = direction_linked;
1462
1463 transform_valid = tr_tool->transform_valid;
1464
1465 /* if the resulting transformation is invalid, or if the
1466 * transformation info is already adjusted to view ...
1467 */
1468 if (! transform_valid ||
1469 trans_infos_equal (old_trans_infos, tg_tool->trans_infos))
1470 {
1471 /* ... readjust the transformation info to the item bounds */
1472 GimpMatrix3 transform = tr_tool->transform;
1473
1474 if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
1475 gimp_matrix3_invert (&transform);
1476
1477 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->apply_info (
1478 tg_tool, tg_tool->init_trans_info);
1479 GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info (
1480 tg_tool, &transform);
1481
1482 /* recalculate the tool's transformation matrix, preserving the
1483 * overall transformation
1484 */
1485 direction_linked = tg_options->direction_linked;
1486 tg_options->direction_linked = TRUE;
1487 gimp_transform_tool_recalc_matrix (tr_tool, display);
1488 tg_options->direction_linked = direction_linked;
1489
1490 if (! tr_tool->transform_valid ||
1491 ! trans_infos_equal (old_trans_infos, tg_tool->trans_infos))
1492 {
1493 transform_valid = tr_tool->transform_valid;
1494 }
1495 }
1496
1497 if (transform_valid)
1498 {
1499 /* push the new info to the undo stack */
1500 gimp_transform_grid_tool_push_internal_undo (tg_tool, FALSE);
1501 }
1502 else
1503 {
1504 /* restore the old transformation info */
1505 memcpy (tg_tool->trans_infos, old_trans_infos,
1506 sizeof (old_trans_infos));
1507
1508 /* recalculate the tool's transformation matrix */
1509 direction_linked = tg_options->direction_linked;
1510 tg_options->direction_linked = FALSE;
1511 gimp_transform_tool_recalc_matrix (tr_tool, display);
1512 tg_options->direction_linked = direction_linked;
1513
1514 gimp_tool_message_literal (tool, tool->display,
1515 _("Cannot readjust the transformation"));
1516 }
1517 }
1518 break;
1519
1520 case GTK_RESPONSE_OK:
1521 g_return_if_fail (display != NULL);
1522 gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
1523 break;
1524
1525 default:
1526 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
1527
1528 /* update the undo actions / menu items */
1529 if (display)
1530 gimp_image_flush (gimp_display_get_image (display));
1531 break;
1532 }
1533 }
1534
1535 static gboolean
gimp_transform_grid_tool_composited_preview(GimpTransformGridTool * tg_tool)1536 gimp_transform_grid_tool_composited_preview (GimpTransformGridTool *tg_tool)
1537 {
1538 GimpTool *tool = GIMP_TOOL (tg_tool);
1539 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
1540 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1541 GimpImage *image = gimp_display_get_image (tool->display);
1542
1543 return tg_options->composited_preview &&
1544 tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
1545 gimp_channel_is_empty (gimp_image_get_mask (image));
1546 }
1547
1548 static void
gimp_transform_grid_tool_update_sensitivity(GimpTransformGridTool * tg_tool)1549 gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
1550 {
1551 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1552
1553 if (! tg_tool->gui)
1554 return;
1555
1556 gimp_tool_gui_set_response_sensitive (
1557 tg_tool->gui, GTK_RESPONSE_OK,
1558 tr_tool->transform_valid);
1559
1560 gimp_tool_gui_set_response_sensitive (
1561 tg_tool->gui, RESPONSE_RESET,
1562 ! (trans_info_equal (tg_tool->trans_infos[GIMP_TRANSFORM_FORWARD],
1563 tg_tool->init_trans_info) &&
1564 trans_info_equal (tg_tool->trans_infos[GIMP_TRANSFORM_BACKWARD],
1565 tg_tool->init_trans_info)));
1566
1567 gimp_tool_gui_set_response_sensitive (
1568 tg_tool->gui, RESPONSE_READJUST,
1569 tr_tool->transform_valid);
1570 }
1571
1572 static void
gimp_transform_grid_tool_update_preview(GimpTransformGridTool * tg_tool)1573 gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool)
1574 {
1575 GimpTool *tool = GIMP_TOOL (tg_tool);
1576 GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
1577 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
1578 GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1579 gint i;
1580
1581 if (! tool->display)
1582 return;
1583
1584 if (tg_options->show_preview &&
1585 gimp_transform_grid_tool_composited_preview (tg_tool) &&
1586 tr_tool->transform_valid)
1587 {
1588 GHashTableIter iter;
1589 GimpDrawable *drawable;
1590 Filter *filter;
1591 gboolean flush = FALSE;
1592
1593 if (! tg_tool->filters)
1594 {
1595 tg_tool->filters = g_hash_table_new_full (
1596 g_direct_hash, g_direct_equal,
1597 NULL, (GDestroyNotify) filter_free);
1598
1599 gimp_transform_grid_tool_update_filters (tg_tool);
1600 }
1601
1602 g_hash_table_iter_init (&iter, tg_tool->filters);
1603
1604 while (g_hash_table_iter_next (&iter,
1605 (gpointer *) &drawable,
1606 (gpointer *) &filter))
1607 {
1608 GimpMatrix3 transform;
1609 GeglRectangle bounds;
1610 gint offset_x;
1611 gint offset_y;
1612 gint width;
1613 gint height;
1614 gint x1, y1;
1615 gint x2, y2;
1616 gboolean update = FALSE;
1617
1618 if (! filter->filter)
1619 continue;
1620
1621 gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
1622
1623 width = gimp_item_get_width (GIMP_ITEM (drawable));
1624 height = gimp_item_get_height (GIMP_ITEM (drawable));
1625
1626 gimp_matrix3_identity (&transform);
1627 gimp_matrix3_translate (&transform, +offset_x, +offset_y);
1628 gimp_matrix3_mult (&tr_tool->transform, &transform);
1629 gimp_matrix3_translate (&transform, -offset_x, -offset_y);
1630
1631 gimp_transform_resize_boundary (&tr_tool->transform,
1632 gimp_item_get_clip (
1633 GIMP_ITEM (filter->root_drawable),
1634 tr_options->clip),
1635 offset_x, offset_y,
1636 offset_x + width, offset_y + height,
1637 &x1, &y1,
1638 &x2, &y2);
1639
1640 bounds.x = x1 - offset_x;
1641 bounds.y = y1 - offset_y;
1642 bounds.width = x2 - x1;
1643 bounds.height = y2 - y1;
1644
1645 if (! gimp_matrix3_equal (&transform, &filter->transform))
1646 {
1647 filter->transform = transform;
1648
1649 gimp_gegl_node_set_matrix (filter->transform_node, &transform);
1650
1651 update = TRUE;
1652 }
1653
1654 if (! gegl_rectangle_equal (&bounds, &filter->bounds))
1655 {
1656 filter->bounds = bounds;
1657
1658 gegl_node_set (filter->crop_node,
1659 "x", (gdouble) bounds.x,
1660 "y", (gdouble) bounds.y,
1661 "width", (gdouble) bounds.width,
1662 "height", (gdouble) bounds.height,
1663 NULL);
1664
1665 update = TRUE;
1666 }
1667
1668 if (GIMP_IS_LAYER (drawable))
1669 {
1670 gimp_drawable_filter_set_add_alpha (
1671 filter->filter,
1672 tr_options->interpolation != GIMP_INTERPOLATION_NONE);
1673 }
1674
1675 if (update)
1676 {
1677 if (tg_options->synchronous_preview)
1678 {
1679 g_signal_handlers_block_by_func (
1680 filter->filter,
1681 G_CALLBACK (gimp_transform_grid_tool_filter_flush),
1682 tg_tool);
1683 }
1684
1685 gimp_drawable_filter_apply (filter->filter, NULL);
1686
1687 if (tg_options->synchronous_preview)
1688 {
1689 g_signal_handlers_unblock_by_func (
1690 filter->filter,
1691 G_CALLBACK (gimp_transform_grid_tool_filter_flush),
1692 tg_tool);
1693
1694 flush = TRUE;
1695 }
1696 }
1697 }
1698
1699 if (flush)
1700 {
1701 GimpImage *image = gimp_display_get_image (tool->display);
1702
1703 gimp_projection_flush_now (gimp_image_get_projection (image), TRUE);
1704 gimp_display_flush_now (tool->display);
1705 }
1706 }
1707 else
1708 {
1709 g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
1710 g_clear_pointer (&tg_tool->preview_drawables, g_list_free);
1711 }
1712
1713 if (tg_tool->preview)
1714 {
1715 if (tg_options->show_preview &&
1716 ! gimp_transform_grid_tool_composited_preview (tg_tool) &&
1717 tr_tool->transform_valid)
1718 {
1719 gimp_canvas_item_begin_change (tg_tool->preview);
1720 gimp_canvas_item_set_visible (tg_tool->preview, TRUE);
1721 g_object_set (
1722 tg_tool->preview,
1723 "transform", &tr_tool->transform,
1724 "clip", gimp_item_get_clip (GIMP_ITEM (tool->drawable),
1725 tr_options->clip),
1726 "opacity", tg_options->preview_opacity,
1727 NULL);
1728 gimp_canvas_item_end_change (tg_tool->preview);
1729 }
1730 else
1731 {
1732 gimp_canvas_item_set_visible (tg_tool->preview, FALSE);
1733 }
1734 }
1735
1736 if (tg_tool->boundary_in)
1737 {
1738 gimp_canvas_item_begin_change (tg_tool->boundary_in);
1739 gimp_canvas_item_set_visible (tg_tool->boundary_in,
1740 tr_tool->transform_valid);
1741 g_object_set (tg_tool->boundary_in,
1742 "transform", &tr_tool->transform,
1743 NULL);
1744 gimp_canvas_item_end_change (tg_tool->boundary_in);
1745 }
1746
1747 if (tg_tool->boundary_out)
1748 {
1749 gimp_canvas_item_begin_change (tg_tool->boundary_out);
1750 gimp_canvas_item_set_visible (tg_tool->boundary_out,
1751 tr_tool->transform_valid);
1752 g_object_set (tg_tool->boundary_out,
1753 "transform", &tr_tool->transform,
1754 NULL);
1755 gimp_canvas_item_end_change (tg_tool->boundary_out);
1756 }
1757
1758 for (i = 0; i < tg_tool->strokes->len; i++)
1759 {
1760 GimpCanvasItem *item = g_ptr_array_index (tg_tool->strokes, i);
1761
1762 gimp_canvas_item_begin_change (item);
1763 gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
1764 g_object_set (item,
1765 "transform", &tr_tool->transform,
1766 NULL);
1767 gimp_canvas_item_end_change (item);
1768 }
1769 }
1770
1771 static void
gimp_transform_grid_tool_update_filters(GimpTransformGridTool * tg_tool)1772 gimp_transform_grid_tool_update_filters (GimpTransformGridTool *tg_tool)
1773 {
1774 GimpTool *tool = GIMP_TOOL (tg_tool);
1775 GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1776 GHashTable *new_drawables;
1777 GList *drawables;
1778 GList *iter;
1779 GimpDrawable *drawable;
1780 GHashTableIter hash_iter;
1781
1782 if (! tg_tool->filters)
1783 return;
1784
1785 if (options->preview_linked &&
1786 gimp_item_get_linked (GIMP_ITEM (tool->drawable)))
1787 {
1788 GimpImage *image = gimp_display_get_image (tool->display);
1789
1790 drawables = gimp_image_item_list_get_list (image,
1791 GIMP_ITEM_TYPE_LAYERS |
1792 GIMP_ITEM_TYPE_CHANNELS,
1793 GIMP_ITEM_SET_LINKED);
1794
1795 drawables = gimp_image_item_list_filter (drawables);
1796 }
1797 else
1798 {
1799 drawables = g_list_prepend (NULL, tool->drawable);
1800 }
1801
1802 new_drawables = g_hash_table_new (g_direct_hash, g_direct_equal);
1803
1804 for (iter = drawables; iter; iter = g_list_next (iter))
1805 g_hash_table_add (new_drawables, iter->data);
1806
1807 for (iter = tg_tool->preview_drawables; iter; iter = g_list_next (iter))
1808 {
1809 drawable = iter->data;
1810
1811 if (! g_hash_table_remove (new_drawables, drawable))
1812 gimp_transform_grid_tool_remove_filter (drawable, tg_tool);
1813 }
1814
1815 g_hash_table_iter_init (&hash_iter, new_drawables);
1816
1817 while (g_hash_table_iter_next (&hash_iter, (gpointer *) &drawable, NULL))
1818 {
1819 AddFilterData data;
1820
1821 data.tg_tool = tg_tool;
1822 data.root_drawable = drawable;
1823
1824 gimp_transform_grid_tool_add_filter (drawable, &data);
1825 }
1826
1827 g_hash_table_unref (new_drawables);
1828
1829 g_list_free (tg_tool->preview_drawables);
1830 tg_tool->preview_drawables = drawables;
1831 }
1832
1833 static void
gimp_transform_grid_tool_hide_active_object(GimpTransformGridTool * tg_tool,GimpObject * object)1834 gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
1835 GimpObject *object)
1836 {
1837 GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
1838 GimpTransformOptions *tr_options = GIMP_TRANSFORM_OPTIONS (options);
1839 GimpDisplay *display = GIMP_TOOL (tg_tool)->display;
1840 GimpImage *image = gimp_display_get_image (display);
1841
1842 if (options->show_preview)
1843 {
1844 /* hide only complete layers and channels, not layer masks */
1845 if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
1846 ! options->composited_preview &&
1847 GIMP_IS_DRAWABLE (object) &&
1848 ! GIMP_IS_LAYER_MASK (object) &&
1849 gimp_item_get_visible (GIMP_ITEM (object)) &&
1850 gimp_channel_is_empty (gimp_image_get_mask (image)))
1851 {
1852 tg_tool->hidden_object = object;
1853
1854 gimp_item_set_visible (GIMP_ITEM (object), FALSE, FALSE);
1855
1856 gimp_projection_flush (gimp_image_get_projection (image));
1857 }
1858 else if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
1859 {
1860 tg_tool->hidden_object = object;
1861
1862 gimp_display_shell_set_show_image (gimp_display_get_shell (display),
1863 FALSE);
1864 }
1865 }
1866 }
1867
1868 static void
gimp_transform_grid_tool_show_active_object(GimpTransformGridTool * tg_tool)1869 gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool)
1870 {
1871 if (tg_tool->hidden_object)
1872 {
1873 GimpDisplay *display = GIMP_TOOL (tg_tool)->display;
1874 GimpImage *image = gimp_display_get_image (display);
1875
1876 if (GIMP_IS_ITEM (tg_tool->hidden_object))
1877 {
1878 gimp_item_set_visible (GIMP_ITEM (tg_tool->hidden_object), TRUE,
1879 FALSE);
1880 }
1881 else
1882 {
1883 g_return_if_fail (GIMP_IS_IMAGE (tg_tool->hidden_object));
1884
1885 gimp_display_shell_set_show_image (gimp_display_get_shell (display),
1886 TRUE);
1887 }
1888
1889 tg_tool->hidden_object = NULL;
1890
1891 gimp_image_flush (image);
1892 }
1893 }
1894
1895 static void
gimp_transform_grid_tool_add_filter(GimpDrawable * drawable,AddFilterData * data)1896 gimp_transform_grid_tool_add_filter (GimpDrawable *drawable,
1897 AddFilterData *data)
1898 {
1899 Filter *filter;
1900 GimpLayerMode mode = GIMP_LAYER_MODE_NORMAL;
1901
1902 if (GIMP_IS_LAYER (drawable))
1903 {
1904 gimp_layer_get_effective_mode (GIMP_LAYER (drawable),
1905 &mode, NULL, NULL, NULL);
1906 }
1907
1908 if (mode != GIMP_LAYER_MODE_PASS_THROUGH)
1909 {
1910 filter = filter_new (data->tg_tool, drawable, data->root_drawable, TRUE);
1911 }
1912 else
1913 {
1914 GimpContainer *container;
1915
1916 filter = filter_new (data->tg_tool, drawable, data->root_drawable, FALSE);
1917
1918 container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
1919
1920 gimp_container_foreach (container,
1921 (GFunc) gimp_transform_grid_tool_add_filter,
1922 data);
1923 }
1924
1925 g_hash_table_insert (data->tg_tool->filters, drawable, filter);
1926
1927 if (GIMP_IS_LAYER (drawable))
1928 {
1929 GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
1930
1931 if (mask)
1932 gimp_transform_grid_tool_add_filter (GIMP_DRAWABLE (mask), data);
1933 }
1934 }
1935
1936 static void
gimp_transform_grid_tool_remove_filter(GimpDrawable * drawable,GimpTransformGridTool * tg_tool)1937 gimp_transform_grid_tool_remove_filter (GimpDrawable *drawable,
1938 GimpTransformGridTool *tg_tool)
1939 {
1940 Filter *filter = g_hash_table_lookup (tg_tool->filters, drawable);
1941
1942 if (GIMP_IS_LAYER (drawable))
1943 {
1944 GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
1945
1946 if (mask)
1947 gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (mask), tg_tool);
1948 }
1949
1950 if (! filter->filter)
1951 {
1952 GimpContainer *container;
1953
1954 container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
1955
1956 gimp_container_foreach (container,
1957 (GFunc) gimp_transform_grid_tool_remove_filter,
1958 tg_tool);
1959 }
1960
1961 g_hash_table_remove (tg_tool->filters, drawable);
1962 }
1963
1964 static void
gimp_transform_grid_tool_effective_mode_changed(GimpLayer * layer,GimpTransformGridTool * tg_tool)1965 gimp_transform_grid_tool_effective_mode_changed (GimpLayer *layer,
1966 GimpTransformGridTool *tg_tool)
1967 {
1968 Filter *filter = g_hash_table_lookup (tg_tool->filters, layer);
1969 GimpLayerMode mode;
1970 gboolean old_pass_through;
1971 gboolean new_pass_through;
1972
1973 gimp_layer_get_effective_mode (layer, &mode, NULL, NULL, NULL);
1974
1975 old_pass_through = ! filter->filter;
1976 new_pass_through = mode == GIMP_LAYER_MODE_PASS_THROUGH;
1977
1978 if (old_pass_through != new_pass_through)
1979 {
1980 AddFilterData data;
1981
1982 data.tg_tool = tg_tool;
1983 data.root_drawable = filter->root_drawable;
1984
1985 gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (layer), tg_tool);
1986 gimp_transform_grid_tool_add_filter (GIMP_DRAWABLE (layer), &data);
1987
1988 gimp_transform_grid_tool_update_preview (tg_tool);
1989 }
1990 }
1991
1992 static Filter *
filter_new(GimpTransformGridTool * tg_tool,GimpDrawable * drawable,GimpDrawable * root_drawable,gboolean add_filter)1993 filter_new (GimpTransformGridTool *tg_tool,
1994 GimpDrawable *drawable,
1995 GimpDrawable *root_drawable,
1996 gboolean add_filter)
1997 {
1998 Filter *filter = g_slice_new0 (Filter);
1999 GeglNode *node;
2000 GeglNode *input_node;
2001 GeglNode *output_node;
2002
2003 filter->tg_tool = tg_tool;
2004 filter->drawable = drawable;
2005 filter->root_drawable = root_drawable;
2006
2007 if (add_filter)
2008 {
2009 node = gegl_node_new ();
2010
2011 input_node = gegl_node_get_input_proxy (node, "input");
2012 output_node = gegl_node_get_input_proxy (node, "output");
2013
2014 filter->transform_node = gegl_node_new_child (
2015 node,
2016 "operation", "gegl:transform",
2017 "near-z", GIMP_TRANSFORM_NEAR_Z,
2018 "sampler", GEGL_SAMPLER_NEAREST,
2019 NULL);
2020
2021 filter->crop_node = gegl_node_new_child (
2022 node,
2023 "operation", "gegl:crop",
2024 NULL);
2025
2026 gegl_node_link_many (input_node,
2027 filter->transform_node,
2028 filter->crop_node,
2029 output_node,
2030 NULL);
2031
2032 gimp_gegl_node_set_underlying_operation (node, filter->transform_node);
2033
2034 filter->filter = gimp_drawable_filter_new (
2035 drawable,
2036 GIMP_TRANSFORM_TOOL_GET_CLASS (tg_tool)->undo_desc,
2037 node,
2038 gimp_tool_get_icon_name (GIMP_TOOL (tg_tool)));
2039
2040 gimp_drawable_filter_set_clip (filter->filter, FALSE);
2041 gimp_drawable_filter_set_override_constraints (filter->filter, TRUE);
2042
2043 g_signal_connect (
2044 filter->filter, "flush",
2045 G_CALLBACK (gimp_transform_grid_tool_filter_flush),
2046 tg_tool);
2047
2048 g_object_unref (node);
2049 }
2050
2051 if (GIMP_IS_GROUP_LAYER (drawable))
2052 {
2053 g_signal_connect (
2054 drawable, "effective-mode-changed",
2055 G_CALLBACK (gimp_transform_grid_tool_effective_mode_changed),
2056 tg_tool);
2057 }
2058
2059 return filter;
2060 }
2061
2062 static void
filter_free(Filter * filter)2063 filter_free (Filter *filter)
2064 {
2065 if (filter->filter)
2066 {
2067 gimp_drawable_filter_abort (filter->filter);
2068
2069 g_object_unref (filter->filter);
2070 }
2071
2072 if (GIMP_IS_GROUP_LAYER (filter->drawable))
2073 {
2074 g_signal_handlers_disconnect_by_func (
2075 filter->drawable,
2076 gimp_transform_grid_tool_effective_mode_changed,
2077 filter->tg_tool);
2078 }
2079
2080 g_slice_free (Filter, filter);
2081 }
2082
2083 static UndoInfo *
undo_info_new(void)2084 undo_info_new (void)
2085 {
2086 return g_slice_new0 (UndoInfo);
2087 }
2088
2089 static void
undo_info_free(UndoInfo * info)2090 undo_info_free (UndoInfo *info)
2091 {
2092 g_slice_free (UndoInfo, info);
2093 }
2094
2095 static gboolean
trans_info_equal(const TransInfo trans_info1,const TransInfo trans_info2)2096 trans_info_equal (const TransInfo trans_info1,
2097 const TransInfo trans_info2)
2098 {
2099 gint i;
2100
2101 for (i = 0; i < TRANS_INFO_SIZE; i++)
2102 {
2103 if (fabs (trans_info1[i] - trans_info2[i]) > EPSILON)
2104 return FALSE;
2105 }
2106
2107 return TRUE;
2108 }
2109
2110 static gboolean
trans_infos_equal(const TransInfo * trans_infos1,const TransInfo * trans_infos2)2111 trans_infos_equal (const TransInfo *trans_infos1,
2112 const TransInfo *trans_infos2)
2113 {
2114 return trans_info_equal (trans_infos1[GIMP_TRANSFORM_FORWARD],
2115 trans_infos2[GIMP_TRANSFORM_FORWARD]) &&
2116 trans_info_equal (trans_infos1[GIMP_TRANSFORM_BACKWARD],
2117 trans_infos2[GIMP_TRANSFORM_BACKWARD]);
2118 }
2119
2120 gboolean
gimp_transform_grid_tool_info_to_matrix(GimpTransformGridTool * tg_tool,GimpMatrix3 * transform)2121 gimp_transform_grid_tool_info_to_matrix (GimpTransformGridTool *tg_tool,
2122 GimpMatrix3 *transform)
2123 {
2124 g_return_val_if_fail (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool), FALSE);
2125 g_return_val_if_fail (transform != NULL, FALSE);
2126
2127 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->info_to_matrix)
2128 {
2129 return GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->info_to_matrix (
2130 tg_tool, transform);
2131 }
2132
2133 return FALSE;
2134 }
2135
2136 void
gimp_transform_grid_tool_matrix_to_info(GimpTransformGridTool * tg_tool,const GimpMatrix3 * transform)2137 gimp_transform_grid_tool_matrix_to_info (GimpTransformGridTool *tg_tool,
2138 const GimpMatrix3 *transform)
2139 {
2140 g_return_if_fail (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool));
2141 g_return_if_fail (transform != NULL);
2142
2143 if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info)
2144 {
2145 return GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->matrix_to_info (
2146 tg_tool, transform);
2147 }
2148 }
2149
2150 void
gimp_transform_grid_tool_push_internal_undo(GimpTransformGridTool * tg_tool,gboolean compress)2151 gimp_transform_grid_tool_push_internal_undo (GimpTransformGridTool *tg_tool,
2152 gboolean compress)
2153 {
2154 UndoInfo *undo_info;
2155
2156 g_return_if_fail (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool));
2157 g_return_if_fail (tg_tool->undo_list != NULL);
2158
2159 undo_info = tg_tool->undo_list->data;
2160
2161 /* push current state on the undo list and set this state as the
2162 * current state, but avoid doing this if there were no changes
2163 */
2164 if (! trans_infos_equal (undo_info->trans_infos, tg_tool->trans_infos))
2165 {
2166 GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
2167 gint64 time = 0;
2168 gboolean flush = FALSE;
2169
2170 if (tg_tool->undo_list->next == NULL)
2171 flush = TRUE;
2172
2173 if (compress)
2174 time = g_get_monotonic_time ();
2175
2176 if (! compress || time - undo_info->time >= UNDO_COMPRESS_TIME)
2177 {
2178 undo_info = undo_info_new ();
2179
2180 tg_tool->undo_list = g_list_prepend (tg_tool->undo_list, undo_info);
2181 }
2182
2183 undo_info->time = time;
2184 undo_info->direction = tr_options->direction;
2185 memcpy (undo_info->trans_infos, tg_tool->trans_infos,
2186 sizeof (tg_tool->trans_infos));
2187
2188 /* If we undid anything and started interacting, we have to
2189 * discard the redo history
2190 */
2191 if (tg_tool->redo_list)
2192 {
2193 g_list_free_full (tg_tool->redo_list,
2194 (GDestroyNotify) undo_info_free);
2195 tg_tool->redo_list = NULL;
2196
2197 flush = TRUE;
2198 }
2199
2200 gimp_transform_grid_tool_update_sensitivity (tg_tool);
2201
2202 /* update the undo actions / menu items */
2203 if (flush)
2204 {
2205 gimp_image_flush (
2206 gimp_display_get_image (GIMP_TOOL (tg_tool)->display));
2207 }
2208 }
2209 }
2210