1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gdk-pixbuf/gdk-pixbuf.h>
21 #include <gegl.h>
22 
23 #include "core-types.h"
24 
25 #include "gimp.h"
26 #include "gimpcontext.h"
27 #include "gimpguide.h"
28 #include "gimpimage.h"
29 #include "gimpimage-crop.h"
30 #include "gimpimage-guides.h"
31 #include "gimpimage-sample-points.h"
32 #include "gimpimage-undo.h"
33 #include "gimpimage-undo-push.h"
34 #include "gimplayer.h"
35 #include "gimpsamplepoint.h"
36 
37 #include "gimp-intl.h"
38 
39 
40 /*  public functions  */
41 
42 void
gimp_image_crop(GimpImage * image,GimpContext * context,GimpFillType fill_type,gint x,gint y,gint width,gint height,gboolean crop_layers)43 gimp_image_crop (GimpImage    *image,
44                  GimpContext  *context,
45                  GimpFillType  fill_type,
46                  gint          x,
47                  gint          y,
48                  gint          width,
49                  gint          height,
50                  gboolean      crop_layers)
51 {
52   GList *list;
53   gint   previous_width;
54   gint   previous_height;
55 
56   g_return_if_fail (GIMP_IS_IMAGE (image));
57   g_return_if_fail (GIMP_IS_CONTEXT (context));
58 
59   previous_width  = gimp_image_get_width  (image);
60   previous_height = gimp_image_get_height (image);
61 
62   /*  Make sure new width and height are non-zero  */
63   if (width < 1 || height < 1)
64     return;
65 
66   gimp_set_busy (image->gimp);
67 
68   g_object_freeze_notify (G_OBJECT (image));
69 
70   if (crop_layers)
71     gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CROP,
72                                  C_("undo-type", "Crop Image"));
73   else
74     gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_RESIZE,
75                                  C_("undo-type", "Resize Image"));
76 
77   /*  Push the image size to the stack  */
78   gimp_image_undo_push_image_size (image, NULL,
79                                    x, y, width, height);
80 
81   /*  Set the new width and height  */
82   g_object_set (image,
83                 "width",  width,
84                 "height", height,
85                 NULL);
86 
87   /*  Resize all channels  */
88   for (list = gimp_image_get_channel_iter (image);
89        list;
90        list = g_list_next (list))
91     {
92       GimpItem *item = list->data;
93 
94       gimp_item_resize (item, context, GIMP_FILL_TRANSPARENT,
95                         width, height, -x, -y);
96     }
97 
98   /*  Resize all vectors  */
99   for (list = gimp_image_get_vectors_iter (image);
100        list;
101        list = g_list_next (list))
102     {
103       GimpItem *item = list->data;
104 
105       gimp_item_resize (item, context, GIMP_FILL_TRANSPARENT,
106                         width, height, -x, -y);
107     }
108 
109   /*  Don't forget the selection mask!  */
110   gimp_item_resize (GIMP_ITEM (gimp_image_get_mask (image)),
111                     context, GIMP_FILL_TRANSPARENT,
112                     width, height, -x, -y);
113 
114   /*  crop all layers  */
115   list = gimp_image_get_layer_iter (image);
116 
117   while (list)
118     {
119       GimpItem *item = list->data;
120 
121       list = g_list_next (list);
122 
123       gimp_item_translate (item, -x, -y, TRUE);
124 
125       if (crop_layers && ! gimp_item_is_content_locked (item))
126         {
127           gint off_x, off_y;
128           gint lx1, ly1, lx2, ly2;
129 
130           gimp_item_get_offset (item, &off_x, &off_y);
131 
132           lx1 = CLAMP (off_x, 0, gimp_image_get_width  (image));
133           ly1 = CLAMP (off_y, 0, gimp_image_get_height (image));
134           lx2 = CLAMP (gimp_item_get_width  (item) + off_x,
135                        0, gimp_image_get_width (image));
136           ly2 = CLAMP (gimp_item_get_height (item) + off_y,
137                        0, gimp_image_get_height (image));
138 
139           width  = lx2 - lx1;
140           height = ly2 - ly1;
141 
142           if (width > 0 && height > 0)
143             {
144               gimp_item_resize (item, context, fill_type,
145                                 width, height,
146                                 -(lx1 - off_x),
147                                 -(ly1 - off_y));
148             }
149           else
150             {
151               gimp_image_remove_layer (image, GIMP_LAYER (item),
152                                        TRUE, NULL);
153             }
154         }
155     }
156 
157   /*  Reposition or remove guides  */
158   list = gimp_image_get_guides (image);
159 
160   while (list)
161     {
162       GimpGuide *guide        = list->data;
163       gboolean   remove_guide = FALSE;
164       gint       position     = gimp_guide_get_position (guide);
165 
166       list = g_list_next (list);
167 
168       switch (gimp_guide_get_orientation (guide))
169         {
170         case GIMP_ORIENTATION_HORIZONTAL:
171           position -= y;
172           if ((position < 0) || (position > height))
173             remove_guide = TRUE;
174           break;
175 
176         case GIMP_ORIENTATION_VERTICAL:
177           position -= x;
178           if ((position < 0) || (position > width))
179             remove_guide = TRUE;
180           break;
181 
182         default:
183           break;
184         }
185 
186       if (remove_guide)
187         gimp_image_remove_guide (image, guide, TRUE);
188       else if (position != gimp_guide_get_position (guide))
189         gimp_image_move_guide (image, guide, position, TRUE);
190     }
191 
192   /*  Reposition or remove sample points  */
193   list = gimp_image_get_sample_points (image);
194 
195   while (list)
196     {
197       GimpSamplePoint *sample_point        = list->data;
198       gboolean         remove_sample_point = FALSE;
199       gint             old_x;
200       gint             old_y;
201       gint             new_x;
202       gint             new_y;
203 
204       list = g_list_next (list);
205 
206       gimp_sample_point_get_position (sample_point, &old_x, &old_y);
207       new_x = old_x;
208       new_y = old_y;
209 
210       new_y -= y;
211       if ((new_y < 0) || (new_y > height))
212        remove_sample_point = TRUE;
213 
214       new_x -= x;
215       if ((new_x < 0) || (new_x > width))
216         remove_sample_point = TRUE;
217 
218       if (remove_sample_point)
219         gimp_image_remove_sample_point (image, sample_point, TRUE);
220       else if (new_x != old_x || new_y != old_y)
221         gimp_image_move_sample_point (image, sample_point,
222                                       new_x, new_y, TRUE);
223     }
224 
225   gimp_image_undo_group_end (image);
226 
227   gimp_image_size_changed_detailed (image,
228                                     -x, -y,
229                                     previous_width, previous_height);
230 
231   g_object_thaw_notify (G_OBJECT (image));
232 
233   gimp_unset_busy (image->gimp);
234 }
235