[gimp] app: factor out the core mask combine functions to gegl-only functions
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: factor out the core mask combine functions to gegl-only functions
- Date: Mon, 8 Apr 2013 19:57:03 +0000 (UTC)
commit 5cd8b98efb9f5cb6f6ee1b008db7cd03ade9edc1
Author: Michael Natterer <mitch gimp org>
Date: Mon Apr 8 21:54:46 2013 +0200
app: factor out the core mask combine functions to gegl-only functions
so we can use them on plain GeglBuffers. Use them in
gimpchannel-combine.c for now.
app/core/gimpchannel-combine.c | 390 ++++---------------------------
app/core/gimpchannel-combine.h | 5 +
app/gegl/Makefile.am | 2 +
app/gegl/gimp-gegl-mask-combine.c | 470 +++++++++++++++++++++++++++++++++++++
app/gegl/gimp-gegl-mask-combine.h | 51 ++++
5 files changed, 575 insertions(+), 343 deletions(-)
---
diff --git a/app/core/gimpchannel-combine.c b/app/core/gimpchannel-combine.c
index b43a878..22bc93a 100644
--- a/app/core/gimpchannel-combine.c
+++ b/app/core/gimpchannel-combine.c
@@ -26,6 +26,8 @@
#include "core-types.h"
+#include "gegl/gimp-gegl-mask-combine.h"
+
#include "gimpchannel.h"
#include "gimpchannel-combine.h"
@@ -38,25 +40,20 @@ gimp_channel_combine_rect (GimpChannel *mask,
gint w,
gint h)
{
- GeglColor *color;
+ GeglBuffer *buffer;
g_return_if_fail (GIMP_IS_CHANNEL (mask));
- if (! gimp_rectangle_intersect (x, y, w, h,
- 0, 0,
- gimp_item_get_width (GIMP_ITEM (mask)),
- gimp_item_get_height (GIMP_ITEM (mask)),
- &x, &y, &w, &h))
- return;
+ buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
- if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
- color = gegl_color_new ("#fff");
- else
- color = gegl_color_new ("#000");
+ if (! gimp_gegl_mask_combine_rect (buffer, op, x, y, w, h))
+ return;
- gegl_buffer_set_color (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)),
- GEGL_RECTANGLE (x, y, w, h), color);
- g_object_unref (color);
+ gimp_rectangle_intersect (x, y, w, h,
+ 0, 0,
+ gimp_item_get_width (GIMP_ITEM (mask)),
+ gimp_item_get_height (GIMP_ITEM (mask)),
+ &x, &y, &w, &h);
/* Determine new boundary */
if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty)
@@ -122,57 +119,6 @@ gimp_channel_combine_ellipse (GimpChannel *mask,
w / 2.0, h / 2.0, antialias);
}
-static void
-gimp_channel_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_channel_combine_ellipse_rect:
* @mask: the channel with which to combine the elliptic rect
@@ -204,205 +150,23 @@ gimp_channel_combine_ellipse_rect (GimpChannel *mask,
gdouble b,
gboolean antialias)
{
- GeglBuffer *buffer;
- GeglBufferIterator *iter;
- GeglRectangle *roi;
- gdouble a_sqr;
- gdouble b_sqr;
- gdouble ellipse_center_x;
- gint x0, y0;
- gint width, height;
+ 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);
- /* Make sure the elliptic corners fit into the rect */
- a = MIN (a, w / 2.0);
- b = MIN (b, h / 2.0);
-
- a_sqr = SQR (a);
- b_sqr = SQR (b);
-
- if (! gimp_rectangle_intersect (x, y, w, h,
- 0, 0,
- gimp_item_get_width (GIMP_ITEM (mask)),
- gimp_item_get_height (GIMP_ITEM (mask)),
- &x0, &y0, &width, &height))
- return;
-
- ellipse_center_x = x + a;
-
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
- iter = gegl_buffer_iterator_new (buffer,
- GEGL_RECTANGLE (x0, y0, width, height), 0,
- babl_format ("Y float"),
- GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
- roi = &iter->roi[0];
-
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *data = iter->data[0];
- gint py;
-
- for (py = roi->y;
- py < roi->y + roi->height;
- py++, data += roi->width)
- {
- const gint px = roi->x;
- gdouble ellipse_center_y;
-
- if (py >= y + b && py < y + h - b)
- {
- /* we are on a row without rounded corners */
- gimp_channel_combine_span (data, op, 0, roi->width, 1.0);
- continue;
- }
-
- /* Match the ellipse center y with our current y */
- if (py < y + b)
- {
- ellipse_center_y = y + b;
- }
- else
- {
- ellipse_center_y = y + h - b;
- }
-
- /* For a non-antialiased ellipse, use the normal equation
- * for an ellipse with an arbitrary center
- * (ellipse_center_x, ellipse_center_y).
- */
- if (! antialias)
- {
- gdouble half_ellipse_width_at_y;
- gint x_start;
- gint x_end;
-
- half_ellipse_width_at_y =
- sqrt (a_sqr -
- a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr);
-
- 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);
-
- gimp_channel_combine_span (data, op,
- MAX (x_start - px, 0),
- MIN (x_end - px, roi->width), 1.0);
- }
- else /* use antialiasing */
- {
- /* 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++)
- {
- gfloat xj;
- gfloat xdist;
- gfloat ydist;
- gfloat r;
- gfloat dist;
- gfloat val;
-
- if (cur_x < x + w / 2)
- {
- ellipse_center_x = x + a;
- }
- else
- {
- ellipse_center_x = x + w - a;
- }
-
- xj = ABS (cur_x + 0.5 - ellipse_center_x);
-
- if (yi < b)
- xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr);
- else
- xdist = 1000.0; /* anything large will work */
-
- if (xj < a)
- ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr);
- else
- ydist = 1000.0; /* anything large will work */
-
- r = hypot (xdist, ydist);
-
- if (r < 0.001)
- dist = 0.0;
- else
- dist = xdist * ydist / r; /* trig formula for distance to
- * hypotenuse
- */
-
- if (xdist < 0.0)
- dist *= -1;
-
- if (dist < -0.5)
- val = 1.0;
- else if (dist < 0.5)
- val = (1.0 - (dist + 0.5));
- else
- val = 0.0;
-
- if (last_val != val)
- {
- if (last_val != -1)
- gimp_channel_combine_span (data, op,
- MAX (x_start - px, 0),
- MIN (cur_x - px, roi->width),
- last_val);
-
- x_start = cur_x;
- last_val = val;
- }
-
- /* skip ahead if we are on the straight segment
- * between rounded corners
- */
- if (cur_x >= x + a && cur_x < x + w - a)
- {
- gimp_channel_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;
- }
-
- /* Time to change center? */
- if (cur_x >= x + w / 2)
- {
- ellipse_center_x = x + w - a;
- }
- }
-
- gimp_channel_combine_span (data, op,
- MAX (x_start - px, 0),
- MIN (cur_x - px, roi->width),
- last_val);
- }
- }
- }
+ if (! gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h,
+ a, b, antialias))
+ return;
- /* use the intersected values for the boundary calculation */
- x = x0;
- y = y0;
- w = width;
- h = height;
+ gimp_rectangle_intersect (x, y, w, h,
+ 0, 0,
+ gimp_item_get_width (GIMP_ITEM (mask)),
+ gimp_item_get_height (GIMP_ITEM (mask)),
+ &x, &y, &w, &h);
/* determine new boundary */
if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty)
@@ -436,103 +200,43 @@ gimp_channel_combine_mask (GimpChannel *mask,
gint off_x,
gint off_y)
{
- GeglBuffer *mask_buffer;
- GeglBuffer *add_on_buffer;
- GeglBufferIterator *iter;
- GeglRectangle rect;
- gint x, y, w, h;
+ GeglBuffer *add_on_buffer;
g_return_if_fail (GIMP_IS_CHANNEL (mask));
g_return_if_fail (GIMP_IS_CHANNEL (add_on));
- if (! gimp_rectangle_intersect (off_x, off_y,
- gimp_item_get_width (GIMP_ITEM (add_on)),
- gimp_item_get_height (GIMP_ITEM (add_on)),
- 0, 0,
- gimp_item_get_width (GIMP_ITEM (mask)),
- gimp_item_get_height (GIMP_ITEM (mask)),
- &x, &y, &w, &h))
- return;
-
- mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
add_on_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (add_on));
- rect.x = x;
- rect.y = y;
- rect.width = w;
- rect.height = h;
+ gimp_channel_combine_buffer (mask, add_on_buffer,
+ op, off_x, off_y);
+}
+
+void
+gimp_channel_combine_buffer (GimpChannel *mask,
+ GeglBuffer *add_on_buffer,
+ GimpChannelOps op,
+ gint off_x,
+ gint off_y)
+{
+ GeglBuffer *buffer;
+ gint x, y, w, h;
- iter = gegl_buffer_iterator_new (mask_buffer, &rect, 0,
- babl_format ("Y float"),
- GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
+ g_return_if_fail (GIMP_IS_CHANNEL (mask));
+ g_return_if_fail (GEGL_IS_BUFFER (add_on_buffer));
- rect.x -= off_x;
- rect.y -= off_y;
+ buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
- gegl_buffer_iterator_add (iter, add_on_buffer, &rect, 0,
- babl_format ("Y float"),
- GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
+ if (! gimp_gegl_mask_combine_buffer (buffer, add_on_buffer,
+ op, off_x, off_y))
+ return;
- switch (op)
- {
- case GIMP_CHANNEL_OP_ADD:
- case GIMP_CHANNEL_OP_REPLACE:
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *mask_data = iter->data[0];
- const gfloat *add_on_data = iter->data[1];
-
- while (iter->length--)
- {
- const gfloat val = *mask_data + *add_on_data;
-
- *mask_data = CLAMP (val, 0.0, 1.0);
-
- add_on_data++;
- mask_data++;
- }
- }
- break;
-
- case GIMP_CHANNEL_OP_SUBTRACT:
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *mask_data = iter->data[0];
- const gfloat *add_on_data = iter->data[1];
-
- while (iter->length--)
- {
- if (*add_on_data > *mask_data)
- *mask_data = 0.0;
- else
- *mask_data -= *add_on_data;
-
- add_on_data++;
- mask_data++;
- }
- }
- break;
-
- case GIMP_CHANNEL_OP_INTERSECT:
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *mask_data = iter->data[0];
- const gfloat *add_on_data = iter->data[1];
-
- while (iter->length--)
- {
- *mask_data = MIN (*mask_data, *add_on_data);
-
- add_on_data++;
- mask_data++;
- }
- }
- break;
-
- default:
- g_warning ("%s: unknown operation type", G_STRFUNC);
- break;
- }
+ gimp_rectangle_intersect (off_x, off_y,
+ gegl_buffer_get_width (add_on_buffer),
+ gegl_buffer_get_height (add_on_buffer),
+ 0, 0,
+ gimp_item_get_width (GIMP_ITEM (mask)),
+ gimp_item_get_height (GIMP_ITEM (mask)),
+ &x, &y, &w, &h);
mask->bounds_known = FALSE;
diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h
index 90fc3de..a881603 100644
--- a/app/core/gimpchannel-combine.h
+++ b/app/core/gimpchannel-combine.h
@@ -46,6 +46,11 @@ void gimp_channel_combine_mask (GimpChannel *mask,
GimpChannelOps op,
gint off_x,
gint off_y);
+void gimp_channel_combine_buffer (GimpChannel *mask,
+ GeglBuffer *add_on_buffer,
+ GimpChannelOps op,
+ gint off_x,
+ gint off_y);
#endif /* __GIMP_CHANNEL_COMBINE_H__ */
diff --git a/app/gegl/Makefile.am b/app/gegl/Makefile.am
index 1ad7050..670cb9e 100644
--- a/app/gegl/Makefile.am
+++ b/app/gegl/Makefile.am
@@ -30,6 +30,8 @@ libappgegl_a_sources = \
gimp-gegl-config-proxy.h \
gimp-gegl-loops.c \
gimp-gegl-loops.h \
+ gimp-gegl-mask-combine.c \
+ gimp-gegl-mask-combine.h \
gimp-gegl-nodes.c \
gimp-gegl-nodes.h \
gimp-gegl-tile-compat.c \
diff --git a/app/gegl/gimp-gegl-mask-combine.c b/app/gegl/gimp-gegl-mask-combine.c
new file mode 100644
index 0000000..6fcf3a2
--- /dev/null
+++ b/app/gegl/gimp-gegl-mask-combine.c
@@ -0,0 +1,470 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 <string.h>
+
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimp-gegl-types.h"
+
+#include "gimp-gegl-mask-combine.h"
+
+
+gboolean
+gimp_gegl_mask_combine_rect (GeglBuffer *mask,
+ GimpChannelOps op,
+ gint x,
+ gint y,
+ gint w,
+ gint h)
+{
+ GeglColor *color;
+
+ g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
+
+ if (! gimp_rectangle_intersect (x, y, w, h,
+ 0, 0,
+ gegl_buffer_get_width (mask),
+ gegl_buffer_get_height (mask),
+ &x, &y, &w, &h))
+ return FALSE;
+
+ if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
+ color = gegl_color_new ("#fff");
+ else
+ color = gegl_color_new ("#000");
+
+ gegl_buffer_set_color (mask, GEGL_RECTANGLE (x, y, w, h), color);
+ g_object_unref (color);
+
+ 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,
+ gint x,
+ gint y,
+ gint w,
+ gint h,
+ gboolean antialias)
+{
+ return gimp_gegl_mask_combine_ellipse_rect (mask, op, x, y, w, h,
+ 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,
+ gint x,
+ gint y,
+ gint w,
+ gint h,
+ gdouble a,
+ gdouble b,
+ gboolean antialias)
+{
+ GeglBufferIterator *iter;
+ GeglRectangle *roi;
+ gdouble a_sqr;
+ gdouble b_sqr;
+ gdouble ellipse_center_x;
+ gint x0, y0;
+ gint width, height;
+
+ 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);
+
+ a_sqr = SQR (a);
+ b_sqr = SQR (b);
+
+ 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;
+
+ ellipse_center_x = x + a;
+
+ iter = gegl_buffer_iterator_new (mask,
+ GEGL_RECTANGLE (x0, y0, width, height), 0,
+ babl_format ("Y float"),
+ GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
+ roi = &iter->roi[0];
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->data[0];
+ gint py;
+
+ for (py = roi->y;
+ py < roi->y + roi->height;
+ py++, data += roi->width)
+ {
+ const gint px = roi->x;
+ gdouble ellipse_center_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;
+ }
+
+ /* Match the ellipse center y with our current y */
+ if (py < y + b)
+ {
+ ellipse_center_y = y + b;
+ }
+ else
+ {
+ ellipse_center_y = y + h - b;
+ }
+
+ /* For a non-antialiased ellipse, use the normal equation
+ * for an ellipse with an arbitrary center
+ * (ellipse_center_x, ellipse_center_y).
+ */
+ if (! antialias)
+ {
+ gdouble half_ellipse_width_at_y;
+ gint x_start;
+ gint x_end;
+
+ half_ellipse_width_at_y =
+ sqrt (a_sqr -
+ a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr);
+
+ 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);
+
+ gimp_gegl_mask_combine_span (data, op,
+ MAX (x_start - px, 0),
+ MIN (x_end - px, roi->width), 1.0);
+ }
+ else /* use antialiasing */
+ {
+ /* 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++)
+ {
+ gfloat xj;
+ gfloat xdist;
+ gfloat ydist;
+ gfloat r;
+ gfloat dist;
+ gfloat val;
+
+ if (cur_x < x + w / 2)
+ {
+ ellipse_center_x = x + a;
+ }
+ else
+ {
+ ellipse_center_x = x + w - a;
+ }
+
+ xj = ABS (cur_x + 0.5 - ellipse_center_x);
+
+ if (yi < b)
+ xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr);
+ else
+ xdist = 1000.0; /* anything large will work */
+
+ if (xj < a)
+ ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr);
+ else
+ ydist = 1000.0; /* anything large will work */
+
+ r = hypot (xdist, ydist);
+
+ if (r < 0.001)
+ dist = 0.0;
+ else
+ dist = xdist * ydist / r; /* trig formula for distance to
+ * hypotenuse
+ */
+
+ if (xdist < 0.0)
+ dist *= -1;
+
+ if (dist < -0.5)
+ val = 1.0;
+ else if (dist < 0.5)
+ val = (1.0 - (dist + 0.5));
+ else
+ val = 0.0;
+
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* Time to change center? */
+ if (cur_x >= x + w / 2)
+ {
+ ellipse_center_x = x + w - a;
+ }
+ }
+
+ gimp_gegl_mask_combine_span (data, op,
+ MAX (x_start - px, 0),
+ MIN (cur_x - px, roi->width),
+ last_val);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gimp_gegl_mask_combine_buffer (GeglBuffer *mask,
+ GeglBuffer *add_on,
+ GimpChannelOps op,
+ gint off_x,
+ gint off_y)
+{
+ GeglBufferIterator *iter;
+ GeglRectangle rect;
+ gint x, y, w, h;
+
+ g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
+ g_return_val_if_fail (GEGL_IS_BUFFER (add_on), FALSE);
+
+ if (! gimp_rectangle_intersect (off_x, off_y,
+ gegl_buffer_get_width (add_on),
+ gegl_buffer_get_height (add_on),
+ 0, 0,
+ gegl_buffer_get_width (mask),
+ gegl_buffer_get_height (mask),
+ &x, &y, &w, &h))
+ return FALSE;
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = w;
+ rect.height = h;
+
+ iter = gegl_buffer_iterator_new (mask, &rect, 0,
+ babl_format ("Y float"),
+ GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
+
+ rect.x -= off_x;
+ rect.y -= off_y;
+
+ gegl_buffer_iterator_add (iter, add_on, &rect, 0,
+ babl_format ("Y float"),
+ GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
+
+ switch (op)
+ {
+ case GIMP_CHANNEL_OP_ADD:
+ case GIMP_CHANNEL_OP_REPLACE:
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *mask_data = iter->data[0];
+ const gfloat *add_on_data = iter->data[1];
+
+ while (iter->length--)
+ {
+ const gfloat val = *mask_data + *add_on_data;
+
+ *mask_data = CLAMP (val, 0.0, 1.0);
+
+ add_on_data++;
+ mask_data++;
+ }
+ }
+ break;
+
+ case GIMP_CHANNEL_OP_SUBTRACT:
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *mask_data = iter->data[0];
+ const gfloat *add_on_data = iter->data[1];
+
+ while (iter->length--)
+ {
+ if (*add_on_data > *mask_data)
+ *mask_data = 0.0;
+ else
+ *mask_data -= *add_on_data;
+
+ add_on_data++;
+ mask_data++;
+ }
+ }
+ break;
+
+ case GIMP_CHANNEL_OP_INTERSECT:
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *mask_data = iter->data[0];
+ const gfloat *add_on_data = iter->data[1];
+
+ while (iter->length--)
+ {
+ *mask_data = MIN (*mask_data, *add_on_data);
+
+ add_on_data++;
+ mask_data++;
+ }
+ }
+ break;
+
+ default:
+ g_warning ("%s: unknown operation type", G_STRFUNC);
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/gegl/gimp-gegl-mask-combine.h b/app/gegl/gimp-gegl-mask-combine.h
new file mode 100644
index 0000000..9a22078
--- /dev/null
+++ b/app/gegl/gimp-gegl-mask-combine.h
@@ -0,0 +1,51 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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_GEGL_MASK_COMBINE_H__
+#define __GIMP_GEGL_MASK_COMBINE_H__
+
+
+gboolean gimp_gegl_mask_combine_rect (GeglBuffer *mask,
+ GimpChannelOps op,
+ gint x,
+ gint y,
+ gint w,
+ gint h);
+gboolean gimp_gegl_mask_combine_ellipse (GeglBuffer *mask,
+ GimpChannelOps op,
+ gint x,
+ gint y,
+ gint w,
+ gint h,
+ gboolean antialias);
+gboolean gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask,
+ GimpChannelOps op,
+ gint x,
+ gint y,
+ gint w,
+ gint h,
+ gdouble a,
+ gdouble b,
+ gboolean antialias);
+gboolean gimp_gegl_mask_combine_buffer (GeglBuffer *mask,
+ GeglBuffer *add_on,
+ GimpChannelOps op,
+ gint off_x,
+ gint off_y);
+
+
+#endif /* __GIMP_GEGL_MASK_COMBINE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]