[gimp/goat-invasion] app: port GimpHeal to GEGL, using insane buffer casting tricks



commit b7d250c4b5553cb1c5b6c5b88911ef9ab274587f
Author: Michael Natterer <mitch gimp org>
Date:   Tue Apr 3 21:06:45 2012 +0200

    app: port GimpHeal to GEGL, using insane buffer casting tricks

 app/paint/gimpheal.c |  324 ++++++++++++++++++++++++++------------------------
 1 files changed, 170 insertions(+), 154 deletions(-)
---
diff --git a/app/paint/gimpheal.c b/app/paint/gimpheal.c
index 86951f8..a740840 100644
--- a/app/paint/gimpheal.c
+++ b/app/paint/gimpheal.c
@@ -26,7 +26,6 @@
 
 #include "paint-types.h"
 
-#include "base/pixel-region.h"
 #include "base/temp-buf.h"
 
 #include "gegl/gimp-gegl-utils.h"
@@ -70,32 +69,6 @@ static gboolean     gimp_heal_start              (GimpPaintCore    *paint_core,
                                                   const GimpCoords *coords,
                                                   GError          **error);
 
-static void         gimp_heal_sub                (PixelRegion      *topPR,
-                                                  PixelRegion      *bottomPR,
-                                                  gdouble          *result);
-
-static void         gimp_heal_add                (gdouble          *first,
-                                                  PixelRegion      *secondPR,
-                                                  PixelRegion      *resultPR);
-
-static gdouble      gimp_heal_laplace_iteration  (gdouble          *matrix,
-                                                  gint              height,
-                                                  gint              depth,
-                                                  gint              width,
-                                                  gdouble          *solution,
-                                                  guchar           *mask);
-
-static void         gimp_heal_laplace_loop       (gdouble          *matrix,
-                                                  gint              height,
-                                                  gint              depth,
-                                                  gint              width,
-                                                  gdouble          *solution,
-                                                  guchar           *mask);
-
-static PixelRegion *gimp_heal_region             (PixelRegion      *tempPR,
-                                                  PixelRegion      *srcPR,
-                                                  const TempBuf    *mask_buf);
-
 static void         gimp_heal_motion             (GimpSourceCore   *source_core,
                                                   GimpDrawable     *drawable,
                                                   GimpPaintOptions *paint_options,
@@ -174,92 +147,92 @@ gimp_heal_start (GimpPaintCore     *paint_core,
   return TRUE;
 }
 
-/* Subtract bottomPR from topPR and store the result as a double
+/* Subtract bottom from top and store in result as a double
  */
 static void
-gimp_heal_sub (PixelRegion *topPR,
-               PixelRegion *bottomPR,
-               gdouble     *result)
+gimp_heal_sub (GeglBuffer          *top_buffer,
+               const GeglRectangle *top_rect,
+               GeglBuffer          *bottom_buffer,
+               const GeglRectangle *bottom_rect,
+               GeglBuffer          *result_buffer,
+               const GeglRectangle *result_rect)
 {
-  gint     i, j, k;
+  GeglBufferIterator *iter;
+  const Babl         *format = gegl_buffer_get_format (top_buffer);
+  gint                bpp    = babl_format_get_bytes_per_pixel (format);
 
-  gint     height = topPR->h;
-  gint     width  = topPR->w;
-  gint     depth  = topPR->bytes;
+  gegl_buffer_set_format (top_buffer, babl_format_n (babl_type ("u8"), bpp));
+  gegl_buffer_set_format (bottom_buffer, babl_format_n (babl_type ("u8"), bpp));
 
-  guchar  *t_data = topPR->data;
-  guchar  *b_data = bottomPR->data;
+  iter = gegl_buffer_iterator_new (top_buffer, top_rect, 0, NULL,
+                                   GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
 
-  guchar  *t;
-  guchar  *b;
-  gdouble *r      = result;
+  gegl_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0, NULL,
+                            GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
 
-  g_assert (topPR->bytes == bottomPR->bytes);
+  gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0,
+                            babl_format_n (babl_type ("double"), bpp),
+                            GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
 
-  for (i = 0; i < height; i++)
+  while (gegl_buffer_iterator_next (iter))
     {
-      t = t_data;
-      b = b_data;
-
-      for (j = 0; j < width; j++)
-        {
-          for (k = 0; k < depth; k++)
-            {
-              r[k] = (gdouble) (t[k]) - (gdouble) (b[k]);
-            }
-          t += depth;
-          b += depth;
-          r += depth;
-        }
+      guchar  *t      = iter->data[0];
+      guchar  *b      = iter->data[1];
+      gdouble *r      = iter->data[2];
+      gint     length = iter->length * bpp;
 
-      t_data += topPR->rowstride;
-      b_data += bottomPR->rowstride;
+      while (length--)
+        *r++ = (gdouble) *t++ - (gdouble) *b++;
     }
+
+  gegl_buffer_set_format (top_buffer, NULL);
+  gegl_buffer_set_format (bottom_buffer, NULL);
 }
 
-/* Add first to secondPR and store the result as a PixelRegion
+/* Add first to second and store in result
  */
 static void
-gimp_heal_add (gdouble     *first,
-               PixelRegion *secondPR,
-               PixelRegion *resultPR)
+gimp_heal_add (GeglBuffer          *first_buffer,
+               const GeglRectangle *first_rect,
+               GeglBuffer          *second_buffer,
+               const GeglRectangle *second_rect,
+               GeglBuffer          *result_buffer,
+               const GeglRectangle *result_rect)
 {
-  gint     i, j, k;
+  GeglBufferIterator *iter;
+  const Babl         *format = gegl_buffer_get_format (result_buffer);
+  gint                bpp    = babl_format_get_bytes_per_pixel (format);
 
-  gint     height = secondPR->h;
-  gint     width  = secondPR->w;
-  gint     depth  = secondPR->bytes;
+  gegl_buffer_set_format (second_buffer, babl_format_n (babl_type ("u8"), bpp));
+  gegl_buffer_set_format (result_buffer, babl_format_n (babl_type ("u8"), bpp));
 
-  guchar  *s_data = secondPR->data;
-  guchar  *r_data = resultPR->data;
+  iter = gegl_buffer_iterator_new (first_buffer, first_rect, 0,
+                                   babl_format_n (babl_type ("double"), bpp),
+                                   GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
 
-  gdouble *f      = first;
-  guchar  *s;
-  guchar  *r;
+  gegl_buffer_iterator_add (iter, second_buffer, second_rect, 0, NULL,
+                            GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
 
-  g_assert (secondPR->bytes == resultPR->bytes);
+  gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0, NULL,
+                            GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
 
-  for (i = 0; i < height; i++)
+  while (gegl_buffer_iterator_next (iter))
     {
-      s = s_data;
-      r = r_data;
+      gdouble *f      = iter->data[0];
+      guchar  *s      = iter->data[1];
+      guchar  *r      = iter->data[2];
+      gint     length = iter->length * bpp;
 
-      for (j = 0; j < width; j++)
+      while (length--)
         {
-          for (k = 0; k < depth; k++)
-            {
-              r[k] = (guchar) CLAMP0255 (ROUND (((gdouble) (f[k])) +
-                                                ((gdouble) (s[k]))));
-            }
+          gdouble tmp = ROUND (*f++ + (gdouble) *s++);
 
-          f += depth;
-          s += depth;
-          r += depth;
+          *r++ = (guchar) CLAMP0255 (tmp);
         }
-
-      s_data += secondPR->rowstride;
-      r_data += resultPR->rowstride;
     }
+
+  gegl_buffer_set_format (second_buffer, NULL);
+  gegl_buffer_set_format (result_buffer, NULL);
 }
 
 /* Perform one iteration of the laplace solver for matrix.  Store the
@@ -411,29 +384,77 @@ gimp_heal_laplace_loop (gdouble *matrix,
  * T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning
  * http://www.tgeorgiev.net/Photoshop_Healing.pdf
  */
-static PixelRegion *
-gimp_heal_region (PixelRegion   *tempPR,
-                  PixelRegion   *srcPR,
-                  const TempBuf *mask_buf)
+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)
 {
-  gdouble *i_1  = g_new (gdouble, tempPR->h * tempPR->bytes * tempPR->w);
-  gdouble *i_2  = g_new (gdouble, tempPR->h * tempPR->bytes * tempPR->w);
-  guchar  *mask = temp_buf_get_data (mask_buf);
-
-  /* substract pattern to image and store the result as a double in i_1 */
-  gimp_heal_sub (tempPR, srcPR, i_1);
+  const Babl *src_format;
+  const Babl *dest_format;
+  gint        src_bpp;
+  gint        dest_bpp;
+  gint        width;
+  gint        height;
+  gdouble    *i_1;
+  gdouble    *i_2;
+  GeglBuffer *i_1_buffer;
+  GeglBuffer *i_2_buffer;
+  guchar     *mask;
+
+  src_format  = gegl_buffer_get_format (src_buffer);
+  dest_format = gegl_buffer_get_format (dest_buffer);
+
+  src_bpp  = babl_format_get_bytes_per_pixel (src_format);
+  dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+  width  = gegl_buffer_get_width  (src_buffer);
+  height = gegl_buffer_get_height (src_buffer);
+
+  g_return_if_fail (src_bpp == dest_bpp);
+
+  i_1  = g_new (gdouble, width * height * src_bpp);
+  i_2  = g_new (gdouble, width * height * src_bpp);
+
+  i_1_buffer =
+    gegl_buffer_linear_new_from_data (i_1,
+                                      babl_format_n (babl_type ("double"),
+                                                     src_bpp),
+                                      GEGL_RECTANGLE (0, 0, width, height),
+                                      GEGL_AUTO_ROWSTRIDE,
+                                      (GDestroyNotify) g_free, i_1);
+  i_2_buffer =
+    gegl_buffer_linear_new_from_data (i_2,
+                                      babl_format_n (babl_type ("double"),
+                                                     src_bpp),
+                                      GEGL_RECTANGLE (0, 0, width, height),
+                                      GEGL_AUTO_ROWSTRIDE,
+                                      (GDestroyNotify) g_free, i_2);
+
+  /* substract pattern from image and store the result as a double in i_1 */
+  gimp_heal_sub (dest_buffer, dest_rect,
+                 src_buffer, src_rect,
+                 i_1_buffer, GEGL_RECTANGLE (0, 0, width, height));
+
+  mask = g_new (guchar, mask_rect->width * mask_rect->height);
+
+  gegl_buffer_get (mask_buffer, mask_rect, 1.0, babl_format ("Y u8"),
+                   mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
 
   /* FIXME: is a faster implementation needed? */
-  gimp_heal_laplace_loop (i_1, tempPR->h, tempPR->bytes, tempPR->w, i_2, mask);
+  gimp_heal_laplace_loop (i_1, height, src_bpp, width, i_2, mask);
 
-  /* add solution to original image and store in tempPR */
-  gimp_heal_add (i_2, srcPR, tempPR);
+  g_free (mask);
 
-  /* clean up */
-  g_free (i_1);
-  g_free (i_2);
+  /* add solution to original image and store in dest */
+  gimp_heal_add (i_2_buffer, GEGL_RECTANGLE (0, 0, width, height),
+                 src_buffer, src_rect,
+                 dest_buffer, dest_rect);
 
-  return tempPR;
+  g_object_unref (i_1_buffer);
+  g_object_unref (i_2_buffer);
 }
 
 static void
@@ -460,11 +481,9 @@ gimp_heal_motion (GimpSourceCore   *source_core,
   GimpDynamics       *dynamics   = GIMP_BRUSH_CORE (paint_core)->dynamics;
   GimpDynamicsOutput *hardness_output;
   GimpImage          *image      = gimp_item_get_image (GIMP_ITEM (drawable));
-  TempBuf            *src_temp_buf;
-  TempBuf            *dest_temp_buf;
+  GeglBuffer         *src_copy;
   GeglBuffer         *dest_buffer;
-  PixelRegion         srcPR;
-  PixelRegion         destPR;
+  GeglBuffer         *mask_buffer;
   const TempBuf      *mask_buf;
   gdouble             fade_point;
   gdouble             hardness;
@@ -485,32 +504,37 @@ gimp_heal_motion (GimpSourceCore   *source_core,
                                              GIMP_BRUSH_HARD,
                                              hardness);
 
-  /* copy the source buffer because we are going to modify it */
-  {
-    GeglBuffer *tmp;
-
-    src_temp_buf = temp_buf_new (src_rect->width, src_rect->height,
-                                 gimp_drawable_bytes_with_alpha (drawable),
-                                 0, 0, NULL);
-
-    tmp = gimp_temp_buf_create_buffer (src_temp_buf,
-                                       gimp_drawable_get_format_with_alpha (drawable),
-                                       FALSE);
+  /* check that all buffers are of the same size */
+  if (src_rect->width  != gegl_buffer_get_width  (paint_buffer) ||
+      src_rect->height != gegl_buffer_get_height (paint_buffer) ||
+      src_rect->width  != mask_buf->width ||
+      src_rect->height != mask_buf->height)
+    {
+      /* this generally means that the source point has hit the edge
+       * of the layer, so it is not an error and we should not
+       * complain, just don't do anything
+       */
+      return;
+    }
 
-    gegl_buffer_copy (src_buffer, src_rect,
-                      tmp, GEGL_RECTANGLE (0, 0, 0, 0));
-    g_object_unref (tmp);
-  }
+  src_copy =
+    gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+                                     src_rect->width,
+                                     src_rect->height),
+                     gimp_drawable_get_format_with_alpha (drawable));
 
-  dest_temp_buf = temp_buf_new (gegl_buffer_get_width  (paint_buffer),
-                                gegl_buffer_get_height (paint_buffer),
-                                gimp_drawable_bytes_with_alpha (drawable),
-                                0, 0, NULL);
+  gegl_buffer_copy (src_buffer,
+                    src_rect,
+                    src_copy,
+                    GEGL_RECTANGLE (0, 0,
+                                    src_rect->width,
+                                    src_rect->height));
 
   dest_buffer =
-    gimp_temp_buf_create_buffer (dest_temp_buf,
-                                 gimp_drawable_get_format_with_alpha (drawable),
-                                 TRUE);
+    gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+                                     gegl_buffer_get_width  (paint_buffer),
+                                     gegl_buffer_get_height (paint_buffer)),
+                     gimp_drawable_get_format_with_alpha (drawable));
 
   gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
                     GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y,
@@ -519,31 +543,21 @@ gimp_heal_motion (GimpSourceCore   *source_core,
                     dest_buffer,
                     GEGL_RECTANGLE (0, 0, 0, 0));
 
-  /* check that srcPR, tempPR, destPR, and mask_buf are the same size */
-  if (src_temp_buf->width  != dest_temp_buf->width  ||
-      src_temp_buf->height != dest_temp_buf->height ||
-      src_temp_buf->width  != mask_buf->width ||
-      src_temp_buf->height != mask_buf->height)
-    {
-      /* this generally means that the source point has hit the edge of the
-         layer, so it is not an error and we should not complain, just
-         don't do anything */
-
-      temp_buf_free (src_temp_buf);
-      g_object_unref (dest_buffer);
-
-      return;
-    }
-
-  pixel_region_init_temp_buf (&srcPR, src_temp_buf,
-                              0, 0,
-                              mask_buf->width, mask_buf->height);
-  pixel_region_init_temp_buf (&destPR, dest_temp_buf,
-                              0, 0,
-                              mask_buf->width, mask_buf->height);
-
-  /* heal destPR using srcPR */
-  gimp_heal_region (&destPR, &srcPR, mask_buf);
+  mask_buffer = gimp_temp_buf_create_buffer ((TempBuf *) mask_buf,
+                                             NULL, FALSE);
+
+  gimp_heal (src_copy,
+             GEGL_RECTANGLE (0, 0,
+                             gegl_buffer_get_width  (src_copy),
+                             gegl_buffer_get_height (src_copy)),
+             dest_buffer,
+             GEGL_RECTANGLE (0, 0,
+                             gegl_buffer_get_width  (dest_buffer),
+                             gegl_buffer_get_height (dest_buffer)),
+             mask_buffer,
+             GEGL_RECTANGLE (0, 0,
+                             gegl_buffer_get_width  (mask_buffer),
+                             gegl_buffer_get_height (mask_buffer)));
 
   gegl_buffer_copy (dest_buffer,
                     GEGL_RECTANGLE (0, 0, mask_buf->width, mask_buf->height),
@@ -553,7 +567,9 @@ gimp_heal_motion (GimpSourceCore   *source_core,
                                     paint_area_width,
                                     paint_area_height));
 
+  g_object_unref (src_copy);
   g_object_unref (dest_buffer);
+  g_object_unref (mask_buffer);
 
   /* replace the canvas with our healed data */
   gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]