1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpheal.c
5  * Copyright (C) Jean-Yves Couleaud <cjyves@free.fr>
6  * Copyright (C) 2013 Loren Merritt
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdint.h>
25 #include <string.h>
26 
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 #include <gegl.h>
29 
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpmath/gimpmath.h"
32 
33 #include "paint-types.h"
34 
35 #include "gegl/gimp-gegl-apply-operation.h"
36 #include "gegl/gimp-gegl-loops.h"
37 
38 #include "core/gimpbrush.h"
39 #include "core/gimpdrawable.h"
40 #include "core/gimpdynamics.h"
41 #include "core/gimperror.h"
42 #include "core/gimpimage.h"
43 #include "core/gimppickable.h"
44 #include "core/gimptempbuf.h"
45 
46 #include "gimpheal.h"
47 #include "gimpsourceoptions.h"
48 
49 #include "gimp-intl.h"
50 
51 
52 
53 /* NOTES
54  *
55  * The method used here is similar to the lighting invariant correction
56  * method but slightly different: we do not divide the RGB components,
57  * but subtract them I2 = I0 - I1, where I0 is the sample image to be
58  * corrected, I1 is the reference pattern. Then we solve DeltaI=0
59  * (Laplace) with I2 Dirichlet conditions at the borders of the
60  * mask. The solver is a red/black checker Gauss-Seidel with over-relaxation.
61  * It could benefit from a multi-grid evaluation of an initial solution
62  * before the main iteration loop.
63  *
64  * I reduced the convergence criteria to 0.1% (0.001) as we are
65  * dealing here with RGB integer components, more is overkill.
66  *
67  * Jean-Yves Couleaud cjyves@free.fr
68  */
69 
70 static gboolean     gimp_heal_start              (GimpPaintCore    *paint_core,
71                                                   GimpDrawable     *drawable,
72                                                   GimpPaintOptions *paint_options,
73                                                   const GimpCoords *coords,
74                                                   GError          **error);
75 static GeglBuffer * gimp_heal_get_paint_buffer   (GimpPaintCore    *core,
76                                                   GimpDrawable     *drawable,
77                                                   GimpPaintOptions *paint_options,
78                                                   GimpLayerMode     paint_mode,
79                                                   const GimpCoords *coords,
80                                                   gint             *paint_buffer_x,
81                                                   gint             *paint_buffer_y,
82                                                   gint             *paint_width,
83                                                   gint             *paint_height);
84 
85 static void         gimp_heal_motion             (GimpSourceCore   *source_core,
86                                                   GimpDrawable     *drawable,
87                                                   GimpPaintOptions *paint_options,
88                                                   const GimpCoords *coords,
89                                                   GeglNode         *op,
90                                                   gdouble           opacity,
91                                                   GimpPickable     *src_pickable,
92                                                   GeglBuffer       *src_buffer,
93                                                   GeglRectangle    *src_rect,
94                                                   gint              src_offset_x,
95                                                   gint              src_offset_y,
96                                                   GeglBuffer       *paint_buffer,
97                                                   gint              paint_buffer_x,
98                                                   gint              paint_buffer_y,
99                                                   gint              paint_area_offset_x,
100                                                   gint              paint_area_offset_y,
101                                                   gint              paint_area_width,
102                                                   gint              paint_area_height);
103 
104 
G_DEFINE_TYPE(GimpHeal,gimp_heal,GIMP_TYPE_SOURCE_CORE)105 G_DEFINE_TYPE (GimpHeal, gimp_heal, GIMP_TYPE_SOURCE_CORE)
106 
107 #define parent_class gimp_heal_parent_class
108 
109 
110 void
111 gimp_heal_register (Gimp                      *gimp,
112                     GimpPaintRegisterCallback  callback)
113 {
114   (* callback) (gimp,
115                 GIMP_TYPE_HEAL,
116                 GIMP_TYPE_SOURCE_OPTIONS,
117                 "gimp-heal",
118                 _("Healing"),
119                 "gimp-tool-heal");
120 }
121 
122 static void
gimp_heal_class_init(GimpHealClass * klass)123 gimp_heal_class_init (GimpHealClass *klass)
124 {
125   GimpPaintCoreClass  *paint_core_class  = GIMP_PAINT_CORE_CLASS (klass);
126   GimpSourceCoreClass *source_core_class = GIMP_SOURCE_CORE_CLASS (klass);
127 
128   paint_core_class->start            = gimp_heal_start;
129   paint_core_class->get_paint_buffer = gimp_heal_get_paint_buffer;
130 
131   source_core_class->motion          = gimp_heal_motion;
132 }
133 
134 static void
gimp_heal_init(GimpHeal * heal)135 gimp_heal_init (GimpHeal *heal)
136 {
137 }
138 
139 static gboolean
gimp_heal_start(GimpPaintCore * paint_core,GimpDrawable * drawable,GimpPaintOptions * paint_options,const GimpCoords * coords,GError ** error)140 gimp_heal_start (GimpPaintCore     *paint_core,
141                  GimpDrawable      *drawable,
142                  GimpPaintOptions  *paint_options,
143                  const GimpCoords  *coords,
144                  GError           **error)
145 {
146   GimpSourceCore *source_core = GIMP_SOURCE_CORE (paint_core);
147 
148   if (! GIMP_PAINT_CORE_CLASS (parent_class)->start (paint_core, drawable,
149                                                      paint_options, coords,
150                                                      error))
151     {
152       return FALSE;
153     }
154 
155   if (! source_core->set_source && gimp_drawable_is_indexed (drawable))
156     {
157       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
158                            _("Healing does not operate on indexed layers."));
159       return FALSE;
160     }
161 
162   return TRUE;
163 }
164 
165 static GeglBuffer *
gimp_heal_get_paint_buffer(GimpPaintCore * core,GimpDrawable * drawable,GimpPaintOptions * paint_options,GimpLayerMode paint_mode,const GimpCoords * coords,gint * paint_buffer_x,gint * paint_buffer_y,gint * paint_width,gint * paint_height)166 gimp_heal_get_paint_buffer (GimpPaintCore    *core,
167                             GimpDrawable     *drawable,
168                             GimpPaintOptions *paint_options,
169                             GimpLayerMode     paint_mode,
170                             const GimpCoords *coords,
171                             gint             *paint_buffer_x,
172                             gint             *paint_buffer_y,
173                             gint             *paint_width,
174                             gint             *paint_height)
175 {
176   return GIMP_PAINT_CORE_CLASS (parent_class)->get_paint_buffer (core,
177                                                                  drawable,
178                                                                  paint_options,
179                                                                  GIMP_LAYER_MODE_NORMAL,
180                                                                  coords,
181                                                                  paint_buffer_x,
182                                                                  paint_buffer_y,
183                                                                  paint_width,
184                                                                  paint_height);
185 }
186 
187 /* Subtract bottom from top and store in result as a float
188  */
189 static void
gimp_heal_sub(GeglBuffer * top_buffer,const GeglRectangle * top_rect,GeglBuffer * bottom_buffer,const GeglRectangle * bottom_rect,GeglBuffer * result_buffer,const GeglRectangle * result_rect)190 gimp_heal_sub (GeglBuffer          *top_buffer,
191                const GeglRectangle *top_rect,
192                GeglBuffer          *bottom_buffer,
193                const GeglRectangle *bottom_rect,
194                GeglBuffer          *result_buffer,
195                const GeglRectangle *result_rect)
196 {
197   GeglBufferIterator *iter;
198   const Babl         *format       = gegl_buffer_get_format (top_buffer);
199   gint                n_components = babl_format_get_n_components (format);
200 
201   if (n_components == 2)
202     format = babl_format ("Y'A float");
203   else if (n_components == 4)
204     format = babl_format ("R'G'B'A float");
205   else
206     g_return_if_reached ();
207 
208   iter = gegl_buffer_iterator_new (top_buffer, top_rect, 0, format,
209                                    GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3);
210 
211   gegl_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0, format,
212                             GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
213 
214   gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0,
215                             babl_format_n (babl_type ("float"), n_components),
216                             GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
217 
218   while (gegl_buffer_iterator_next (iter))
219     {
220       gfloat *t      = iter->items[0].data;
221       gfloat *b      = iter->items[1].data;
222       gfloat *r      = iter->items[2].data;
223       gint    length = iter->length * n_components;
224 
225       while (length--)
226         *r++ = *t++ - *b++;
227     }
228 }
229 
230 /* Add first to second and store in result
231  */
232 static void
gimp_heal_add(GeglBuffer * first_buffer,const GeglRectangle * first_rect,GeglBuffer * second_buffer,const GeglRectangle * second_rect,GeglBuffer * result_buffer,const GeglRectangle * result_rect)233 gimp_heal_add (GeglBuffer          *first_buffer,
234                const GeglRectangle *first_rect,
235                GeglBuffer          *second_buffer,
236                const GeglRectangle *second_rect,
237                GeglBuffer          *result_buffer,
238                const GeglRectangle *result_rect)
239 {
240   GeglBufferIterator *iter;
241   const Babl         *format       = gegl_buffer_get_format (result_buffer);
242   gint                n_components = babl_format_get_n_components (format);
243 
244   if (n_components == 2)
245     format = babl_format ("Y'A float");
246   else if (n_components == 4)
247     format = babl_format ("R'G'B'A float");
248   else
249     g_return_if_reached ();
250 
251   iter = gegl_buffer_iterator_new (first_buffer, first_rect, 0,
252                                    babl_format_n (babl_type ("float"),
253                                                   n_components),
254                                    GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3);
255 
256   gegl_buffer_iterator_add (iter, second_buffer, second_rect, 0, format,
257                             GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
258 
259   gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0, format,
260                             GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
261 
262   while (gegl_buffer_iterator_next (iter))
263     {
264       gfloat *f      = iter->items[0].data;
265       gfloat *s      = iter->items[1].data;
266       gfloat *r      = iter->items[2].data;
267       gint    length = iter->length * n_components;
268 
269       while (length--)
270         *r++ = *f++ + *s++;
271     }
272 }
273 
274 #if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4
275 static float
gimp_heal_laplace_iteration_sse(gfloat * pixels,gfloat * Adiag,gint * Aidx,gfloat w,gint nmask)276 gimp_heal_laplace_iteration_sse (gfloat *pixels,
277                                  gfloat *Adiag,
278                                  gint   *Aidx,
279                                  gfloat  w,
280                                  gint    nmask)
281 {
282   typedef float v4sf __attribute__((vector_size(16)));
283   gint i;
284   v4sf wv  = { w, w, w, w };
285   v4sf err = { 0, 0, 0, 0 };
286   union { v4sf v; float f[4]; } erru;
287 
288 #define Xv(j) (*(v4sf*)&pixels[Aidx[i * 5 + j]])
289 
290   for (i = 0; i < nmask; i++)
291     {
292       v4sf a    = { Adiag[i], Adiag[i], Adiag[i], Adiag[i] };
293       v4sf diff = a * Xv(0) - wv * (Xv(1) + Xv(2) + Xv(3) + Xv(4));
294 
295       Xv(0) -= diff;
296       err += diff * diff;
297     }
298 
299   erru.v = err;
300 
301   return erru.f[0] + erru.f[1] + erru.f[2] + erru.f[3];
302 }
303 #endif
304 
305 /* Perform one iteration of Gauss-Seidel, and return the sum squared residual.
306  */
307 static float
gimp_heal_laplace_iteration(gfloat * pixels,gfloat * Adiag,gint * Aidx,gfloat w,gint nmask,gint depth)308 gimp_heal_laplace_iteration (gfloat *pixels,
309                              gfloat *Adiag,
310                              gint   *Aidx,
311                              gfloat  w,
312                              gint    nmask,
313                              gint    depth)
314 {
315   gint   i, k;
316   gfloat err = 0;
317 
318 #if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4
319   if (depth == 4)
320     return gimp_heal_laplace_iteration_sse (pixels, Adiag, Aidx, w, nmask);
321 #endif
322 
323   for (i = 0; i < nmask; i++)
324     {
325       gint   j0 = Aidx[i * 5 + 0];
326       gint   j1 = Aidx[i * 5 + 1];
327       gint   j2 = Aidx[i * 5 + 2];
328       gint   j3 = Aidx[i * 5 + 3];
329       gint   j4 = Aidx[i * 5 + 4];
330       gfloat a  = Adiag[i];
331 
332       for (k = 0; k < depth; k++)
333         {
334           gfloat diff = (a * pixels[j0 + k] -
335                          w * (pixels[j1 + k] +
336                               pixels[j2 + k] +
337                               pixels[j3 + k] +
338                               pixels[j4 + k]));
339 
340           pixels[j0 + k] -= diff;
341           err += diff * diff;
342         }
343     }
344 
345   return err;
346 }
347 
348 /* Solve the laplace equation for pixels and store the result in-place.
349  */
350 static void
gimp_heal_laplace_loop(gfloat * pixels,gint height,gint depth,gint width,guchar * mask)351 gimp_heal_laplace_loop (gfloat *pixels,
352                         gint    height,
353                         gint    depth,
354                         gint    width,
355                         guchar *mask)
356 {
357   /* Tolerate a total deviation-from-smoothness of 0.1 LSBs at 8bit depth. */
358 #define EPSILON  (0.1/255)
359 #define MAX_ITER 500
360 
361   gint    i, j, iter, parity, nmask, zero;
362   gfloat *Adiag;
363   gint   *Aidx;
364   gfloat  w;
365 
366   Adiag = g_new (gfloat, width * height);
367   Aidx  = g_new (gint, 5 * width * height);
368 
369   /* All off-diagonal elements of A are either -1 or 0. We could store it as a
370    * general-purpose sparse matrix, but that adds some unnecessary overhead to
371    * the inner loop. Instead, assume exactly 4 off-diagonal elements in each
372    * row, all of which have value -1. Any row that in fact wants less than 4
373    * coefs can put them in a dummy column to be multiplied by an empty pixel.
374    */
375   zero = depth * width * height;
376   memset (pixels + zero, 0, depth * sizeof (gfloat));
377 
378   /* Construct the system of equations.
379    * Arrange Aidx in checkerboard order, so that a single linear pass over that
380    * array results updating all of the red cells and then all of the black cells.
381    */
382   nmask = 0;
383   for (parity = 0; parity < 2; parity++)
384     for (i = 0; i < height; i++)
385       for (j = (i&1)^parity; j < width; j+=2)
386         if (mask[j + i * width])
387           {
388 #define A_NEIGHBOR(o,di,dj) \
389             if ((dj<0 && j==0) || (dj>0 && j==width-1) || (di<0 && i==0) || (di>0 && i==height-1)) \
390               Aidx[o + nmask * 5] = zero; \
391             else                                               \
392               Aidx[o + nmask * 5] = ((i + di) * width + (j + dj)) * depth;
393 
394             /* Omit Dirichlet conditions for any neighbors off the
395              * edge of the canvas.
396              */
397             Adiag[nmask] = 4 - (i==0) - (j==0) - (i==height-1) - (j==width-1);
398             A_NEIGHBOR (0,  0,  0);
399             A_NEIGHBOR (1,  0,  1);
400             A_NEIGHBOR (2,  1,  0);
401             A_NEIGHBOR (3,  0, -1);
402             A_NEIGHBOR (4, -1,  0);
403             nmask++;
404           }
405 
406   /* Empirically optimal over-relaxation factor. (Benchmarked on
407    * round brushes, at least. I don't know whether aspect ratio
408    * affects it.)
409    */
410   w = 2.0 - 1.0 / (0.1575 * sqrt (nmask) + 0.8);
411   w *= 0.25;
412   for (i = 0; i < nmask; i++)
413     Adiag[i] *= w;
414 
415   /* Gauss-Seidel with successive over-relaxation */
416   for (iter = 0; iter < MAX_ITER; iter++)
417     {
418       gfloat err = gimp_heal_laplace_iteration (pixels, Adiag, Aidx,
419                                                 w, nmask, depth);
420       if (err < EPSILON * EPSILON * w * w)
421         break;
422     }
423 
424   g_free (Adiag);
425   g_free (Aidx);
426 }
427 
428 /* Original Algorithm Design:
429  *
430  * T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning
431  * http://www.tgeorgiev.net/Photoshop_Healing.pdf
432  */
433 static void
gimp_heal(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,GeglBuffer * mask_buffer,const GeglRectangle * mask_rect)434 gimp_heal (GeglBuffer          *src_buffer,
435            const GeglRectangle *src_rect,
436            GeglBuffer          *dest_buffer,
437            const GeglRectangle *dest_rect,
438            GeglBuffer          *mask_buffer,
439            const GeglRectangle *mask_rect)
440 {
441   const Babl *src_format;
442   const Babl *dest_format;
443   gint        src_components;
444   gint        dest_components;
445   gint        width;
446   gint        height;
447   gfloat     *diff, *diff_alloc;
448   GeglBuffer *diff_buffer;
449   guchar     *mask;
450 
451   src_format  = gegl_buffer_get_format (src_buffer);
452   dest_format = gegl_buffer_get_format (dest_buffer);
453 
454   src_components  = babl_format_get_n_components (src_format);
455   dest_components = babl_format_get_n_components (dest_format);
456 
457   width  = gegl_buffer_get_width  (src_buffer);
458   height = gegl_buffer_get_height (src_buffer);
459 
460   g_return_if_fail (src_components == dest_components);
461 
462   diff_alloc = g_new (gfloat, 4 + (width * height + 1) * src_components);
463   diff = (gfloat*)(((uintptr_t)diff_alloc + 15) & ~15);
464 
465   diff_buffer =
466     gegl_buffer_linear_new_from_data (diff,
467                                       babl_format_n (babl_type ("float"),
468                                                      src_components),
469                                       GEGL_RECTANGLE (0, 0, width, height),
470                                       GEGL_AUTO_ROWSTRIDE,
471                                       (GDestroyNotify) g_free, diff_alloc);
472 
473   /* subtract pattern from image and store the result as a float in diff */
474   gimp_heal_sub (dest_buffer, dest_rect,
475                  src_buffer, src_rect,
476                  diff_buffer, GEGL_RECTANGLE (0, 0, width, height));
477 
478   mask = g_new (guchar, mask_rect->width * mask_rect->height);
479 
480   gegl_buffer_get (mask_buffer, mask_rect, 1.0, babl_format ("Y u8"),
481                    mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
482 
483   gimp_heal_laplace_loop (diff, height, src_components, width, mask);
484 
485   g_free (mask);
486 
487   /* add solution to original image and store in dest */
488   gimp_heal_add (diff_buffer, GEGL_RECTANGLE (0, 0, width, height),
489                  src_buffer, src_rect,
490                  dest_buffer, dest_rect);
491 
492   g_object_unref (diff_buffer);
493 }
494 
495 static void
gimp_heal_motion(GimpSourceCore * source_core,GimpDrawable * drawable,GimpPaintOptions * paint_options,const GimpCoords * coords,GeglNode * op,gdouble opacity,GimpPickable * src_pickable,GeglBuffer * src_buffer,GeglRectangle * src_rect,gint src_offset_x,gint src_offset_y,GeglBuffer * paint_buffer,gint paint_buffer_x,gint paint_buffer_y,gint paint_area_offset_x,gint paint_area_offset_y,gint paint_area_width,gint paint_area_height)496 gimp_heal_motion (GimpSourceCore   *source_core,
497                   GimpDrawable     *drawable,
498                   GimpPaintOptions *paint_options,
499                   const GimpCoords *coords,
500                   GeglNode         *op,
501                   gdouble           opacity,
502                   GimpPickable     *src_pickable,
503                   GeglBuffer       *src_buffer,
504                   GeglRectangle    *src_rect,
505                   gint              src_offset_x,
506                   gint              src_offset_y,
507                   GeglBuffer       *paint_buffer,
508                   gint              paint_buffer_x,
509                   gint              paint_buffer_y,
510                   gint              paint_area_offset_x,
511                   gint              paint_area_offset_y,
512                   gint              paint_area_width,
513                   gint              paint_area_height)
514 {
515   GimpPaintCore     *paint_core  = GIMP_PAINT_CORE (source_core);
516   GimpContext       *context     = GIMP_CONTEXT (paint_options);
517   GimpSourceOptions *src_options = GIMP_SOURCE_OPTIONS (paint_options);
518   GimpDynamics      *dynamics    = GIMP_BRUSH_CORE (paint_core)->dynamics;
519   GimpImage         *image       = gimp_item_get_image (GIMP_ITEM (drawable));
520   GeglBuffer        *src_copy;
521   GeglBuffer        *mask_buffer;
522   GimpPickable      *dest_pickable;
523   const GimpTempBuf *mask_buf;
524   gdouble            fade_point;
525   gdouble            force;
526   gint               mask_off_x;
527   gint               mask_off_y;
528   gint               dest_pickable_off_x;
529   gint               dest_pickable_off_y;
530 
531   fade_point = gimp_paint_options_get_fade (paint_options, image,
532                                             paint_core->pixel_dist);
533 
534   if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
535     force = gimp_dynamics_get_linear_value (dynamics,
536                                             GIMP_DYNAMICS_OUTPUT_FORCE,
537                                             coords,
538                                             paint_options,
539                                             fade_point);
540   else
541     force = paint_options->brush_force;
542 
543   mask_buf = gimp_brush_core_get_brush_mask (GIMP_BRUSH_CORE (source_core),
544                                              coords,
545                                              GIMP_BRUSH_HARD,
546                                              force);
547 
548   if (! mask_buf)
549     return;
550 
551   /* check that all buffers are of the same size */
552   if (src_rect->width  != gegl_buffer_get_width  (paint_buffer) ||
553       src_rect->height != gegl_buffer_get_height (paint_buffer))
554     {
555       /* this generally means that the source point has hit the edge
556        * of the layer, so it is not an error and we should not
557        * complain, just don't do anything
558        */
559       return;
560     }
561 
562   /*  heal should work in perceptual space, use R'G'B' instead of RGB  */
563   src_copy = gegl_buffer_new (GEGL_RECTANGLE (paint_area_offset_x,
564                                               paint_area_offset_y,
565                                               src_rect->width,
566                                               src_rect->height),
567                               babl_format ("R'G'B'A float"));
568 
569   if (! op)
570     {
571       gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE,
572                              src_copy, gegl_buffer_get_extent (src_copy));
573     }
574   else
575     {
576       gimp_gegl_apply_operation (src_buffer, NULL, NULL, op,
577                                  src_copy, gegl_buffer_get_extent (src_copy),
578                                  FALSE);
579     }
580 
581   if (src_options->sample_merged)
582     {
583       dest_pickable = GIMP_PICKABLE (image);
584 
585       gimp_item_get_offset (GIMP_ITEM (drawable),
586                             &dest_pickable_off_x,
587                             &dest_pickable_off_y);
588     }
589   else
590     {
591       dest_pickable = GIMP_PICKABLE (drawable);
592 
593       dest_pickable_off_x = 0;
594       dest_pickable_off_y = 0;
595     }
596 
597   gimp_gegl_buffer_copy (gimp_pickable_get_buffer (dest_pickable),
598                          GEGL_RECTANGLE (paint_buffer_x + dest_pickable_off_x,
599                                          paint_buffer_y + dest_pickable_off_y,
600                                          gegl_buffer_get_width  (paint_buffer),
601                                          gegl_buffer_get_height (paint_buffer)),
602                          GEGL_ABYSS_NONE,
603                          paint_buffer,
604                          GEGL_RECTANGLE (paint_area_offset_x,
605                                          paint_area_offset_y,
606                                          paint_area_width,
607                                          paint_area_height));
608 
609   mask_buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask_buf);
610 
611   /* find the offset of the brush mask's rect */
612   {
613     gint x = (gint) floor (coords->x) - (gegl_buffer_get_width  (mask_buffer) >> 1);
614     gint y = (gint) floor (coords->y) - (gegl_buffer_get_height (mask_buffer) >> 1);
615 
616     mask_off_x = (x < 0) ? -x : 0;
617     mask_off_y = (y < 0) ? -y : 0;
618   }
619 
620   gimp_heal (src_copy, gegl_buffer_get_extent (src_copy),
621              paint_buffer,
622              GEGL_RECTANGLE (paint_area_offset_x,
623                              paint_area_offset_y,
624                              paint_area_width,
625                              paint_area_height),
626              mask_buffer,
627              GEGL_RECTANGLE (mask_off_x, mask_off_y,
628                              paint_area_width,
629                              paint_area_height));
630 
631   g_object_unref (src_copy);
632   g_object_unref (mask_buffer);
633 
634   /* replace the canvas with our healed data */
635   gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
636                                   coords,
637                                   MIN (opacity, GIMP_OPACITY_OPAQUE),
638                                   gimp_context_get_opacity (context),
639                                   gimp_paint_options_get_brush_mode (paint_options),
640                                   force,
641                                   GIMP_PAINT_INCREMENTAL);
642 }
643