[gimp/goat-invasion: 253/418] app: replace border_region() by GimpOperationBorder



commit 7055516828af76ddc17be6852b92822a58b0877a
Author: Michael Natterer <mitch gimp org>
Date:   Mon Mar 26 00:35:03 2012 +0200

    app: replace border_region() by GimpOperationBorder

 app/core/gimpchannel.c         |   23 +-
 app/gegl/Makefile.am           |    2 +
 app/gegl/gimp-gegl-types.h     |    1 +
 app/gegl/gimp-gegl.c           |    2 +
 app/gegl/gimpoperationborder.c |  718 ++++++++++++++++++++++++++++++++++++++++
 app/gegl/gimpoperationborder.h |   57 ++++
 app/paint-funcs/paint-funcs.c  |  454 -------------------------
 app/paint-funcs/paint-funcs.h  |    6 -
 8 files changed, 796 insertions(+), 467 deletions(-)
---
diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c
index f2b27bb..34658e5 100644
--- a/app/core/gimpchannel.c
+++ b/app/core/gimpchannel.c
@@ -26,7 +26,6 @@
 
 #include "core-types.h"
 
-#include "base/pixel-region.h"
 #include "base/tile.h"
 #include "base/tile-manager.h"
 
@@ -1415,8 +1414,8 @@ gimp_channel_real_border (GimpChannel *channel,
                           gboolean     edge_lock,
                           gboolean     push_undo)
 {
-  PixelRegion bPR;
-  gint        x1, y1, x2, y2;
+  GeglNode *border;
+  gint      x1, y1, x2, y2;
 
   if (radius_x < 0 || radius_y < 0)
     return;
@@ -1453,11 +1452,21 @@ gimp_channel_real_border (GimpChannel *channel,
   else
     gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (channel));
 
-  pixel_region_init (&bPR,
-                     gimp_drawable_get_tiles (GIMP_DRAWABLE (channel)),
-                     x1, y1, x2 - x1, y2 - y1, TRUE);
+  border = gegl_node_new_child (NULL,
+                                "operation", "gimp:border",
+                                "radius-x",  radius_x,
+                                "radius-y",  radius_y,
+                                "feather",   feather,
+                                "edge-lock", edge_lock,
+                                NULL);
+
+  gimp_apply_operation (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)),
+                        NULL, NULL,
+                        border,
+                        gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)),
+                        GIMP_GEGL_RECT (x1, y1, x2 - x1, y2 - y1));
 
-  border_region (&bPR, radius_x, radius_y, feather, edge_lock);
+  g_object_unref (border);
 
   channel->bounds_known = FALSE;
 
diff --git a/app/gegl/Makefile.am b/app/gegl/Makefile.am
index 645c6ed..59ee0b8 100644
--- a/app/gegl/Makefile.am
+++ b/app/gegl/Makefile.am
@@ -48,6 +48,8 @@ libappgegl_a_sources = \
 	gimpthresholdconfig.c			\
 	gimpthresholdconfig.h			\
 	\
+	gimpoperationborder.c			\
+	gimpoperationborder.h			\
 	gimpoperationcagecoefcalc.c		\
 	gimpoperationcagecoefcalc.h		\
 	gimpoperationcagetransform.c		\
diff --git a/app/gegl/gimp-gegl-types.h b/app/gegl/gimp-gegl-types.h
index d2aff4b..778053b 100644
--- a/app/gegl/gimp-gegl-types.h
+++ b/app/gegl/gimp-gegl-types.h
@@ -27,6 +27,7 @@
 
 /*  operations  */
 
+typedef struct _GimpOperationBorder             GimpOperationBorder;
 typedef struct _GimpOperationCageCoefCalc       GimpOperationCageCoefCalc;
 typedef struct _GimpOperationCageTransform      GimpOperationCageTransform;
 typedef struct _GimpOperationEqualize           GimpOperationEqualize;
diff --git a/app/gegl/gimp-gegl.c b/app/gegl/gimp-gegl.c
index 6e43360..1db74e3 100644
--- a/app/gegl/gimp-gegl.c
+++ b/app/gegl/gimp-gegl.c
@@ -32,6 +32,7 @@
 
 #include "gimp-gegl.h"
 
+#include "gimpoperationborder.h"
 #include "gimpoperationcagecoefcalc.h"
 #include "gimpoperationcagetransform.h"
 #include "gimpoperationequalize.h"
@@ -121,6 +122,7 @@ gimp_gegl_init (Gimp *gimp)
                    babl_component ("A"),
                    NULL);
 
+  g_type_class_ref (GIMP_TYPE_OPERATION_BORDER);
   g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_COEF_CALC);
   g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_TRANSFORM);
   g_type_class_ref (GIMP_TYPE_OPERATION_EQUALIZE);
