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