[gimp/gimp-2-10] app: improve gimp_gegl_mask_combine_ellipse[_rect]()



commit 9b76ea070d2b8da7185beb70de7d467ab60579db
Author: Ell <ell_se yahoo com>
Date:   Wed Mar 20 09:27:08 2019 -0400

    app: improve gimp_gegl_mask_combine_ellipse[_rect]()
    
    Improve gimp_gegl_mask_combine_ellipse_rect() -- the funciton
    responsible for rendering ellipse/rounded-rectangle selections.
    
    Most notably, this commit significantly improves the function's
    performance, by identifying whole tiles, whole rows, or parts of a
    row, that are fully inside, or fully outside, the ellipse, and
    filling them in bulk, instead of calculating the anti-aliasing
    value at each pixel, which is now only done along the
    circumference.
    
    This commit also improves anti-aliasing, by more accurately
    approximating the distance from a pixel to the ellipse, and by
    normalizing the distance according to the pixel's cross-section
    length in the direction of the said point.  In particular, we
    guarantee that pixels that are fully inside/outside the ellipse
    have a value of 1/0, respectively, facilitating the aforementioned
    optimization.
    
    Additionally, this commit fixes various edge cases where several
    primitives coincide at a single pixel (in the rounded-rectangle
    case), adds support for CHANNEL_OP_INTERSECT, and parallelizes
    processing.
    
    (cherry picked from commit 1044342393e1da6048b948f3a02f70acd591dfae)

 app/core/gimpchannel-combine.c     |  45 +--
 app/core/gimpchannel-combine.h     |   4 +-
 app/gegl/gimp-gegl-mask-combine.cc | 600 ++++++++++++++++++++++---------------
 app/gegl/gimp-gegl-mask-combine.h  |   4 +-
 4 files changed, 365 insertions(+), 288 deletions(-)
---
diff --git a/app/core/gimpchannel-combine.c b/app/core/gimpchannel-combine.c
index 5277bff625..69118242b7 100644
--- a/app/core/gimpchannel-combine.c
+++ b/app/core/gimpchannel-combine.c
@@ -89,24 +89,6 @@ gimp_channel_combine_rect (GimpChannel    *mask,
   gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h);
 }
 
-/**
- * gimp_channel_combine_ellipse:
- * @mask:      the channel with which to combine the ellipse
- * @op:        whether to replace, add to, or subtract from the current
- *             contents
- * @x:         x coordinate of upper left corner of ellipse
- * @y:         y coordinate of upper left corner of ellipse
- * @w:         width of ellipse bounding box
- * @h:         height of ellipse bounding box
- * @antialias: if %TRUE, antialias the ellipse
- *
- * Mainly used for elliptical selections.  If @op is
- * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels
- * within the ellipse to 255.  If @op is %GIMP_CHANNEL_OP_SUBTRACT,
- * sets pixels within to zero.  If @antialias is %TRUE, pixels that
- * impinge on the edge of the ellipse are set to intermediate values,
- * depending on how much they overlap.
- **/
 void
 gimp_channel_combine_ellipse (GimpChannel    *mask,
                               GimpChannelOps  op,
@@ -120,26 +102,6 @@ gimp_channel_combine_ellipse (GimpChannel    *mask,
                                      w / 2.0, h / 2.0, antialias);
 }
 