diff --git a/app/gegl/gimpoperationborder.c b/app/gegl/gimpoperationborder.c
new file mode 100644
index 0000000..77cf147
--- /dev/null
+++ b/app/gegl/gimpoperationborder.c
@@ -0,0 +1,718 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationborder.c
+ * Copyright (C) 2012 Michael Natterer <mitch gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimp-gegl-types.h"
+
+#include "gimp-gegl-utils.h"
+#include "gimpoperationborder.h"
+
+
+enum
+{
+  PROP_0,
+  PROP_RADIUS_X,
+  PROP_RADIUS_Y,
+  PROP_FEATHER,
+  PROP_EDGE_LOCK
+};
+
+
+static void     gimp_operation_border_get_property (GObject      *object,
+                                                    guint         property_id,
+                                                    GValue       *value,
+                                                    GParamSpec   *pspec);
+static void     gimp_operation_border_set_property (GObject      *object,
+                                                    guint         property_id,
+                                                    const GValue *value,
+                                                    GParamSpec   *pspec);
+
+static GeglRectangle
+gimp_operation_border_get_required_for_output (GeglOperation       *self,
+                                               const gchar         *input_pad,
+                                               const GeglRectangle *roi);
+static GeglRectangle
+      gimp_operation_border_get_cached_region (GeglOperation       *self,
+                                               const GeglRectangle *roi);
+static void     gimp_operation_border_prepare (GeglOperation       *operation);
+static gboolean gimp_operation_border_process (GeglOperation       *operation,
+                                               GeglBuffer          *input,
+                                               GeglBuffer          *output,
+                                               const GeglRectangle *roi,
+                                               gint                 level);
+
+
+G_DEFINE_TYPE (GimpOperationBorder, gimp_operation_border,
+               GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_border_parent_class
+
+
+static void
+gimp_operation_border_class_init (GimpOperationBorderClass *klass)
+{
+  GObjectClass             *object_class    = G_OBJECT_CLASS (klass);
+  GeglOperationClass       *operation_class = GEGL_OPERATION_CLASS (klass);
+  GeglOperationFilterClass *filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  object_class->set_property   = gimp_operation_border_set_property;
+  object_class->get_property   = gimp_operation_border_get_property;
+
+  operation_class->name        = "gimp:border";
+  operation_class->categories  = "gimp";
+  operation_class->description = "GIMP Border operation";
+
+  operation_class->prepare                 = gimp_operation_border_prepare;
+  operation_class->get_required_for_output = gimp_operation_border_get_required_for_output;
+  operation_class->get_cached_region       = gimp_operation_border_get_cached_region;
+
+  filter_class->process        = gimp_operation_border_process;
+
+  g_object_class_install_property (object_class, PROP_RADIUS_X,
+                                   g_param_spec_int ("radius-x",
+                                                     "Radius X",
+                                                     "Border radius in X diection",
+                                                     1, 2342, 1,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_RADIUS_Y,
+                                   g_param_spec_int ("radius-y",
+                                                     "Radius Y",
+                                                     "Border radius in Y diection",
+                                                     1, 2342, 1,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_FEATHER,
+                                   g_param_spec_boolean ("feather",
+                                                         "Feather",
+                                                         "Feather the border",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_EDGE_LOCK,
+                                   g_param_spec_boolean ("edge-lock",
+                                                         "Edge Lock",
+                                                         "Shrink from border",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_border_init (GimpOperationBorder *self)
+{
+}
+
+static void
+gimp_operation_border_get_property (GObject    *object,
+                                    guint       property_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+ GimpOperationBorder *self = GIMP_OPERATION_BORDER (object);
+
+  switch (property_id)
+    {
+    case PROP_RADIUS_X:
+      g_value_set_int (value, self->radius_x);
+      break;
+
+    case PROP_RADIUS_Y:
+      g_value_set_int (value, self->radius_y);
+      break;
+
+    case PROP_FEATHER:
+      g_value_set_boolean (value, self->feather);
+      break;
+
+    case PROP_EDGE_LOCK:
+      g_value_set_boolean (value, self->edge_lock);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_operation_border_set_property (GObject      *object,
+                                    guint         property_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GimpOperationBorder *self = GIMP_OPERATION_BORDER (object);
+
+  switch (property_id)
+    {
+    case PROP_RADIUS_X:
+      self->radius_x = g_value_get_int (value);
+      break;
+
+    case PROP_RADIUS_Y:
+      self->radius_y = g_value_get_int (value);
+      break;
+
+    case PROP_FEATHER:
+      self->feather = g_value_get_boolean (value);
+      break;
+
+    case PROP_EDGE_LOCK:
+      self->edge_lock = g_value_get_boolean (value);
+      break;
+
+   default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_operation_border_prepare (GeglOperation *operation)
+{
+  gegl_operation_set_format (operation, "input",  babl_format ("Y u8"));
+  gegl_operation_set_format (operation, "output", babl_format ("Y u8"));
+}
+
+static GeglRectangle
+gimp_operation_border_get_required_for_output (GeglOperation       *self,
+                                               const gchar         *input_pad,
+                                               const GeglRectangle *roi)
+{
+  return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static GeglRectangle
+gimp_operation_border_get_cached_region (GeglOperation       *self,
+                                         const GeglRectangle *roi)
+{
+  return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static inline void
+rotate_pointers (guchar  **p,
+                 guint32   n)
+{
+  guint32  i;
+  guchar  *tmp;
+
+  tmp = p[0];
+
+  for (i = 0; i < n - 1; i++)
+    p[i] = p[i + 1];
+
+  p[i] = tmp;
+}
+
+/* Computes whether pixels in `buf[1]', if they are selected, have neighbouring
+   pixels that are unselected. Put result in `transition'. */
+static void
+compute_transition (guchar    *transition,
+                    guchar   **buf,
+                    gint32     width,
+                    gboolean   edge_lock)
+{
+  register gint32 x = 0;
+
+  if (width == 1)
+    {
+      if (buf[1][0] > 127 && (buf[0][0] < 128 || buf[2][0] < 128))
+        transition[0] = 255;
+      else
+        transition[0] = 0;
+      return;
+    }
+
+  if (buf[1][0] > 127 && edge_lock)
+    {
+      /* The pixel to the left (outside of the canvas) is considered selected,
+         so we check if there are any unselected pixels in neighbouring pixels
+         _on_ the canvas. */
+      if (buf[0][x] < 128 || buf[0][x + 1] < 128 ||
+                             buf[1][x + 1] < 128 ||
+          buf[2][x] < 128 || buf[2][x + 1] < 128 )
+        {
+          transition[x] = 255;
+        }
+      else
+        {
+          transition[x] = 0;
+        }
+    }
+  else if (buf[1][0] > 127 && !edge_lock)
+    {
+      /* We must not care about neighbouring pixels on the image canvas since
+         there always are unselected pixels to the left (which is outside of
+         the image canvas). */
+      transition[x] = 255;
+    }
+  else
+    {
+      transition[x] = 0;
+    }
+
+  for (x = 1; x < width - 1; x++)
+    {
+      if (buf[1][x] >= 128)
+        {
+          if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
+              buf[1][x - 1] < 128 ||                    buf[1][x + 1] < 128 ||
+              buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
+            transition[x] = 255;
+          else
+            transition[x] = 0;
+        }
+      else
+        {
+          transition[x] = 0;
+        }
+    }
+
+  if (buf[1][width - 1] >= 128 && edge_lock)
+    {
+      /* The pixel to the right (outside of the canvas) is considered selected,
+         so we check if there are any unselected pixels in neighbouring pixels
+         _on_ the canvas. */
+      if ( buf[0][x - 1] < 128 || buf[0][x] < 128 ||
+           buf[1][x - 1] < 128 ||
+           buf[2][x - 1] < 128 || buf[2][x] < 128)
+        {
+          transition[width - 1] = 255;
+        }
+      else
+        {
+          transition[width - 1] = 0;
+        }
+    }
+  else if (buf[1][width - 1] >= 128 && !edge_lock)
+    {
+      /* We must not care about neighbouring pixels on the image canvas since
+         there always are unselected pixels to the right (which is outside of
+         the image canvas). */
+      transition[width - 1] = 255;
+    }
+  else
+    {
+      transition[width - 1] = 0;
+    }
+}
+
+static gboolean
+gimp_operation_border_process (GeglOperation       *operation,
+                               GeglBuffer          *input,
+                               GeglBuffer          *output,
+                               const GeglRectangle *roi,
+                               gint                 level)
+{
+  /* This function has no bugs, but if you imagine some you can blame
+   * them on jaycox gimp org
+   */
+  GimpOperationBorder *self          = GIMP_OPERATION_BORDER (operation);
+  const Babl          *input_format  = babl_format ("Y u8");
+  const Babl          *output_format = babl_format ("Y u8");
+
+  gint32 i, j, x, y;
+
+  /* A cache used in the algorithm as it works its way down. `buf[1]' is the
+     current row. Thus, at algorithm initialization, `buf[0]' represents the
+     row 'above' the first row of the region. */
+  guchar  *buf[3];
+
+  /* The resulting selection is calculated row by row, and this buffer holds the
+     output for each individual row, on each iteration. */
+  guchar  *out;
+
+  /* Keeps track of transitional pixels (pixels that are selected and have
+     unselected neighbouring pixels). */
+  guchar **transition;
+
+  /* TODO: Figure out role clearly in algorithm. */
+  gint16  *max;
+
+  /* TODO: Figure out role clearly in algorithm. */
+  guchar **density;
+
+  gint16   last_index;
+
+  /* optimize this case specifically */
+  if (self->radius_x == 1 && self->radius_y == 1)
+    {
+      guchar *transition;
+      guchar *source[3];
+
+      for (i = 0; i < 3; i++)
+        source[i] = g_new (guchar, roi->width);
+
+      transition = g_new (guchar, roi->width);
+
+      /* With `self->edge_lock', initialize row above image as
+       * selected, otherwise, initialize as unselected.
+       */
+      memset (source[0], self->edge_lock ? 255 : 0, roi->width);
+
+      gegl_buffer_get (input,
+                       GIMP_GEGL_RECT (roi->x, roi->y + 0,
+                                       roi->width, 1),
+                       1.0, input_format, source[1],
+                       GEGL_AUTO_ROWSTRIDE);
+
+      if (roi->height > 1)
+        gegl_buffer_get (input,
+                         GIMP_GEGL_RECT (roi->x, roi->y + 1,
+                                         roi->width, 1),
+                         1.0, input_format, source[2],
+                         GEGL_AUTO_ROWSTRIDE);
+      else
+        memcpy (source[2], source[1], roi->width);
+
+      compute_transition (transition, source, roi->width, self->edge_lock);
+      gegl_buffer_set (output,
+                       GIMP_GEGL_RECT (roi->x, roi->y,
+                                       roi->width, 1),
+                       1.0, output_format, transition,
+                       GEGL_AUTO_ROWSTRIDE);
+
+      for (y = 1; y < roi->height; y++)
+        {
+          rotate_pointers (source, 3);
+
+          if (y + 1 < roi->height)
+            {
+              gegl_buffer_get (input,
+                               GIMP_GEGL_RECT (roi->x, roi->y + y + 1,
+                                               roi->width, 1),
+                               1.0, input_format, source[2],
+                               GEGL_AUTO_ROWSTRIDE);
+            }
+          else
+            {
+              /* Depending on `self->edge_lock', set the row below the
+               * image as either selected or non-selected.
+               */
+              memset(source[2], self->edge_lock ? 255 : 0, roi->width);
+            }
+
+          compute_transition (transition, source, roi->width, self->edge_lock);
+          gegl_buffer_set (output,
+                           GIMP_GEGL_RECT (roi->x, roi->y + y,
+                                           roi->width, 1),
+                           1.0, output_format, transition,
+                           GEGL_AUTO_ROWSTRIDE);
+        }
+
+      for (i = 0; i < 3; i++)
+        g_free (source[i]);
+
+      g_free (transition);
+
+      /* Finnished handling the radius = 1 special case, return here. */
+      return TRUE;
+    }
+
+  max = g_new (gint16, roi->width + 2 * self->radius_x);
+
+  for (i = 0; i < (roi->width + 2 * self->radius_x); i++)
+    max[i] = self->radius_y + 2;
+
+  max += self->radius_x;
+
+  for (i = 0; i < 3; i++)
+    buf[i] = g_new (guchar, roi->width);
+
+  transition = g_new (guchar *, self->radius_y + 1);
+
+  for (i = 0; i < self->radius_y + 1; i++)
+    {
+      transition[i] = g_new (guchar, roi->width + 2 * self->radius_x);
+      memset(transition[i], 0, roi->width + 2 * self->radius_x);
+      transition[i] += self->radius_x;
+    }
+
+  out = g_new (guchar, roi->width);
+
+  density = g_new (guchar *, 2 * self->radius_x + 1);
+  density += self->radius_x;
+
+   /* allocate density[][] */
+  for (x = 0; x < (self->radius_x + 1); x++)
+    {
+      density[ x]  = g_new (guchar, 2 * self->radius_y + 1);
+      density[ x] += self->radius_y;
+      density[-x]  = density[x];
+    }
+
+  /* compute density[][] */
+  for (x = 0; x < (self->radius_x + 1); x++)
+    {
+      register gdouble tmpx, tmpy, dist;
+      guchar a;
+
+      if (x > 0)
+        tmpx = x - 0.5;
+      else if (x < 0)
+        tmpx = x + 0.5;
+      else
+        tmpx = 0.0;
+
+      for (y = 0; y < (self->radius_y + 1); y++)
+        {
+          if (y > 0)
+            tmpy = y - 0.5;
+          else if (y < 0)
+            tmpy = y + 0.5;
+          else
+            tmpy = 0.0;
+
+          dist = ((tmpy * tmpy) / (self->radius_y * self->radius_y) +
+                  (tmpx * tmpx) / (self->radius_x * self->radius_x));
+
+          if (dist < 1.0)
+            {
+              if (self->feather)
+                a = 255 * (1.0 - sqrt (dist));
+              else
+                a = 255;
+            }
+          else
+            {
+              a = 0;
+            }
+
+          density[ x][ y] = a;
+          density[ x][-y] = a;
+          density[-x][ y] = a;
+          density[-x][-y] = a;
+        }
+    }
+
+  /* Since the algorithm considerers `buf[0]' to be 'over' the row
+   * currently calculated, we must start with `buf[0]' as non-selected
+   * if there is no `self->edge_lock. If there is an
+   * 'self->edge_lock', initialize the first row to 'selected'. Refer
+   * to bug #350009.
+   */
+  memset (buf[0], self->edge_lock ? 255 : 0, roi->width);
+  gegl_buffer_get (input,
+                   GIMP_GEGL_RECT (roi->x, roi->y + 0,
+                                   roi->width, 1),
+                   1.0, input_format, buf[1],
+                   GEGL_AUTO_ROWSTRIDE);
+
+  if (roi->height > 1)
+    gegl_buffer_get (input,
+                     GIMP_GEGL_RECT (roi->x, roi->y + 1,
+                                     roi->width, 1),
+                     1.0, input_format, buf[2],
+                     GEGL_AUTO_ROWSTRIDE);
+  else
+    memcpy (buf[2], buf[1], roi->width);
+
+  compute_transition (transition[1], buf, roi->width, self->edge_lock);
+
+   /* set up top of image */
+  for (y = 1; y < self->radius_y && y + 1 < roi->height; y++)
+    {
+      rotate_pointers (buf, 3);
+      gegl_buffer_get (input,
+                       GIMP_GEGL_RECT (roi->x, roi->y + y + 1,
+                                       roi->width, 1),
+                       1.0, input_format, buf[2],
+                       GEGL_AUTO_ROWSTRIDE);
+      compute_transition (transition[y + 1], buf, roi->width, self->edge_lock);
+    }
+
+  /* set up max[] for top of image */
+  for (x = 0; x < roi->width; x++)
+    {
+      max[x] = -(self->radius_y + 7);
+
+      for (j = 1; j < self->radius_y + 1; j++)
+        if (transition[j][x])
+          {
+            max[x] = j;
+            break;
+          }
+    }
+
+  /* main calculation loop */
+  for (y = 0; y < roi->height; y++)
+    {
+      rotate_pointers (buf, 3);
+      rotate_pointers (transition, self->radius_y + 1);
+
+      if (y < roi->height - (self->radius_y + 1))
+        {
+          gegl_buffer_get (input,
+                           GIMP_GEGL_RECT (roi->x,
+                                           roi->y + y + self->radius_y + 1,
+                                           roi->width, 1),
+                           1.0, input_format, buf[2],
+                           GEGL_AUTO_ROWSTRIDE);
+          compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock);
+        }
+      else
+        {
+          if (self->edge_lock)
+            {
+              memcpy (transition[self->radius_y], transition[self->radius_y - 1], roi->width);
+            }
+          else
+            {
+              /* No edge lock, set everything 'below canvas' as seen
+               * from the algorithm as unselected.
+               */
+              memset (buf[2], 0, roi->width);
+              compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock);
+            }
+        }
+
+      /* update max array */
+      for (x = 0; x < roi->width; x++)
+        {
+          if (max[x] < 1)
+            {
+              if (max[x] <= -self->radius_y)
+                {
+                  if (transition[self->radius_y][x])
+                    max[x] = self->radius_y;
+                  else
+                    max[x]--;
+                }
+              else
+                {
+                  if (transition[-max[x]][x])
+                    max[x] = -max[x];
+                  else if (transition[-max[x] + 1][x])
+                    max[x] = -max[x] + 1;
+                  else
+                    max[x]--;
+                }
+            }
+          else
+            {
+              max[x]--;
+            }
+
+          if (max[x] < -self->radius_y - 1)
+            max[x] = -self->radius_y - 1;
+        }
+
+      last_index = 1;
+
+       /* render scan line */
+      for (x = 0 ; x < roi->width; x++)
+        {
+          guchar last_max;
+
+          last_index--;
+
+          if (last_index >= 0)
+            {
+              last_max = 0;
+
+              for (i = self->radius_x; i >= 0; i--)
+                if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y &&
+                    density[i][max[x+i]] > last_max)
+                  {
+                    last_max = density[i][max[x + i]];
+                    last_index = i;
+                  }
+
+              out[x] = last_max;
+            }
+          else
+            {
+              last_max = 0;
+
+              for (i = self->radius_x; i >= -self->radius_x; i--)
+                if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y &&
+                    density[i][max[x + i]] > last_max)
+                  {
+                    last_max = density[i][max[x + i]];
+                    last_index = i;
+                  }
+
+              out[x] = last_max;
+            }
+
+          if (last_max == 0)
+            {
+              for (i = x + 1; i < roi->width; i++)
+                {
+                  if (max[i] >= -self->radius_y)
+                    break;
+                }
+
+              if (i - x > self->radius_x)
+                {
+                  for (; x < i - self->radius_x; x++)
+                    out[x] = 0;
+
+                  x--;
+                }
+
+              last_index = self->radius_x;
+            }
+        }
+
+      gegl_buffer_set (output,
+                       GIMP_GEGL_RECT (roi->x, roi->y + y,
+                                       roi->width, 1),
+                       1.0, output_format, out,
+                       GEGL_AUTO_ROWSTRIDE);
+    }
+
+  g_free (out);
+
+  for (i = 0; i < 3; i++)
+    g_free (buf[i]);
+
+  max -= self->radius_x;
+  g_free (max);
+
+  for (i = 0; i < self->radius_y + 1; i++)
+    {
+      transition[i] -= self->radius_x;
+      g_free (transition[i]);
+    }
+
+  g_free (transition);
+
+  for (i = 0; i < self->radius_x + 1 ; i++)
+    {
+      density[i] -= self->radius_y;
+      g_free (density[i]);
+    }
+
+  density -= self->radius_x;
+  g_free (density);
+
+  return TRUE;
+}
diff --git a/app/gegl/gimpoperationborder.h b/app/gegl/gimpoperationborder.h
new file mode 100644
index 0000000..72e3b13
--- /dev/null
+++ b/app/gegl/gimpoperationborder.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationborder.h
+ * Copyright (C) 2012 Michael Natterer <mitch gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_OPERATION_BORDER_H__
+#define __GIMP_OPERATION_BORDER_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_BORDER            (gimp_operation_border_get_type ())
+#define GIMP_OPERATION_BORDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorder))
+#define GIMP_OPERATION_BORDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass))
+#define GIMP_IS_OPERATION_BORDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BORDER))
+#define GIMP_IS_OPERATION_BORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GIMP_TYPE_OPERATION_BORDER))
+#define GIMP_OPERATION_BORDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass))
+
+
+typedef struct _GimpOperationBorderClass GimpOperationBorderClass;
+
+struct _GimpOperationBorder
+{
+  GeglOperationFilter  parent_instance;
+
+  gint                 radius_x;
+  gint                 radius_y;
+  gboolean             feather;
+  gboolean             edge_lock;
+};
+
+struct _GimpOperationBorderClass
+{
+  GeglOperationFilterClass  parent_class;
+};
+
+
+GType   gimp_operation_border_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BORDER_H__ */
diff --git a/app/paint-funcs/paint-funcs.c b/app/paint-funcs/paint-funcs.c
index 58c022f..666a2d8 100644
--- a/app/paint-funcs/paint-funcs.c
+++ b/app/paint-funcs/paint-funcs.c
@@ -2254,460 +2254,6 @@ dilate_region (PixelRegion *region)
   g_free (out);
 }
 
-/* Computes whether pixels in `buf[1]', if they are selected, have neighbouring
-   pixels that are unselected. Put result in `transition'. */
-static void
-compute_transition (guchar    *transition,
-                    guchar   **buf,
-                    gint32     width,
-                    gboolean   edge_lock)
-{
-  register gint32 x = 0;
-
-  if (width == 1)
-    {
-      if (buf[1][0] > 127 && (buf[0][0] < 128 || buf[2][0] < 128))
-        transition[0] = 255;
-      else
-        transition[0] = 0;
-      return;
-    }
-
-  if (buf[1][0] > 127 && edge_lock)
-    {
-      /* The pixel to the left (outside of the canvas) is considered selected,
-         so we check if there are any unselected pixels in neighbouring pixels
-         _on_ the canvas. */
-      if (buf[0][x] < 128 || buf[0][x + 1] < 128 ||
-                             buf[1][x + 1] < 128 ||
-          buf[2][x] < 128 || buf[2][x + 1] < 128 )
-        {
-          transition[x] = 255;
-        }
-      else
-        {
-          transition[x] = 0;
-        }
-    }
-  else if (buf[1][0] > 127 && !edge_lock)
-    {
-      /* We must not care about neighbouring pixels on the image canvas since
-         there always are unselected pixels to the left (which is outside of
-         the image canvas). */
-      transition[x] = 255;
-    }
-  else
-    {
-      transition[x] = 0;
-    }
-
-  for (x = 1; x < width - 1; x++)
-    {
-      if (buf[1][x] >= 128)
-        {
-          if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
-              buf[1][x - 1] < 128 ||                    buf[1][x + 1] < 128 ||
-              buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
-            transition[x] = 255;
-          else
-            transition[x] = 0;
-        }
-      else
-        {
-          transition[x] = 0;
-        }
-    }
-
-  if (buf[1][width - 1] >= 128 && edge_lock)
-    {
-      /* The pixel to the right (outside of the canvas) is considered selected,
-         so we check if there are any unselected pixels in neighbouring pixels
-         _on_ the canvas. */
-      if ( buf[0][x - 1] < 128 || buf[0][x] < 128 ||
-           buf[1][x - 1] < 128 ||
-           buf[2][x - 1] < 128 || buf[2][x] < 128)
-        {
-          transition[width - 1] = 255;
-        }
-      else
-        {
-          transition[width - 1] = 0;
-        }
-    }
-  else if (buf[1][width - 1] >= 128 && !edge_lock)
-    {
-      /* We must not care about neighbouring pixels on the image canvas since
-         there always are unselected pixels to the right (which is outside of
-         the image canvas). */
-      transition[width - 1] = 255;
-    }
-  else
-    {
-      transition[width - 1] = 0;
-    }
-}
-
-void
-border_region (PixelRegion *src,
-               gint16       xradius,
-               gint16       yradius,
-               gboolean     feather,
-               gboolean     edge_lock)
-{
-  /*
-     This function has no bugs, but if you imagine some you can
-     blame them on jaycox gimp org
-  */
-
-  register gint32 i, j, x, y;
-
-  /* A cache used in the algorithm as it works its way down. `buf[1]' is the
-     current row. Thus, at algorithm initialization, `buf[0]' represents the
-     row 'above' the first row of the region. */
-  guchar  *buf[3];
-
-  /* The resulting selection is calculated row by row, and this buffer holds the
-     output for each individual row, on each iteration. */
-  guchar  *out;
-
-  /* Keeps track of transitional pixels (pixels that are selected and have
-     unselected neighbouring pixels). */
-  guchar **transition;
-
-  /* TODO: Figure out role clearly in algorithm. */
-  gint16  *max;
-
-  /* TODO: Figure out role clearly in algorithm. */
-  guchar **density;
-
-  gint16   last_index;
-
-  if (xradius < 0 || yradius < 0)
-    {
-      g_warning ("border_region: negative radius specified.");
-      return;
-    }
-
-  /* A border without a width is no border at all; return an empty region. */
-  if (xradius == 0 || yradius == 0)
-    {
-      guchar color[] = "\0\0\0\0";
-
-      color_region (src, color);
-      return;
-    }
-
-  /* optimize this case specifically */
-  if (xradius == 1 && yradius == 1)
-    {
-      guchar *transition;
-      guchar *source[3];
-
-      for (i = 0; i < 3; i++)
-        source[i] = g_new (guchar, src->w);
-
-      transition = g_new (guchar, src->w);
-
-      /* With `edge_lock', initialize row above image as selected, otherwise,
-         initialize as unselected. */
-      memset (source[0], edge_lock ? 255 : 0, src->w);
-
-      pixel_region_get_row (src, src->x, src->y + 0, src->w, source[1], 1);
-
-      if (src->h > 1)
-        pixel_region_get_row (src, src->x, src->y + 1, src->w, source[2], 1);
-      else
-        memcpy (source[2], source[1], src->w);
-
-      compute_transition (transition, source, src->w, edge_lock);
-      pixel_region_set_row (src, src->x, src->y , src->w, transition);
-
-      for (y = 1; y < src->h; y++)
-        {
-          rotate_pointers (source, 3);
-
-          if (y + 1 < src->h)
-            {
-              pixel_region_get_row (src, src->x, src->y + y + 1, src->w,
-                                    source[2], 1);
-            }
-          else
-            {
-              /* Depending on `edge_lock', set the row below the image as either
-                 selected or non-selected. */
-              memset(source[2], edge_lock ? 255 : 0, src->w);
-            }
-
-          compute_transition (transition, source, src->w, edge_lock);
-          pixel_region_set_row (src, src->x, src->y + y, src->w, transition);
-        }
-
-      for (i = 0; i < 3; i++)
-        g_free (source[i]);
-
-      g_free (transition);
-
-      /* Finnished handling the radius = 1 special case, return here. */
-      return;
-    }
-
-  max = g_new (gint16, src->w + 2 * xradius);
-
-  for (i = 0; i < (src->w + 2 * xradius); i++)
-    max[i] = yradius + 2;
-
-  max += xradius;
-
-  for (i = 0; i < 3; i++)
-    buf[i] = g_new (guchar, src->w);
-
-  transition = g_new (guchar *, yradius + 1);
-
-  for (i = 0; i < yradius + 1; i++)
-    {
-      transition[i] = g_new (guchar, src->w + 2 * xradius);
-      memset(transition[i], 0, src->w + 2 * xradius);
-      transition[i] += xradius;
-    }
-
-  out = g_new (guchar, src->w);
-
-  density = g_new (guchar *, 2 * xradius + 1);
-  density += xradius;
-
-   /* allocate density[][] */
-  for (x = 0; x < (xradius + 1); x++)
-    {
-      density[ x]  = g_new (guchar, 2 * yradius + 1);
-      density[ x] += yradius;
-      density[-x]  = density[x];
-    }
-
-  /* compute density[][] */
-  for (x = 0; x < (xradius + 1); x++)
-    {
-      register gdouble tmpx, tmpy, dist;
-      guchar a;
-
-      if (x > 0)
-        tmpx = x - 0.5;
-      else if (x < 0)
-        tmpx = x + 0.5;
-      else
-        tmpx = 0.0;
-
-      for (y = 0; y < (yradius + 1); y++)
-        {
-          if (y > 0)
-            tmpy = y - 0.5;
-          else if (y < 0)
-            tmpy = y + 0.5;
-          else
-            tmpy = 0.0;
-
-          dist = ((tmpy * tmpy) / (yradius * yradius) +
-                  (tmpx * tmpx) / (xradius * xradius));
-
-          if (dist < 1.0)
-            {
-              if (feather)
-                a = 255 * (1.0 - sqrt (dist));
-              else
-                a = 255;
-            }
-          else
-            {
-              a = 0;
-            }
-
-          density[ x][ y] = a;
-          density[ x][-y] = a;
-          density[-x][ y] = a;
-          density[-x][-y] = a;
-        }
-    }
-
-  /* Since the algorithm considerers `buf[0]' to be 'over' the row currently
-     calculated, we must start with `buf[0]' as non-selected if there is no
-     `edge_lock. If there is an 'edge_lock', initialize the first row to
-     'selected'. Refer to bug #350009. */
-  memset (buf[0], edge_lock ? 255 : 0, src->w);
-  pixel_region_get_row (src, src->x, src->y + 0, src->w, buf[1], 1);
-
-  if (src->h > 1)
-    pixel_region_get_row (src, src->x, src->y + 1, src->w, buf[2], 1);
-  else
-    memcpy (buf[2], buf[1], src->w);
-
-  compute_transition (transition[1], buf, src->w, edge_lock);
-
-   /* set up top of image */
-  for (y = 1; y < yradius && y + 1 < src->h; y++)
-    {
-      rotate_pointers (buf, 3);
-      pixel_region_get_row (src, src->x, src->y + y + 1, src->w, buf[2], 1);
-      compute_transition (transition[y + 1], buf, src->w, edge_lock);
-    }
-
-  /* set up max[] for top of image */
-  for (x = 0; x < src->w; x++)
-    {
-      max[x] = -(yradius + 7);
-
-      for (j = 1; j < yradius + 1; j++)
-        if (transition[j][x])
-          {
-            max[x] = j;
-            break;
-          }
-    }
-
-  /* main calculation loop */
-  for (y = 0; y < src->h; y++)
-    {
-      rotate_pointers (buf, 3);
-      rotate_pointers (transition, yradius + 1);
-
-      if (y < src->h - (yradius + 1))
-        {
-          pixel_region_get_row (src, src->x, src->y + y + yradius + 1, src->w,
-                                buf[2], 1);
-          compute_transition (transition[yradius], buf, src->w, edge_lock);
-        }
-      else
-        {
-          if (edge_lock)
-            {
-              memcpy (transition[yradius], transition[yradius - 1], src->w);
-            }
-          else
-            {
-              /* No edge lock, set everything 'below canvas' as seen from the
-                 algorithm as unselected. */
-              memset (buf[2], 0, src->w);
-              compute_transition (transition[yradius], buf, src->w, edge_lock);
-            }
-        }
-
-       /* update max array */
-      for (x = 0; x < src->w; x++)
-        {
-          if (max[x] < 1)
-            {
-              if (max[x] <= -yradius)
-                {
-                  if (transition[yradius][x])
-                    max[x] = yradius;
-                  else
-                    max[x]--;
-                }
-              else
-                {
-                  if (transition[-max[x]][x])
-                    max[x] = -max[x];
-                  else if (transition[-max[x] + 1][x])
-                    max[x] = -max[x] + 1;
-                  else
-                    max[x]--;
-                }
-            }
-          else
-            {
-              max[x]--;
-            }
-
-          if (max[x] < -yradius - 1)
-            max[x] = -yradius - 1;
-        }
-
-      last_index = 1;
-
-       /* render scan line */
-      for (x = 0 ; x < src->w; x++)
-        {
-          guchar last_max;
-
-          last_index--;
-
-          if (last_index >= 0)
-            {
-              last_max = 0;
-
-              for (i = xradius; i >= 0; i--)
-                if (max[x + i] <= yradius && max[x + i] >= -yradius &&
-                    density[i][max[x+i]] > last_max)
-                  {
-                    last_max = density[i][max[x + i]];
-                    last_index = i;
-                  }
-
-              out[x] = last_max;
-            }
-          else
-            {
-              last_max = 0;
-
-              for (i = xradius; i >= -xradius; i--)
-                if (max[x + i] <= yradius && max[x + i] >= -yradius &&
-                    density[i][max[x + i]] > last_max)
-                  {
-                    last_max = density[i][max[x + i]];
-                    last_index = i;
-                  }
-
-              out[x] = last_max;
-            }
-
-          if (last_max == 0)
-            {
-              for (i = x + 1; i < src->w; i++)
-                {
-                  if (max[i] >= -yradius)
-                    break;
-                }
-
-              if (i - x > xradius)
-                {
-                  for (; x < i - xradius; x++)
-                    out[x] = 0;
-
-                  x--;
-                }
-
-              last_index = xradius;
-            }
-        }
-
-      pixel_region_set_row (src, src->x, src->y + y, src->w, out);
-    }
-
-  g_free (out);
-
-  for (i = 0; i < 3; i++)
-    g_free (buf[i]);
-
-  max -= xradius;
-  g_free (max);
-
-  for (i = 0; i < yradius + 1; i++)
-    {
-      transition[i] -= xradius;
-      g_free (transition[i]);
-    }
-
-  g_free (transition);
-
-  for (i = 0; i < xradius + 1 ; i++)
-    {
-      density[i] -= yradius;
-      g_free (density[i]);
-    }
-
-  density -= xradius;
-  g_free (density);
-}
-
-
 /* Computes whether pixels in `buf[1]' have neighbouring pixels that are
    unselected. Put result in `transition'. */
 static void
diff --git a/app/paint-funcs/paint-funcs.h b/app/paint-funcs/paint-funcs.h
index 4981ce6..2d93e48 100644
--- a/app/paint-funcs/paint-funcs.h
+++ b/app/paint-funcs/paint-funcs.h
@@ -285,12 +285,6 @@ void  convolve_region                     (PixelRegion         *srcR,
                                            GimpConvolutionType  mode,
                                            gboolean             alpha_weighting);
 
-void  border_region                       (PixelRegion *src,
-                                           gint16       xradius,
-                                           gint16       yradius,
-                                           gboolean     feather,
-                                           gboolean     edge_lock);
-
 gfloat shapeburst_region                  (PixelRegion      *srcPR,
                                            PixelRegion      *distPR,
                                            GimpProgressFunc  progress_callback,



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