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 
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25 
26 #include "tools-types.h"
27 
28 #include "config/gimpguiconfig.h"
29 
30 #include "core/gimp.h"
31 #include "core/gimpdrawable-transform.h"
32 #include "core/gimperror.h"
33 #include "core/gimpimage.h"
34 #include "core/gimpimage-item-list.h"
35 #include "core/gimpimage-transform.h"
36 #include "core/gimpimage-undo.h"
37 #include "core/gimpitem-linked.h"
38 #include "core/gimplayer.h"
39 #include "core/gimplayermask.h"
40 #include "core/gimpprogress.h"
41 #include "core/gimp-transform-resize.h"
42 
43 #include "vectors/gimpvectors.h"
44 
45 #include "display/gimpdisplay.h"
46 #include "display/gimpdisplayshell.h"
47 
48 #include "widgets/gimpmessagedialog.h"
49 #include "widgets/gimpmessagebox.h"
50 #include "widgets/gimpwidgets-utils.h"
51 
52 #include "gimptoolcontrol.h"
53 #include "gimptools-utils.h"
54 #include "gimptransformoptions.h"
55 #include "gimptransformtool.h"
56 
57 #include "gimp-intl.h"
58 
59 
60 /* the minimal ratio between the transformed item size and the image size,
61  * above which confirmation is required.
62  */
63 #define MIN_CONFIRMATION_RATIO 10
64 
65 
66 /*  local function prototypes  */
67 
68 static void                     gimp_transform_tool_control            (GimpTool           *tool,
69                                                                         GimpToolAction      action,
70                                                                         GimpDisplay        *display);
71 
72 static gchar                  * gimp_transform_tool_real_get_undo_desc (GimpTransformTool  *tr_tool);
73 static GimpTransformDirection   gimp_transform_tool_real_get_direction (GimpTransformTool  *tr_tool);
74 static GeglBuffer             * gimp_transform_tool_real_transform     (GimpTransformTool  *tr_tool,
75                                                                         GimpObject         *object,
76                                                                         GeglBuffer         *orig_buffer,
77                                                                         gint                orig_offset_x,
78                                                                         gint                orig_offset_y,
79                                                                         GimpColorProfile  **buffer_profile,
80                                                                         gint               *new_offset_x,
81                                                                         gint               *new_offset_y);
82 
83 static void                     gimp_transform_tool_halt               (GimpTransformTool  *tr_tool);
84 
85 static gboolean                 gimp_transform_tool_confirm            (GimpTransformTool  *tr_tool,
86                                                                         GimpDisplay        *display);
87 
88 
G_DEFINE_TYPE(GimpTransformTool,gimp_transform_tool,GIMP_TYPE_DRAW_TOOL)89 G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
90 
91 #define parent_class gimp_transform_tool_parent_class
92 
93 
94 /*  private functions  */
95 
96 
97 static void
98 gimp_transform_tool_class_init (GimpTransformToolClass *klass)
99 {
100   GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
101 
102   tool_class->control  = gimp_transform_tool_control;
103 
104   klass->recalc_matrix = NULL;
105   klass->get_undo_desc = gimp_transform_tool_real_get_undo_desc;
106   klass->get_direction = gimp_transform_tool_real_get_direction;
107   klass->transform     = gimp_transform_tool_real_transform;
108 
109   klass->undo_desc     = _("Transform");
110   klass->progress_text = _("Transforming");
111 }
112 
113 static void
gimp_transform_tool_init(GimpTransformTool * tr_tool)114 gimp_transform_tool_init (GimpTransformTool *tr_tool)
115 {
116   gimp_matrix3_identity (&tr_tool->transform);
117   tr_tool->transform_valid = TRUE;
118 
119   tr_tool->restore_type = FALSE;
120 }
121 
122 static void
gimp_transform_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)123 gimp_transform_tool_control (GimpTool       *tool,
124                              GimpToolAction  action,
125                              GimpDisplay    *display)
126 {
127   GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
128 
129   switch (action)
130     {
131     case GIMP_TOOL_ACTION_PAUSE:
132     case GIMP_TOOL_ACTION_RESUME:
133       break;
134 
135     case GIMP_TOOL_ACTION_HALT:
136       gimp_transform_tool_halt (tr_tool);
137       break;
138 
139     case GIMP_TOOL_ACTION_COMMIT:
140       break;
141     }
142 
143   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
144 }
145 
146 static gchar *
gimp_transform_tool_real_get_undo_desc(GimpTransformTool * tr_tool)147 gimp_transform_tool_real_get_undo_desc (GimpTransformTool *tr_tool)
148 {
149   return g_strdup (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->undo_desc);
150 }
151 
152 static GimpTransformDirection
gimp_transform_tool_real_get_direction(GimpTransformTool * tr_tool)153 gimp_transform_tool_real_get_direction (GimpTransformTool *tr_tool)
154 {
155   GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
156 
157   return options->direction;
158 }
159 
160 static GeglBuffer *
gimp_transform_tool_real_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)161 gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
162                                     GimpObject        *object,
163                                     GeglBuffer        *orig_buffer,
164                                     gint               orig_offset_x,
165                                     gint               orig_offset_y,
166                                     GimpColorProfile **buffer_profile,
167                                     gint              *new_offset_x,
168                                     gint              *new_offset_y)
169 {
170   GimpTransformToolClass *klass   = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);
171   GimpTool               *tool    = GIMP_TOOL (tr_tool);
172   GimpTransformOptions   *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
173   GimpContext            *context = GIMP_CONTEXT (options);
174   GeglBuffer             *ret     = NULL;
175   GimpTransformResize     clip    = options->clip;
176   GimpTransformDirection  direction;
177   GimpProgress           *progress;
178 
179   direction = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_direction (tr_tool);
180 
181   progress = gimp_progress_start (GIMP_PROGRESS (tool), FALSE,
182                                   "%s", klass->progress_text);
183 
184   if (orig_buffer)
185     {
186       /*  this happens when transforming a selection cut out of a
187        *  normal drawable
188        */
189 
190       g_return_val_if_fail (GIMP_IS_DRAWABLE (object), NULL);
191 
192       ret = gimp_drawable_transform_buffer_affine (GIMP_DRAWABLE (object),
193                                                    context,
194                                                    orig_buffer,
195                                                    orig_offset_x,
196                                                    orig_offset_y,
197                                                    &tr_tool->transform,
198                                                    direction,
199                                                    options->interpolation,
200                                                    clip,
201                                                    buffer_profile,
202                                                    new_offset_x,
203                                                    new_offset_y,
204                                                    progress);
205     }
206   else if (GIMP_IS_ITEM (object))
207     {
208       /*  this happens for entire drawables, paths and layer groups  */
209 
210       GimpItem *item = GIMP_ITEM (object);
211 
212       if (gimp_item_get_linked (item))
213         {
214           gimp_item_linked_transform (item, context,
215                                       &tr_tool->transform,
216                                       direction,
217                                       options->interpolation,
218                                       clip,
219                                       progress);
220         }
221       else
222         {
223           clip = gimp_item_get_clip (item, clip);
224 
225           gimp_item_transform (item, context,
226                                &tr_tool->transform,
227                                direction,
228                                options->interpolation,
229                                clip,
230                                progress);
231         }
232     }
233   else
234     {
235       /*  this happens for images  */
236 
237       g_return_val_if_fail (GIMP_IS_IMAGE (object), NULL);
238 
239       gimp_image_transform (GIMP_IMAGE (object), context,
240                             &tr_tool->transform,
241                             direction,
242                             options->interpolation,
243                             clip,
244                             progress);
245     }
246 
247   if (progress)
248     gimp_progress_end (progress);
249 
250   return ret;
251 }
252 
253 static void
gimp_transform_tool_halt(GimpTransformTool * tr_tool)254 gimp_transform_tool_halt (GimpTransformTool *tr_tool)
255 {
256   GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
257 
258   tr_tool->x1 = 0;
259   tr_tool->y1 = 0;
260   tr_tool->x2 = 0;
261   tr_tool->y2 = 0;
262 
263   if (tr_tool->restore_type)
264     {
265       g_object_set (options,
266                     "type", tr_tool->saved_type,
267                     NULL);
268 
269       tr_tool->restore_type = FALSE;
270     }
271 }
272 
273 static gboolean
gimp_transform_tool_confirm(GimpTransformTool * tr_tool,GimpDisplay * display)274 gimp_transform_tool_confirm (GimpTransformTool *tr_tool,
275                              GimpDisplay       *display)
276 {
277   GimpTransformOptions *options          = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
278   GimpDisplayShell     *shell            = gimp_display_get_shell (display);
279   GimpImage            *image            = gimp_display_get_image (display);
280   GimpObject           *active_object;
281   gdouble               max_ratio        = 0.0;
282   GimpObject           *max_ratio_object = NULL;
283 
284   active_object = gimp_transform_tool_get_active_object (tr_tool, display);
285 
286   if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
287     {
288       GimpMatrix3             transform;
289       GimpTransformDirection  direction;
290       GeglRectangle           selection_bounds;
291       gboolean                selection_empty = TRUE;
292       GList                  *objects;
293       GList                  *iter;
294 
295       transform = tr_tool->transform;
296       direction = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_direction (
297         tr_tool);
298 
299       if (direction == GIMP_TRANSFORM_BACKWARD)
300         gimp_matrix3_invert (&transform);
301 
302       if (options->type == GIMP_TRANSFORM_TYPE_LAYER &&
303           ! gimp_viewable_get_children (GIMP_VIEWABLE (active_object)))
304         {
305           selection_empty = ! gimp_item_bounds (
306             GIMP_ITEM (gimp_image_get_mask (image)),
307             &selection_bounds.x,     &selection_bounds.y,
308             &selection_bounds.width, &selection_bounds.height);
309         }
310 
311       if (selection_empty              &&
312           GIMP_IS_ITEM (active_object) &&
313           gimp_item_get_linked (GIMP_ITEM (active_object)))
314         {
315           objects = gimp_image_item_list_get_list (image,
316                                                    GIMP_ITEM_TYPE_ALL,
317                                                    GIMP_ITEM_SET_LINKED);
318         }
319       else
320         {
321           objects = g_list_append (NULL, active_object);
322         }
323 
324       if (options->type == GIMP_TRANSFORM_TYPE_IMAGE)
325         {
326           objects = g_list_concat (
327             objects,
328             gimp_image_item_list_get_list (image,
329                                            GIMP_ITEM_TYPE_ALL,
330                                            GIMP_ITEM_SET_ALL));
331         }
332 
333       for (iter = objects; iter; iter = g_list_next (iter))
334         {
335           GimpObject          *object = iter->data;
336           GimpTransformResize  clip   = options->clip;
337           GeglRectangle        orig_bounds;
338           GeglRectangle        new_bounds;
339           gdouble              ratio  = 0.0;
340 
341           if (GIMP_IS_DRAWABLE (object))
342             {
343               if (selection_empty)
344                 {
345                   GimpItem *item = GIMP_ITEM (object);
346 
347                   gimp_item_get_offset (item, &orig_bounds.x, &orig_bounds.y);
348 
349                   orig_bounds.width  = gimp_item_get_width  (item);
350                   orig_bounds.height = gimp_item_get_height (item);
351 
352                   clip = gimp_item_get_clip (item, clip);
353                 }
354               else
355                 {
356                   orig_bounds = selection_bounds;
357                 }
358             }
359           else if (GIMP_IS_ITEM (object))
360             {
361               GimpItem *item = GIMP_ITEM (object);
362 
363               gimp_item_bounds (item,
364                                 &orig_bounds.x,     &orig_bounds.y,
365                                 &orig_bounds.width, &orig_bounds.height);
366 
367               clip = gimp_item_get_clip (item, clip);
368             }
369           else
370             {
371               GimpImage *image;
372 
373               g_return_val_if_fail (GIMP_IS_IMAGE (object), FALSE);
374 
375               image = GIMP_IMAGE (object);
376 
377               orig_bounds.x      = 0;
378               orig_bounds.y      = 0;
379               orig_bounds.width  = gimp_image_get_width  (image);
380               orig_bounds.height = gimp_image_get_height (image);
381             }
382 
383           gimp_transform_resize_boundary (&transform, clip,
384 
385                                           orig_bounds.x,
386                                           orig_bounds.y,
387                                           orig_bounds.x + orig_bounds.width,
388                                           orig_bounds.y + orig_bounds.height,
389 
390                                           &new_bounds.x,
391                                           &new_bounds.y,
392                                           &new_bounds.width,
393                                           &new_bounds.height);
394 
395           new_bounds.width  -= new_bounds.x;
396           new_bounds.height -= new_bounds.y;
397 
398           if (new_bounds.width > orig_bounds.width)
399             {
400               ratio = MAX (ratio,
401                            (gdouble) new_bounds.width /
402                            (gdouble) gimp_image_get_width (image));
403             }
404 
405           if (new_bounds.height > orig_bounds.height)
406             {
407               ratio = MAX (ratio,
408                            (gdouble) new_bounds.height /
409                            (gdouble) gimp_image_get_height (image));
410             }
411 
412           if (ratio > max_ratio)
413             {
414               max_ratio        = ratio;
415               max_ratio_object = object;
416             }
417         }
418 
419       g_list_free (objects);
420     }
421 
422   if (max_ratio > MIN_CONFIRMATION_RATIO)
423     {
424       GtkWidget *dialog;
425       gint       response;
426 
427       dialog = gimp_message_dialog_new (_("Confirm Transformation"),
428                                         GIMP_ICON_DIALOG_WARNING,
429                                         GTK_WIDGET (shell),
430                                         GTK_DIALOG_MODAL |
431                                         GTK_DIALOG_DESTROY_WITH_PARENT,
432                                         gimp_standard_help_func, NULL,
433 
434                                         _("_Cancel"),    GTK_RESPONSE_CANCEL,
435                                         _("_Transform"), GTK_RESPONSE_OK,
436 
437                                         NULL);
438 
439       gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
440                                                GTK_RESPONSE_OK,
441                                                GTK_RESPONSE_CANCEL,
442                                                -1);
443 
444       if (GIMP_IS_ITEM (max_ratio_object))
445         {
446           gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
447                                              _("Transformation creates "
448                                                "a very large item."));
449 
450           gimp_message_box_set_text (
451             GIMP_MESSAGE_DIALOG (dialog)->box,
452             _("Applying the transformation will result "
453               "in an item that is over %g times larger "
454               "than the image."),
455               floor (max_ratio));
456         }
457       else
458         {
459           gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
460                                              _("Transformation creates "
461                                                "a very large image."));
462 
463           gimp_message_box_set_text (
464             GIMP_MESSAGE_DIALOG (dialog)->box,
465             _("Applying the transformation will enlarge "
466               "the image by a factor of %g."),
467               floor (max_ratio));
468         }
469 
470       response = gtk_dialog_run (GTK_DIALOG (dialog));
471 
472       gtk_widget_destroy (dialog);
473 
474       if (response != GTK_RESPONSE_OK)
475         return FALSE;
476     }
477 
478   return TRUE;
479 }
480 
481 
482 /*  public functions  */
483 
484 
485 gboolean
gimp_transform_tool_bounds(GimpTransformTool * tr_tool,GimpDisplay * display)486 gimp_transform_tool_bounds (GimpTransformTool *tr_tool,
487                             GimpDisplay       *display)
488 {
489   GimpTransformOptions *options;
490   GimpDisplayShell     *shell;
491   GimpImage            *image;
492   gboolean              non_empty = TRUE;
493 
494   g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), FALSE);
495 
496   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
497   image   = gimp_display_get_image (display);
498   shell   = gimp_display_get_shell (display);
499 
500   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
501 
502   switch (options->type)
503     {
504     case GIMP_TRANSFORM_TYPE_LAYER:
505       {
506         GimpDrawable *drawable;
507         gint          offset_x;
508         gint          offset_y;
509         gint          x, y;
510         gint          width, height;
511 
512         drawable = gimp_image_get_active_drawable (image);
513 
514         gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
515 
516         non_empty = gimp_item_mask_intersect (GIMP_ITEM (drawable),
517                                               &x, &y, &width, &height);
518         tr_tool->x1 = x + offset_x;
519         tr_tool->y1 = y + offset_y;
520         tr_tool->x2 = x + width  + offset_x;
521         tr_tool->y2 = y + height + offset_y;
522       }
523       break;
524 
525     case GIMP_TRANSFORM_TYPE_SELECTION:
526       {
527         gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
528                           &tr_tool->x1, &tr_tool->y1,
529                           &tr_tool->x2, &tr_tool->y2);
530         tr_tool->x2 += tr_tool->x1;
531         tr_tool->y2 += tr_tool->y1;
532       }
533       break;
534 
535     case GIMP_TRANSFORM_TYPE_PATH:
536       {
537         GimpChannel *selection = gimp_image_get_mask (image);
538 
539         /* if selection is not empty, use its bounds to perform the
540          * transformation of the path
541          */
542 
543         if (! gimp_channel_is_empty (selection))
544           {
545             gimp_item_bounds (GIMP_ITEM (selection),
546                               &tr_tool->x1, &tr_tool->y1,
547                               &tr_tool->x2, &tr_tool->y2);
548           }
549         else
550           {
551             /* without selection, test the emptiness of the path bounds :
552              * if empty, use the canvas bounds
553              * else use the path bounds
554              */
555 
556             if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_active_vectors (image)),
557                                     &tr_tool->x1, &tr_tool->y1,
558                                     &tr_tool->x2, &tr_tool->y2))
559               {
560                 tr_tool->x1 = 0;
561                 tr_tool->y1 = 0;
562                 tr_tool->x2 = gimp_image_get_width (image);
563                 tr_tool->y2 = gimp_image_get_height (image);
564               }
565           }
566 
567         tr_tool->x2 += tr_tool->x1;
568         tr_tool->y2 += tr_tool->y1;
569       }
570 
571       break;
572 
573     case GIMP_TRANSFORM_TYPE_IMAGE:
574       if (! shell->show_all)
575         {
576           tr_tool->x1 = 0;
577           tr_tool->y1 = 0;
578           tr_tool->x2 = gimp_image_get_width  (image);
579           tr_tool->y2 = gimp_image_get_height (image);
580         }
581       else
582         {
583           GeglRectangle bounding_box;
584 
585           bounding_box = gimp_display_shell_get_bounding_box (shell);
586 
587           tr_tool->x1 = bounding_box.x;
588           tr_tool->y1 = bounding_box.y;
589           tr_tool->x2 = bounding_box.x + bounding_box.width;
590           tr_tool->y2 = bounding_box.y + bounding_box.height;
591         }
592       break;
593     }
594 
595   return non_empty;
596 }
597 
598 void
gimp_transform_tool_recalc_matrix(GimpTransformTool * tr_tool,GimpDisplay * display)599 gimp_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
600                                    GimpDisplay       *display)
601 {
602   g_return_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool));
603   g_return_if_fail (GIMP_IS_DISPLAY (display));
604 
605   if (tr_tool->x1 == tr_tool->x2 && tr_tool->y1 == tr_tool->y2)
606     gimp_transform_tool_bounds (tr_tool, display);
607 
608   if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
609     GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix (tr_tool);
610 }
611 
612 GimpObject *
gimp_transform_tool_get_active_object(GimpTransformTool * tr_tool,GimpDisplay * display)613 gimp_transform_tool_get_active_object (GimpTransformTool  *tr_tool,
614                                        GimpDisplay        *display)
615 {
616   GimpTransformOptions *options;
617   GimpImage            *image;
618   GimpObject           *object = NULL;
619 
620   g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), NULL);
621   g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
622 
623   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
624 
625   image = gimp_display_get_image (display);
626 
627   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
628 
629   if (tr_tool->object)
630     return tr_tool->object;
631 
632   switch (options->type)
633     {
634     case GIMP_TRANSFORM_TYPE_LAYER:
635       object = GIMP_OBJECT (gimp_image_get_active_drawable (image));
636       break;
637 
638     case GIMP_TRANSFORM_TYPE_SELECTION:
639       object = GIMP_OBJECT (gimp_image_get_mask (image));
640 
641       if (gimp_channel_is_empty (GIMP_CHANNEL (object)))
642         object = NULL;
643       break;
644 
645     case GIMP_TRANSFORM_TYPE_PATH:
646       object = GIMP_OBJECT (gimp_image_get_active_vectors (image));
647       break;
648 
649     case GIMP_TRANSFORM_TYPE_IMAGE:
650       object = GIMP_OBJECT (image);
651       break;
652     }
653 
654   return object;
655 }
656 
657 GimpObject *
gimp_transform_tool_check_active_object(GimpTransformTool * tr_tool,GimpDisplay * display,GError ** error)658 gimp_transform_tool_check_active_object (GimpTransformTool  *tr_tool,
659                                          GimpDisplay        *display,
660                                          GError            **error)
661 {
662   GimpTransformOptions *options;
663   GimpObject           *object;
664   const gchar          *null_message   = NULL;
665   const gchar          *locked_message = NULL;
666   GimpGuiConfig        *config         = GIMP_GUI_CONFIG (display->gimp->config);
667 
668   g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), NULL);
669   g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
670   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
671 
672   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
673 
674   object = gimp_transform_tool_get_active_object (tr_tool, display);
675 
676   switch (options->type)
677     {
678     case GIMP_TRANSFORM_TYPE_LAYER:
679       null_message = _("There is no layer to transform.");
680 
681       if (object)
682         {
683           GimpItem *item = GIMP_ITEM (object);
684 
685           if (gimp_item_is_content_locked (item))
686             locked_message = _("The active layer's pixels are locked.");
687           else if (gimp_item_is_position_locked (item))
688             locked_message = _("The active layer's position and size are locked.");
689 
690           if (! gimp_item_is_visible (item) &&
691               ! config->edit_non_visible &&
692               object != tr_tool->object) /* see bug #759194 */
693             {
694               g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
695                                    _("The active layer is not visible."));
696               return NULL;
697             }
698 
699           if (! gimp_transform_tool_bounds (tr_tool, display))
700             {
701               g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
702                                    _("The selection does not intersect with the layer."));
703               return NULL;
704             }
705         }
706       break;
707 
708     case GIMP_TRANSFORM_TYPE_SELECTION:
709       null_message = _("There is no selection to transform.");
710 
711       if (object)
712         {
713           GimpItem *item = GIMP_ITEM (object);
714 
715           /* cannot happen, so don't translate these messages */
716           if (gimp_item_is_content_locked (item))
717             locked_message = "The selection's pixels are locked.";
718           else if (gimp_item_is_position_locked (item))
719             locked_message = "The selection's position and size are locked.";
720         }
721       break;
722 
723     case GIMP_TRANSFORM_TYPE_PATH:
724       null_message = _("There is no path to transform.");
725 
726       if (object)
727         {
728           GimpItem *item = GIMP_ITEM (object);
729 
730           if (gimp_item_is_content_locked (item))
731             locked_message = _("The active path's strokes are locked.");
732           else if (gimp_item_is_position_locked (item))
733             locked_message = _("The active path's position is locked.");
734           else if (! gimp_vectors_get_n_strokes (GIMP_VECTORS (item)))
735             locked_message = _("The active path has no strokes.");
736         }
737       break;
738 
739     case GIMP_TRANSFORM_TYPE_IMAGE:
740       /* cannot happen, so don't translate this message */
741       null_message = "There is no image to transform.";
742       break;
743     }
744 
745   if (! object)
746     {
747       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, null_message);
748       if (error)
749         gimp_widget_blink (options->type_box);
750       return NULL;
751     }
752 
753   if (locked_message)
754     {
755       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, locked_message);
756       if (error)
757         gimp_tools_blink_lock_box (display->gimp, GIMP_ITEM (object));
758       return NULL;
759     }
760 
761   return object;
762 }
763 
764 gboolean
gimp_transform_tool_transform(GimpTransformTool * tr_tool,GimpDisplay * display)765 gimp_transform_tool_transform (GimpTransformTool *tr_tool,
766                                GimpDisplay       *display)
767 {
768   GimpTool             *tool;
769   GimpTransformOptions *options;
770   GimpContext          *context;
771   GimpImage            *image;
772   GimpObject           *active_object;
773   GeglBuffer           *orig_buffer   = NULL;
774   gint                  orig_offset_x = 0;
775   gint                  orig_offset_y = 0;
776   GeglBuffer           *new_buffer;
777   gint                  new_offset_x;
778   gint                  new_offset_y;
779   GimpColorProfile     *buffer_profile;
780   gchar                *undo_desc     = NULL;
781   gboolean              new_layer     = FALSE;
782   GError               *error         = NULL;
783 
784   g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), FALSE);
785   g_return_val_if_fail (GIMP_IS_DISPLAY (display), FALSE);
786 
787   tool    = GIMP_TOOL (tr_tool);
788   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
789   context = GIMP_CONTEXT (options);
790   image   = gimp_display_get_image (display);
791 
792   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
793 
794   active_object = gimp_transform_tool_check_active_object (tr_tool, display,
795                                                            &error);
796 
797   if (! active_object)
798     {
799       gimp_tool_message_literal (tool, display, error->message);
800       g_clear_error (&error);
801       return FALSE;
802     }
803 
804   gimp_transform_tool_recalc_matrix (tr_tool, display);
805 
806   if (! tr_tool->transform_valid)
807     {
808       gimp_tool_message_literal (tool, display,
809                                  _("The current transform is invalid"));
810       return FALSE;
811     }
812 
813   if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix &&
814       gimp_matrix3_is_identity (&tr_tool->transform))
815     {
816       /* No need to commit an identity transformation! */
817       return TRUE;
818     }
819 
820   if (! gimp_transform_tool_confirm (tr_tool, display))
821     return FALSE;
822 
823   gimp_set_busy (display->gimp);
824 
825   /*  We're going to dirty this image, but we want to keep the tool around  */
826   gimp_tool_control_push_preserve (tool->control, TRUE);
827 
828   undo_desc = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_undo_desc (tr_tool);
829   gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, undo_desc);
830   g_free (undo_desc);
831 
832   switch (options->type)
833     {
834     case GIMP_TRANSFORM_TYPE_LAYER:
835       if (! gimp_viewable_get_children (GIMP_VIEWABLE (active_object)) &&
836           ! gimp_channel_is_empty (gimp_image_get_mask (image)))
837         {
838           orig_buffer = gimp_drawable_transform_cut (
839             GIMP_DRAWABLE (active_object),
840             context,
841             &orig_offset_x,
842             &orig_offset_y,
843             &new_layer);
844         }
845       break;
846 
847     case GIMP_TRANSFORM_TYPE_SELECTION:
848     case GIMP_TRANSFORM_TYPE_PATH:
849     case GIMP_TRANSFORM_TYPE_IMAGE:
850       break;
851     }
852 
853   /*  Send the request for the transformation to the tool...
854    */
855   new_buffer = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->transform (
856     tr_tool,
857     active_object,
858     orig_buffer,
859     orig_offset_x,
860     orig_offset_y,
861     &buffer_profile,
862     &new_offset_x,
863     &new_offset_y);
864 
865   if (orig_buffer)
866     g_object_unref (orig_buffer);
867 
868   switch (options->type)
869     {
870     case GIMP_TRANSFORM_TYPE_LAYER:
871       if (new_buffer)
872         {
873           /*  paste the new transformed image to the image...also implement
874            *  undo...
875            */
876           gimp_drawable_transform_paste (GIMP_DRAWABLE (active_object),
877                                          new_buffer, buffer_profile,
878                                          new_offset_x, new_offset_y,
879                                          new_layer);
880           g_object_unref (new_buffer);
881         }
882       break;
883 
884     case GIMP_TRANSFORM_TYPE_SELECTION:
885     case GIMP_TRANSFORM_TYPE_PATH:
886     case GIMP_TRANSFORM_TYPE_IMAGE:
887       /*  Nothing to be done  */
888       break;
889     }
890 
891   gimp_image_undo_group_end (image);
892 
893   /*  We're done dirtying the image, and would like to be restarted if
894    *  the image gets dirty while the tool exists
895    */
896   gimp_tool_control_pop_preserve (tool->control);
897 
898   gimp_unset_busy (display->gimp);
899 
900   gimp_image_flush (image);
901 
902   return TRUE;
903 }
904 
905 void
gimp_transform_tool_set_type(GimpTransformTool * tr_tool,GimpTransformType type)906 gimp_transform_tool_set_type (GimpTransformTool *tr_tool,
907                               GimpTransformType  type)
908 {
909   GimpTransformOptions *options;
910 
911   g_return_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool));
912 
913   options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
914 
915   if (! tr_tool->restore_type)
916     tr_tool->saved_type = options->type;
917 
918   tr_tool->restore_type = FALSE;
919 
920   g_object_set (options,
921                 "type", type,
922                 NULL);
923 
924   tr_tool->restore_type = TRUE;
925 }
926