1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpdrawable-filters.c
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 #include <gegl.h>
24 #include <cairo.h>
25 
26 #include "core-types.h"
27 
28 #include "gegl/gimpapplicator.h"
29 #include "gegl/gimp-gegl-apply-operation.h"
30 #include "gegl/gimp-gegl-loops.h"
31 
32 #include "gimp.h"
33 #include "gimp-utils.h"
34 #include "gimpdrawable.h"
35 #include "gimpdrawable-filters.h"
36 #include "gimpdrawable-private.h"
37 #include "gimpfilter.h"
38 #include "gimpfilterstack.h"
39 #include "gimpimage.h"
40 #include "gimpimage-undo.h"
41 #include "gimplayer.h"
42 #include "gimpprogress.h"
43 #include "gimpprojection.h"
44 
45 
46 GimpContainer *
gimp_drawable_get_filters(GimpDrawable * drawable)47 gimp_drawable_get_filters (GimpDrawable *drawable)
48 {
49   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
50 
51   return drawable->private->filter_stack;
52 }
53 
54 gboolean
gimp_drawable_has_filters(GimpDrawable * drawable)55 gimp_drawable_has_filters (GimpDrawable *drawable)
56 {
57   GList *list;
58 
59   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
60 
61   for (list = GIMP_LIST (drawable->private->filter_stack)->queue->head;
62        list;
63        list = g_list_next (list))
64     {
65       GimpFilter *filter = list->data;
66 
67       if (gimp_filter_get_active (filter))
68         return TRUE;
69     }
70 
71   return FALSE;
72 }
73 
74 void
gimp_drawable_add_filter(GimpDrawable * drawable,GimpFilter * filter)75 gimp_drawable_add_filter (GimpDrawable *drawable,
76                           GimpFilter   *filter)
77 {
78   g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
79   g_return_if_fail (GIMP_IS_FILTER (filter));
80   g_return_if_fail (gimp_drawable_has_filter (drawable, filter) == FALSE);
81 
82   gimp_container_add (drawable->private->filter_stack,
83                       GIMP_OBJECT (filter));
84 }
85 
86 void
gimp_drawable_remove_filter(GimpDrawable * drawable,GimpFilter * filter)87 gimp_drawable_remove_filter (GimpDrawable *drawable,
88                              GimpFilter   *filter)
89 {
90   g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
91   g_return_if_fail (GIMP_IS_FILTER (filter));
92   g_return_if_fail (gimp_drawable_has_filter (drawable, filter) == TRUE);
93 
94   gimp_container_remove (drawable->private->filter_stack,
95                          GIMP_OBJECT (filter));
96 }
97 
98 gboolean
gimp_drawable_has_filter(GimpDrawable * drawable,GimpFilter * filter)99 gimp_drawable_has_filter (GimpDrawable *drawable,
100                           GimpFilter   *filter)
101 {
102   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
103   g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE);
104 
105   return gimp_container_have (drawable->private->filter_stack,
106                               GIMP_OBJECT (filter));
107 }
108 
109 gboolean
gimp_drawable_merge_filter(GimpDrawable * drawable,GimpFilter * filter,GimpProgress * progress,const gchar * undo_desc,const Babl * format,gboolean clip,gboolean cancellable,gboolean update)110 gimp_drawable_merge_filter (GimpDrawable *drawable,
111                             GimpFilter   *filter,
112                             GimpProgress *progress,
113                             const gchar  *undo_desc,
114                             const Babl   *format,
115                             gboolean      clip,
116                             gboolean      cancellable,
117                             gboolean      update)
118 {
119   GimpImage      *image;
120   GimpApplicator *applicator;
121   gboolean        applicator_cache         = FALSE;
122   const Babl     *applicator_output_format = NULL;
123   GeglBuffer     *buffer                   = NULL;
124   GeglBuffer     *dest_buffer;
125   GeglBuffer     *undo_buffer              = NULL;
126   GeglRectangle   undo_rect;
127   GeglBuffer     *cache                    = NULL;
128   GeglRectangle  *rects                    = NULL;
129   gint            n_rects                  = 0;
130   GeglRectangle   rect;
131   gboolean        success                  = TRUE;
132 
133   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
134   g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE);
135   g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
136 
137   image       = gimp_item_get_image (GIMP_ITEM (drawable));
138   applicator  = gimp_filter_get_applicator (filter);
139   dest_buffer = gimp_drawable_get_buffer (drawable);
140 
141   if (! format)
142     format = gimp_drawable_get_format (drawable);
143 
144   rect = gegl_node_get_bounding_box (gimp_filter_get_node (filter));
145 
146   if (! clip && gegl_rectangle_equal (&rect,
147                                       gegl_buffer_get_extent (dest_buffer)))
148     {
149       clip = TRUE;
150     }
151 
152   if (clip)
153     {
154       if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
155                                       &rect.x, &rect.y,
156                                       &rect.width, &rect.height))
157         {
158           return TRUE;
159         }
160 
161       if (format != gimp_drawable_get_format (drawable))
162         {
163           buffer = gegl_buffer_new (gegl_buffer_get_extent (dest_buffer),
164                                     format);
165 
166           dest_buffer = buffer;
167         }
168     }
169   else
170     {
171       buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
172                                 format);
173 
174       dest_buffer = g_object_new (GEGL_TYPE_BUFFER,
175                                   "source",  buffer,
176                                   "shift-x", -rect.x,
177                                   "shift-y", -rect.y,
178                                   NULL);
179     }
180 
181   if (applicator)
182     {
183       const GeglRectangle *crop_rect;
184 
185       crop_rect = gimp_applicator_get_crop (applicator);
186 
187       if (crop_rect && ! gegl_rectangle_intersect (&rect, &rect, crop_rect))
188         return TRUE;
189 
190       /*  the cache and its valid rectangles are the region that
191        *  has already been processed by this applicator.
192        */
193       cache = gimp_applicator_get_cache_buffer (applicator,
194                                                 &rects, &n_rects);
195 
196       /*  skip the cache and output-format conversion while processing
197        *  the remaining area, so that the result is written directly to
198        *  the drawable's buffer.
199        */
200       applicator_cache         = gimp_applicator_get_cache (applicator);
201       applicator_output_format = gimp_applicator_get_output_format (applicator);
202 
203       gimp_applicator_set_cache (applicator, FALSE);
204       if (applicator_output_format == format)
205         gimp_applicator_set_output_format (applicator, NULL);
206     }
207 
208   if (! buffer)
209     {
210       gegl_rectangle_align_to_buffer (
211         &undo_rect,
212         &rect,
213         gimp_drawable_get_buffer (drawable),
214         GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
215 
216       undo_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
217                                                      undo_rect.width,
218                                                      undo_rect.height),
219                                      gimp_drawable_get_format (drawable));
220 
221       gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
222                              &undo_rect,
223                              GEGL_ABYSS_NONE,
224                              undo_buffer,
225                              GEGL_RECTANGLE (0, 0, 0, 0));
226     }
227 
228   gimp_projection_stop_rendering (gimp_image_get_projection (image));
229 
230   /* make sure we have a source node - this connects the filter stack to the
231    * underlying source node
232    */
233   (void) gimp_drawable_get_source_node (drawable);
234 
235   if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable),
236                                         progress, undo_desc,
237                                         gimp_filter_get_node (filter), FALSE,
238                                         dest_buffer, &rect, FALSE,
239                                         cache, rects, n_rects,
240                                         cancellable))
241     {
242       /*  finished successfully  */
243 
244       if (clip)
245         {
246           if (buffer)
247             {
248               gimp_drawable_set_buffer_full (drawable,
249                                              TRUE, undo_desc,
250                                              buffer, NULL,
251                                              FALSE);
252             }
253           else
254             {
255               gimp_drawable_push_undo (drawable, undo_desc, undo_buffer,
256                                        undo_rect.x, undo_rect.y,
257                                        undo_rect.width, undo_rect.height);
258             }
259         }
260       else
261         {
262           GimpLayerMask *mask = NULL;
263           gint           offset_x;
264           gint           offset_y;
265 
266           gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
267 
268           if (GIMP_IS_LAYER (drawable))
269             mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
270 
271           if (mask)
272             {
273               gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD,
274                                            undo_desc);
275             }
276 
277           gimp_drawable_set_buffer_full (
278             drawable, TRUE, undo_desc, buffer,
279             GEGL_RECTANGLE (offset_x + rect.x, offset_y + rect.y, 0, 0),
280             FALSE);
281 
282           if (mask)
283             {
284               gimp_item_resize (GIMP_ITEM (mask),
285                                 gimp_get_default_context (image->gimp),
286                                 GIMP_FILL_TRANSPARENT,
287                                 rect.width, rect.height,
288                                 -rect.x, -rect.y);
289 
290               gimp_image_undo_group_end (image);
291             }
292         }
293     }
294   else
295     {
296       /*  canceled by the user  */
297 
298       if (clip)
299         {
300           gimp_gegl_buffer_copy (undo_buffer,
301                                  GEGL_RECTANGLE (0, 0,
302                                                  undo_rect.width,
303                                                  undo_rect.height),
304                                  GEGL_ABYSS_NONE,
305                                  gimp_drawable_get_buffer (drawable),
306                                  &undo_rect);
307         }
308 
309       success = FALSE;
310     }
311 
312   if (clip)
313     {
314       g_clear_object (&undo_buffer);
315       g_clear_object (&buffer);
316     }
317   else
318     {
319       g_object_unref (buffer);
320       g_object_unref (dest_buffer);
321     }
322 
323   if (cache)
324     {
325       g_object_unref (cache);
326       g_free (rects);
327     }
328 
329   if (applicator)
330     {
331       gimp_applicator_set_cache (applicator, applicator_cache);
332       gimp_applicator_set_output_format (applicator, applicator_output_format);
333     }
334 
335   if (update)
336     {
337       gimp_drawable_update (drawable,
338                             rect.x, rect.y,
339                             rect.width, rect.height);
340     }
341 
342   return success;
343 }
344