-/**
- * gimp_channel_combine_ellipse_rect:
- * @mask:      the channel with which to combine the elliptic rect
- * @op:        whether to replace, add to, or subtract from the current
- *             contents
- * @x:         x coordinate of upper left corner of bounding rect
- * @y:         y coordinate of upper left corner of bounding rect
- * @w:         width of bounding rect
- * @h:         height of bounding rect
- * @a:         elliptic a-constant applied to corners
- * @b:         elliptic b-constant applied to corners
- * @antialias: if %TRUE, antialias the elliptic corners
- *
- * Used for rounded cornered rectangles and ellipses.  If @op is
- * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels
- * within the ellipse to 255.  If @op is %GIMP_CHANNEL_OP_SUBTRACT,
- * sets pixels within to zero.  If @antialias is %TRUE, pixels that
- * impinge on the edge of the ellipse are set to intermediate values,
- * depending on how much they overlap.
- **/
 void
 gimp_channel_combine_ellipse_rect (GimpChannel    *mask,
                                    GimpChannelOps  op,
@@ -147,20 +109,19 @@ gimp_channel_combine_ellipse_rect (GimpChannel    *mask,
                                    gint            y,
                                    gint            w,
                                    gint            h,
-                                   gdouble         a,
-                                   gdouble         b,
+                                   gdouble         rx,
+                                   gdouble         ry,
                                    gboolean        antialias)
 {
   GeglBuffer *buffer;
 
   g_return_if_fail (GIMP_IS_CHANNEL (mask));
-  g_return_if_fail (a >= 0.0 && b >= 0.0);
   g_return_if_fail (op != GIMP_CHANNEL_OP_INTERSECT);
 
   buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
 
   if (! gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h,
-                                             a, b, antialias))
+                                             rx, ry, antialias))
     return;
 
   gimp_rectangle_intersect (x, y, w, h,
diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h
index 55648bf14b..de1f125c5c 100644
--- a/app/core/gimpchannel-combine.h
+++ b/app/core/gimpchannel-combine.h
@@ -38,8 +38,8 @@ void   gimp_channel_combine_ellipse_rect (GimpChannel    *mask,
                                           gint            y,
                                           gint            w,
                                           gint            h,
-                                          gdouble         a,
-                                          gdouble         b,
+                                          gdouble         rx,
+                                          gdouble         ry,
                                           gboolean        antialias);
 void   gimp_channel_combine_mask         (GimpChannel    *mask,
                                           GimpChannel    *add_on,
diff --git a/app/gegl/gimp-gegl-mask-combine.cc b/app/gegl/gimp-gegl-mask-combine.cc
index 9681a9aa00..e025759707 100644
--- a/app/gegl/gimp-gegl-mask-combine.cc
+++ b/app/gegl/gimp-gegl-mask-combine.cc
@@ -34,6 +34,12 @@ extern "C"
 #include "gimp-gegl-mask-combine.h"
 
 
+#define EPSILON 1e-6
+
+#define PIXELS_PER_THREAD \
+  (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
+
+
 gboolean
 gimp_gegl_mask_combine_rect (GeglBuffer     *mask,
                              GimpChannelOps  op,
@@ -75,24 +81,6 @@ gimp_gegl_mask_combine_rect (GeglBuffer     *mask,
   return TRUE;
 }
 
-/**
- * gimp_gegl_mask_combine_ellipse:
- * @mask:      the channel with which to combine the ellipse
- * @op:        whether to replace, add to, or subtract from the current
- *             contents
- * @x:         x coordinate of upper left corner of ellipse
- * @y:         y coordinate of upper left corner of ellipse
- * @w:         width of ellipse bounding box
- * @h:         height of ellipse bounding box
- * @antialias: if %TRUE, antialias the ellipse
- *
- * Mainly used for elliptical selections.  If @op is
- * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels
- * within the ellipse to 255.  If @op is %GIMP_CHANNEL_OP_SUBTRACT,
- * sets pixels within to zero.  If @antialias is %TRUE, pixels that
- * impinge on the edge of the ellipse are set to intermediate values,
- * depending on how much they overlap.
- **/
 gboolean
 gimp_gegl_mask_combine_ellipse (GeglBuffer     *mask,
                                 GimpChannelOps  op,
@@ -106,77 +94,6 @@ gimp_gegl_mask_combine_ellipse (GeglBuffer     *mask,
                                               w / 2.0, h / 2.0, antialias);
 }
 
-static void
-gimp_gegl_mask_combine_span (gfloat         *data,
-                             GimpChannelOps  op,
-                             gint            x1,
-                             gint            x2,
-                             gfloat          value)
-{
-  if (x2 <= x1)
-    return;
-
-  switch (op)
-    {
-    case GIMP_CHANNEL_OP_ADD:
-    case GIMP_CHANNEL_OP_REPLACE:
-      if (value == 1.0)
-        {
-          while (x1 < x2)
-            data[x1++] = 1.0;
-        }
-      else
-        {
-          while (x1 < x2)
-            {
-              const gfloat val = data[x1] + value;
-              data[x1++] = val > 1.0 ? 1.0 : val;
-            }
-        }
-      break;
-
-    case GIMP_CHANNEL_OP_SUBTRACT:
-      if (value == 1.0)
-        {
-          while (x1 < x2)
-            data[x1++] = 0.0;
-        }
-      else
-        {
-          while (x1 < x2)
-            {
-              const gfloat val = data[x1] - value;
-              data[x1++] = val > 0.0 ? val : 0.0;
-            }
-        }
-      break;
-
-    case GIMP_CHANNEL_OP_INTERSECT:
-      /* Should not happen */
-      break;
-    }
-}
-
-/**
- * gimp_gegl_mask_combine_ellipse_rect:
- * @mask:      the channel with which to combine the elliptic rect
- * @op:        whether to replace, add to, or subtract from the current
- *             contents
- * @x:         x coordinate of upper left corner of bounding rect
- * @y:         y coordinate of upper left corner of bounding rect
- * @w:         width of bounding rect
- * @h:         height of bounding rect
- * @a:         elliptic a-constant applied to corners
- * @b:         elliptic b-constant applied to corners
- * @antialias: if %TRUE, antialias the elliptic corners
- *
- * Used for rounded cornered rectangles and ellipses.  If @op is
- * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels
- * within the ellipse to 255.  If @op is %GIMP_CHANNEL_OP_SUBTRACT,
- * sets pixels within to zero.  If @antialias is %TRUE, pixels that
- * impinge on the edge of the ellipse are set to intermediate values,
- * depending on how much they overlap.
- **/
 gboolean
 gimp_gegl_mask_combine_ellipse_rect (GeglBuffer     *mask,
                                      GimpChannelOps  op,
@@ -184,200 +101,399 @@ gimp_gegl_mask_combine_ellipse_rect (GeglBuffer     *mask,
                                      gint            y,
                                      gint            w,
                                      gint            h,
-                                     gdouble         a,
-                                     gdouble         b,
+                                     gdouble         rx,
+                                     gdouble         ry,
                                      gboolean        antialias)
 {
-  GeglBufferIterator *iter;
-  GeglRectangle      *roi;
-  gdouble             a_sqr;
-  gdouble             b_sqr;
-  gdouble             ellipse_center_x;
-  gint                x0, y0;
-  gint                width, height;
+  GeglRectangle  rect;
+  const Babl    *format;
+  gint           bpp;
+  gfloat         one_f = 1.0f;
+  gpointer       one;
+  gdouble        cx;
+  gdouble        cy;
+  gint           left;
+  gint           right;
+  gint           top;
+  gint           bottom;
 
   g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
-  g_return_val_if_fail (a >= 0.0 && b >= 0.0, FALSE);
-  g_return_val_if_fail (op != GIMP_CHANNEL_OP_INTERSECT, FALSE);
 
-  /* Make sure the elliptic corners fit into the rect */
-  a = MIN (a, w / 2.0);
-  b = MIN (b, h / 2.0);
+  if (rx <= EPSILON || ry <= EPSILON)
+    return gimp_gegl_mask_combine_rect (mask, op, x, y, w, h);
 
-  a_sqr = SQR (a);
-  b_sqr = SQR (b);
+  left   = x;
+  right  = x + w;
+  top    = y;
+  bottom = y + h;
 
-  if (! gimp_rectangle_intersect (x, y, w, h,
-                                  0, 0,
-                                  gegl_buffer_get_width  (mask),
-                                  gegl_buffer_get_height (mask),
-                                  &x0, &y0, &width, &height))
-    return FALSE;
+  cx = (left + right)  / 2.0;
+  cy = (top  + bottom) / 2.0;
 
-  ellipse_center_x = x + a;
+  rx = MIN (rx, w / 2.0);
+  ry = MIN (ry, h / 2.0);
 
-  iter = gegl_buffer_iterator_new (mask,
-                                   GEGL_RECTANGLE (x0, y0, width, height), 0,
-                                   babl_format ("Y float"),
-                                   GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
-  roi = &iter->items[0].roi;
+  if (! gegl_rectangle_intersect (&rect,
+                                  GEGL_RECTANGLE (x, y, w, h),
+                                  gegl_buffer_get_abyss (mask)))
+    {
+      return FALSE;
+    }
 
-  while (gegl_buffer_iterator_next (iter))
+  format = gegl_buffer_get_format (mask);
+
+  if (antialias)
     {
-      gfloat *data = (gfloat *) iter->items[0].data;
-      gint    py;
+      format = gimp_babl_format_change_component_type (
+        format, GIMP_COMPONENT_TYPE_FLOAT);
+    }
+
+  bpp = babl_format_get_bytes_per_pixel (format);
+  one = g_alloca (bpp);
 
-      for (py = roi->y;
-           py < roi->y + roi->height;
-           py++, data += roi->width)
+  babl_process (babl_fish ("Y float", format), &one_f, one, 1);
+
+  /* coordinate-system transforms.  (x, y) coordinates are in the image
+   * coordinate-system, and (u, v) coordinates are in a coordinate-system
+   * aligned with the center of one of the elliptic corners, with the positive
+   * directions pointing away from the rectangle.  when converting from (x, y)
+   * to (u, v), we use the closest elliptic corner.
+   */
+  auto x_to_u = [=] (gdouble x)
+  {
+    if (x < cx)
+      return (left + rx) - x;
+    else
+      return x - (right - rx);
+  };
+
+  auto y_to_v = [=] (gdouble y)
+  {
+    if (y < cy)
+      return (top + ry) - y;
+    else
+      return y - (bottom - ry);
+  };
+
+  auto u_to_x_left = [=] (gdouble u)
+  {
+    return (left + rx) - u;
+  };
+
+  auto u_to_x_right = [=] (gdouble u)
+  {
+    return (right - rx) + u;
+  };
+
+  /* intersection of a horizontal line with the ellipse */
+  auto v_to_u = [=] (gdouble v)
+  {
+    if (v > 0.0)
+      return sqrt (MAX (SQR (rx) - SQR (rx * v / ry), 0.0));
+    else
+      return rx;
+  };
+
+  /* intersection of a vertical line with the ellipse */
+  auto u_to_v = [=] (gdouble u)
+  {
+    if (u > 0.0)
+      return sqrt (MAX (SQR (ry) - SQR (ry * u / rx), 0.0));
+    else
+      return ry;
+  };
+
+  /* signed, normalized distance of a point from the ellipse's circumference.
+   * the sign of the result determines if the point is inside (positive) or
+   * outside (negative) the ellipse.  the result is normalized to the cross-
+   * section length of a pixel, in the direction of the closest point along the
+   * ellipse.
+   *
+   * we use the following method to approximate the distance: pass horizontal
+   * and vertical lines at the given point, P, and find their (positive) points
+   * of intersection with the ellipse, A and B.  the segment AB is an
+   * approximation of the corresponding elliptic arc (see bug #147836).  find
+   * the closest point, C, to P, along the segment AB.  find the (positive)
+   * point of intersection, Q, of the line PC and the ellipse.  Q is an
+   * approximation for the closest point to P along the ellipse, and the
+   * approximated distance is the distance from P to Q.
+   */
+  auto ellipse_distance = [=] (gdouble u,
+                               gdouble v)
+  {
+    gdouble du;
+    gdouble dv;
+    gdouble t;
+    gdouble a, b, c;
+    gdouble d;
+
+    u = MAX (u, 0.0);
+    v = MAX (v, 0.0);
+
+    du = v_to_u (v) - u;
+    dv = u_to_v (u) - v;
+
+    t = SQR (du) / (SQR (du) + SQR (dv));
+
+    du *= 1.0 - t;
+    dv *= t;
+
+    v  *= rx / ry;
+    dv *= rx / ry;
+
+    a = SQR (du) + SQR (dv);
+    b = u * du + v * dv;
+    c = SQR (u) + SQR (v) - SQR (rx);
+
+    if (a <= EPSILON)
+      return 0.0;
+
+    if (c < 0.0)
+      t = (-b + sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
+    else
+      t = (-b - sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
+
+    dv *= ry / rx;
+
+    d = sqrt (SQR (du * t) + SQR (dv * t));
+
+    if (c > 0.0)
+      d = -d;
+
+    d /= sqrt (SQR (MIN (du / dv, dv / du)) + 1.0);
+
+    return d;
+  };
+
+  /* anti-aliased value of a pixel */
+  auto pixel_value = [=] (gint x,
+                          gint y)
+  {
+    gdouble u = x_to_u (x + 0.5);
+    gdouble v = y_to_v (y + 0.5);
+    gdouble d = ellipse_distance (u, v);
+
+    /* use the distance of the pixel's center from the ellipse to approximate
+     * the coverage
+     */
+    d = CLAMP (0.5 + d, 0.0, 1.0);
+
+    /* we're at the horizontal boundary of an elliptic corner */
+    if (u < 0.5)
+      d = d * (0.5 + u) + (0.5 - u);
+
+    /* we're at the vertical boundary of an elliptic corner */
+    if (v < 0.5)
+      d = d * (0.5 + v) + (0.5 - v);
+
+    /* opposite horizontal corners intersect the pixel */
+    if (x == (right - 1) - (x - left))
+      d = 2.0 * d - 1.0;
+
+    /* opposite vertical corners intersect the pixel */
+    if (y == (bottom - 1) - (y - top))
+      d = 2.0 * d - 1.0;
+
+    return d;
+  };
+
+  auto ellipse_range = [=] (gdouble  y,
+                            gdouble *x0,
+                            gdouble *x1)
+  {
+    gdouble u = v_to_u (y_to_v (y));
+
+    *x0 = u_to_x_left  (u);
+    *x1 = u_to_x_right (u);
+  };
+
+  auto fill0 = [=] (gpointer dest,
+                    gint     n)
+  {
+    switch (op)
+      {
+      case GIMP_CHANNEL_OP_REPLACE:
+      case GIMP_CHANNEL_OP_INTERSECT:
+        memset (dest, 0, bpp * n);
+        break;
+
+      case GIMP_CHANNEL_OP_ADD:
+      case GIMP_CHANNEL_OP_SUBTRACT:
+        break;
+      }
+
+    return (gpointer) ((guint8 *) dest + bpp * n);
+  };
+
+  auto fill1 = [=] (gpointer dest,
+                    gint     n)
+  {
+    switch (op)
+      {
+      case GIMP_CHANNEL_OP_REPLACE:
+      case GIMP_CHANNEL_OP_ADD:
+        gegl_memset_pattern (dest, one, bpp, n);
+        break;
+
+      case GIMP_CHANNEL_OP_SUBTRACT:
+        memset (dest, 0, bpp * n);
+        break;
+
+      case GIMP_CHANNEL_OP_INTERSECT:
+        break;
+      }
+
+    return (gpointer) ((guint8 *) dest + bpp * n);
+  };
+
+  auto set = [=] (gpointer dest,
+                  gfloat   value)
+  {
+    gfloat *p = (gfloat *) dest;
+
+    switch (op)
+      {
+      case GIMP_CHANNEL_OP_REPLACE:
+        *p = value;
+        break;
+
+      case GIMP_CHANNEL_OP_ADD:
+        *p = MIN (*p + value, 1.0);
+        break;
+
+      case GIMP_CHANNEL_OP_SUBTRACT:
+        *p = MAX (*p - value, 0.0);
+        break;
+
+      case GIMP_CHANNEL_OP_INTERSECT:
+        *p = MIN (*p, value);
+        break;
+      }
+
+    return (gpointer) (p + 1);
+  };
+
+  gegl_parallel_distribute_area (
+    &rect, PIXELS_PER_THREAD,
+    [=] (const GeglRectangle *area)
+    {
+      GeglBufferIterator *iter;
+
+      iter = gegl_buffer_iterator_new (
+        mask, area, 0, format,
+        op == GIMP_CHANNEL_OP_REPLACE ? GEGL_ACCESS_WRITE :
+                                        GEGL_ACCESS_READWRITE,
+        GEGL_ABYSS_NONE, 1);
+
+      while (gegl_buffer_iterator_next (iter))
         {
-          const gint px = roi->x;
-          gdouble    ellipse_center_y;
+          const GeglRectangle *roi = &iter->items[0].roi;
+          gpointer             d   = iter->items[0].data;
+          gdouble              tx0, ty0;
+          gdouble              tx1, ty1;
+          gdouble              x0;
+          gdouble              x1;
+          gint                 y;
 
-          if (py >= y + b && py < y + h - b)
-            {
-              /*  we are on a row without rounded corners  */
-              gimp_gegl_mask_combine_span (data, op, 0, roi->width, 1.0);
-              continue;
-            }
+          /* tile bounds */
+          tx0 = roi->x;
+          ty0 = roi->y;
 
-          /* Match the ellipse center y with our current y */
-          if (py < y + b)
-            {
-              ellipse_center_y = y + b;
-            }
-          else
+          tx1 = roi->x + roi->width;
+          ty1 = roi->y + roi->height;
+
+          if (! antialias)
             {
-              ellipse_center_y = y + h - b;
+              tx0 += 0.5;
+              ty0 += 0.5;
+
+              tx1 -= 0.5;
+              ty1 -= 0.5;
             }
 
-          /* For a non-antialiased ellipse, use the normal equation
-           * for an ellipse with an arbitrary center
-           * (ellipse_center_x, ellipse_center_y).
+          /* if the tile is fully inside/outside the ellipse, fill it with 1/0,
+           * respectively, and skip the rest.
            */
-          if (! antialias)
-            {
-              gdouble half_ellipse_width_at_y;
-              gint    x_start;
-              gint    x_end;
+          ellipse_range (ty0, &x0, &x1);
 
-              half_ellipse_width_at_y =
-                sqrt (a_sqr -
-                      a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr);
+          if (tx0 >= x0 && tx1 <= x1)
+            {
+              ellipse_range (ty1, &x0, &x1);
 
-              x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y);
-              x_end   = ROUND (ellipse_center_x + w - 2 * a +
-                               half_ellipse_width_at_y);
+              if (tx0 > x0 && tx1 <= x1)
+                {
+                  fill1 (d, iter->length);
 
-              gimp_gegl_mask_combine_span (data, op,
-                                           MAX (x_start - px, 0),
-                                           MIN (x_end   - px, roi->width), 1.0);
+                  continue;
+                }
             }
-          else  /* use antialiasing */
+          else if (tx1 < x0 || tx0 > x1)
             {
-              /* algorithm changed 7-18-04, because the previous one
-               * did not work well for eccentric ellipses.  The new
-               * algorithm measures the distance to the ellipse in the
-               * X and Y directions, and uses trigonometry to
-               * approximate the distance to the ellipse as the
-               * distance to the hypotenuse of a right triangle whose
-               * legs are the X and Y distances.  (WES)
-               */
-              const gfloat yi       = ABS (py + 0.5 - ellipse_center_y);
-              gfloat       last_val = -1;
-              gint         x_start  = px;
-              gint         cur_x;
-
-              for (cur_x = px; cur_x < (px + roi->width); cur_x++)
+              ellipse_range (ty1, &x0, &x1);
+
+              if (tx1 < x0 || tx0 > x1)
                 {
-                  gfloat  xj;
-                  gfloat  xdist;
-                  gfloat  ydist;
-                  gfloat  r;
-                  gfloat  dist;
-                  gfloat  val;
-
-                  if (cur_x < x + w / 2)
-                    {
-                      ellipse_center_x = x + a;
-                    }
-                  else
+                  if ((ty0 - cy) * (ty1 - cy) >= 0.0)
                     {
-                      ellipse_center_x = x + w - a;
+                      fill0 (d, iter->length);
+
+                      continue;
                     }
+                }
+            }
 
-                  xj = ABS (cur_x + 0.5 - ellipse_center_x);
+          for (y = roi->y; y < roi->y + roi->height; y++)
+            {
+              gint a, b;
 
-                  if (yi < b)
-                    xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr);
-                  else
-                    xdist = 1000.0;  /* anything large will work */
+              if (antialias)
+                {
+                  gdouble v  = y_to_v (y + 0.5);
+                  gdouble u0 = v_to_u (v - 0.5);
+                  gdouble u1 = v_to_u (v + 0.5);
+                  gint    x;
 
-                  if (xj < a)
-                    ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr);
-                  else
-                    ydist = 1000.0;  /* anything large will work */
+                  a = floor (u_to_x_left (u0)) - roi->x;
+                  a = CLAMP (a, 0, roi->width);
 
-                  r = hypot (xdist, ydist);
+                  b = ceil  (u_to_x_left (u1)) - roi->x;
+                  b = CLAMP (b, a, roi->width);
 
-                  if (r < 0.001)
-                    dist = 0.0;
-                  else
-                    dist = xdist * ydist / r; /* trig formula for distance to
-                                               * hypotenuse
-                                               */
+                  d = fill0 (d, a);
 
-                  if (xdist < 0.0)
-                    dist *= -1;
+                  for (x = roi->x + a; x < roi->x + b; x++)
+                    d = set (d, pixel_value (x, y));
 
-                  if (dist < -0.5)
-                    val = 1.0;
-                  else if (dist < 0.5)
-                    val = (1.0 - (dist + 0.5));
-                  else
-                    val = 0.0;
+                  a = floor (u_to_x_right (u1)) - roi->x;
+                  a = CLAMP (a, b, roi->width);
 
-                  if (last_val != val)
-                    {
-                      if (last_val != -1)
-                        gimp_gegl_mask_combine_span (data, op,
-                                                     MAX (x_start - px, 0),
-                                                     MIN (cur_x   - px, roi->width),
-                                                     last_val);
-
-                      x_start = cur_x;
-                      last_val = val;
-                    }
+                  d = fill1 (d, a - b);
 
-                  /*  skip ahead if we are on the straight segment
-                   *  between rounded corners
-                   */
-                  if (cur_x >= x + a && cur_x < x + w - a)
-                    {
-                      gimp_gegl_mask_combine_span (data, op,
-                                                   MAX (x_start - px, 0),
-                                                   MIN (cur_x   - px, roi->width),
-                                                   last_val);
-
-                      x_start = cur_x;
-                      cur_x = x + w - a;
-                      last_val = val = 1.0;
-                    }
+                  b = ceil  (u_to_x_right (u0)) - roi->x;
+                  b = CLAMP (b, a, roi->width);
 
-                  /* Time to change center? */
-                  if (cur_x >= x + w / 2)
-                    {
-                      ellipse_center_x = x + w - a;
-                    }
+                  for (x = roi->x + a; x < roi->x + b; x++)
+                    d = set (d, pixel_value (x, y));
+
+                  d = fill0 (d, roi->width - b);
                 }
+              else
+                {
+                  ellipse_range (y + 0.5, &x0, &x1);
+
+                  a = ceil  (x0 - 0.5) - roi->x;
+                  a = CLAMP (a, 0, roi->width);
+
+                  b = floor (x1 + 0.5) - roi->x;
+                  b = CLAMP (b, 0, roi->width);
 
-              gimp_gegl_mask_combine_span (data, op,
-                                           MAX (x_start - px, 0),
-                                           MIN (cur_x   - px, roi->width),
-                                           last_val);
+                  d = fill0 (d, a);
+                  d = fill1 (d, b - a);
+                  d = fill0 (d, roi->width - b);
+                }
             }
         }
-    }
+    });
 
   return TRUE;
 }
diff --git a/app/gegl/gimp-gegl-mask-combine.h b/app/gegl/gimp-gegl-mask-combine.h
index 0f9da50548..d8e0fa2590 100644
--- a/app/gegl/gimp-gegl-mask-combine.h
+++ b/app/gegl/gimp-gegl-mask-combine.h
@@ -38,8 +38,8 @@ gboolean   gimp_gegl_mask_combine_ellipse_rect (GeglBuffer     *mask,
                                                 gint            y,
                                                 gint            w,
                                                 gint            h,
-                                                gdouble         a,
-                                                gdouble         b,
+                                                gdouble         rx,
+                                                gdouble         ry,
                                                 gboolean        antialias);
 gboolean   gimp_gegl_mask_combine_buffer       (GeglBuffer     *mask,
                                                 GeglBuffer     *add_on,


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