Commit 1ee6516d authored by Michael Natterer's avatar Michael Natterer 😴 Committed by Michael Natterer

Applied modified patch from Martin Nordholts which adds a "Rounded

2006-10-18  Michael Natterer  <mitch@gimp.org>

	Applied modified patch from Martin Nordholts which adds a "Rounded
	Corners" option to the rectangle select tool. Fixes bug #86279.

	* app/core/gimpchannel-combine.[ch]: added
	gimp_channel_combine_ellipse_rect(). Use it from
	gimp_channel_combine_ellipse().

	* app/core/gimpchannel-select.[ch]: added
	gimp_channel_select_round_rect()

	* app/tools/gimprectangleselectoptions.[ch]: added properties
	"round-corners" and "corner-radius" and GUI for the new propeties.

	* app/tools/gimprectangleselecttool.h: added macro
	GIMP_RECT_SELECT_TOOL_GET_OPTIONS().

	* app/tools/gimprectangleselecttool.c (gimp_rect_select_tool_draw):
	draw round corners if enabled.

	(gimp_rect_select_tool_real_select): use
	gimp_channel_select_round_rect() if enabled.

	* app/tools/gimpselectionoptions.[ch]: added "antialias_toggle"
	to the GimpSelectionOptions struct so the rect select options
	can set its sensitivity.

	Unrelated:

	* app/tools/gimpellipseselecttool.c (gimp_ellipse_select_tool_draw):
	use 360 * 64 instead of 23040.
parent 64e893e6
2006-10-18 Michael Natterer <mitch@gimp.org>
Applied modified patch from Martin Nordholts which adds a "Rounded
Corners" option to the rectangle select tool. Fixes bug #86279.
* app/core/gimpchannel-combine.[ch]: added
gimp_channel_combine_ellipse_rect(). Use it from
gimp_channel_combine_ellipse().
* app/core/gimpchannel-select.[ch]: added
gimp_channel_select_round_rect()
* app/tools/gimprectangleselectoptions.[ch]: added properties
"round-corners" and "corner-radius" and GUI for the new propeties.
* app/tools/gimprectangleselecttool.h: added macro
GIMP_RECT_SELECT_TOOL_GET_OPTIONS().
* app/tools/gimprectangleselecttool.c (gimp_rect_select_tool_draw):
draw round corners if enabled.
(gimp_rect_select_tool_real_select): use
gimp_channel_select_round_rect() if enabled.
* app/tools/gimpselectionoptions.[ch]: added "antialias_toggle"
to the GimpSelectionOptions struct so the rect select options
can set its sensitivity.
Unrelated:
* app/tools/gimpellipseselecttool.c (gimp_ellipse_select_tool_draw):
use 360 * 64 instead of 23040.
2006-10-18 Sven Neumann <sven@gimp.org>
* [lots of files]: there's no need to make GTypeInfo and
......
......@@ -56,7 +56,7 @@ gimp_channel_add_segment (GimpChannel *mask,
if (!width)
return;
if (y < 0 || y > GIMP_ITEM (mask)->height)
if (y < 0 || y >= GIMP_ITEM (mask)->height)
return;
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
......@@ -212,163 +212,248 @@ gimp_channel_combine_ellipse (GimpChannel *mask,
gint h,
gboolean antialias)
{
gint i, j;
gfloat a, b;
gfloat a_sqr, b_sqr, aob_sqr;
gfloat cx, cy;
gimp_channel_combine_ellipse_rect (mask, op, x, y, w, h,
w / 2.0, h / 2.0, antialias);
}
static inline void
gimp_channel_combine_segment (GimpChannel *mask,
GimpChannelOps op,
gint start,
gint row,
gint width,
gint value)
{
switch (op)
{
case GIMP_CHANNEL_OP_ADD:
case GIMP_CHANNEL_OP_REPLACE:
gimp_channel_add_segment (mask, start, row, width, value);
break;
case GIMP_CHANNEL_OP_SUBTRACT:
gimp_channel_sub_segment (mask, start, row, width, value);
break;
case GIMP_CHANNEL_OP_INTERSECT:
/* Should not happend */
break;
}
}
/**
* 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,
gint x,
gint y,
gint w,
gint h,
gdouble a,
gdouble b,
gboolean antialias)
{
gint cur_y;
gdouble a_sqr;
gdouble b_sqr;
gdouble straight_width;
gdouble straight_height;
g_return_if_fail (GIMP_IS_CHANNEL (mask));
g_return_if_fail (a >= 0.0 && b >= 0.0);
if (! gimp_rectangle_intersect (x, y, w, h,
0, 0,
GIMP_ITEM (mask)->width,
GIMP_ITEM (mask)->height,
NULL, NULL, NULL, NULL))
{
return;
}
/* Allow us to use gimp_channel_combine_segment without breaking
* previous logic
*/
if (op == GIMP_CHANNEL_OP_INTERSECT)
return;
a = w / 2.0;
b = h / 2.0;
/* 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);
aob_sqr = a_sqr / b_sqr;
cx = x + a;
cy = y + b;
straight_width = w - 2 * a;
straight_height = h - 2 * b;
for (i = y; i < (y + h); i++)
for (cur_y = y; cur_y < (y + h); cur_y++)
{
if (i >= 0 && i < GIMP_ITEM (mask)->height)
gdouble x_start;
gdouble x_end;
gdouble ellipse_center_x;
gdouble ellipse_center_y;
gdouble half_ellipse_width_at_y;
/* If this row is not part of the mask, continue with the next row */
if (cur_y < 0 || cur_y >= GIMP_ITEM (mask)->height)
{
if (!antialias)
{
gfloat y_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
gfloat rad = sqrt (a_sqr - a_sqr * y_sqr / (gdouble) b_sqr);
gint x1 = ROUND (cx - rad);
gint x2 = ROUND (cx + rad);
continue;
}
switch (op)
{
case GIMP_CHANNEL_OP_ADD:
case GIMP_CHANNEL_OP_REPLACE:
gimp_channel_add_segment (mask, x1, i, (x2 - x1), 255);
break;
case GIMP_CHANNEL_OP_SUBTRACT:
gimp_channel_sub_segment (mask, x1, i, (x2 - x1), 255);
break;
default:
g_return_if_reached ();
break;
}
}
else /* antialiasing */
/* If we are on a row not affected by rounded corners, simply combine the
* whole row.
*/
if (cur_y >= y + b && cur_y < y + b + straight_height)
{
x_start = x;
x_end = x + w;
gimp_channel_combine_segment (mask, op, x_start,
cur_y, x_end - x_start, 255);
continue;
}
/* Match the ellipse center y with our current y */
if (cur_y < y + b)
{
ellipse_center_y = y + b;
}
else
{
ellipse_center_y = y + b + straight_height;
}
/* For a non-antialiased ellipse, use the normal equation for an ellipse
* with an arbitrary center (ellipse_center_x, ellipse_center_y).
*/
if (!antialias)
{
ellipse_center_x = x + a;
half_ellipse_width_at_y =
sqrt (a_sqr - a_sqr * SQR (cur_y + 0.5f - ellipse_center_y) / b_sqr);
x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y);
x_end = ROUND (ellipse_center_x + straight_width +
half_ellipse_width_at_y);
gimp_channel_combine_segment (mask, op, x_start,
cur_y, x_end - x_start, 255);
}
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)
*/
gint val;
gint last_val;
gint x_start;
gint cur_x;
gfloat xj, yi;
gfloat xdist, ydist;
gfloat r;
gfloat dist;
x_start = x;
yi = ABS (cur_y + 0.5 - ellipse_center_y);
last_val = 0;
ellipse_center_x = x + a;
for (cur_x = x; cur_x < (x + w); cur_x++)
{
/* 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)
*/
gint val, last;
gint x0;
gfloat xj, yi;
gfloat xdist, ydist;
gfloat r;
gfloat dist;
x0 = x;
yi = ABS (i + 0.5 - cy);
last = 0;
for (j = x; j < (x + w); j++)
xj = ABS (cur_x + 0.5 - ellipse_center_x);
if (yi < b)
xdist = xj - a * sqrt (1 - yi * yi / b_sqr);
else
xdist = 100.0; /* anything large will work */
if (xj < a)
ydist = yi - b * sqrt (1 - xj * xj / a_sqr);
else
ydist = 100.0; /* anything large will work */
r = hypot (xdist, ydist);
if (r < 0.001)
dist = 0.;
else
dist = xdist * ydist / r; /* trig formula for distance to
* hypotenuse
*/
if (xdist < 0.0)
dist *= -1;
if (dist < -0.5)
val = 255;
else if (dist < 0.5)
val = (gint) (255 * (1 - (dist + 0.5)));
else
val = 0;
gimp_channel_combine_segment (mask, op,
x_start, cur_y,
cur_x - x_start,
last_val);
if (last_val != val)
{
xj = ABS (j + 0.5 - cx);
if (yi < b)
xdist = xj - a * sqrt (1 - yi * yi / b_sqr);
else
xdist = 100.0; /* anything large will work */
if (xj < a)
ydist = yi - b * sqrt (1 - xj * xj / a_sqr);
else
ydist = 100.0; /* anything large will work */
r = hypot (xdist, ydist);
if (r < 0.001)
dist = 0.;
else
dist = xdist * ydist / r; /* trig formula for distance
* to hypotenuse
*/
if (xdist < 0.0)
dist *= -1;
if (dist < -0.5)
val = 255;
else if (dist < 0.5)
val = (gint) (255 * (1 - (dist + 0.5)));
else
val = 0;
if (last != val && last)
x_start = cur_x;
last_val = val;
/* because we are symetric accross the y axis we can
* skip ahead a bit if we are inside. Do this if we
* have reached a value of 255 OR if we have passed
* the center of the leftmost ellipse.
*/
if ((val == 255 || cur_x >= x + a) && cur_x < x + w / 2)
{
switch (op)
{
case GIMP_CHANNEL_OP_ADD:
case GIMP_CHANNEL_OP_REPLACE:
gimp_channel_add_segment (mask, x0, i, j - x0, last);
break;
case GIMP_CHANNEL_OP_SUBTRACT:
gimp_channel_sub_segment (mask, x0, i, j - x0, last);
break;
default:
g_return_if_reached ();
break;
}
}
if (last != val)
{
x0 = j;
last = val;
/* because we are symetric accross the y axis we can
* skip ahead a bit if we are inside the ellipse
*/
if (val == 255 && j < cx)
j = cx + (cx - j) - 1;
last_val = val = 255;
cur_x = (ellipse_center_x +
(ellipse_center_x - cur_x) - 1 +
floor (straight_width));
}
}
if (last)
/* Time to change center? */
if (cur_x >= x + w / 2)
{
switch (op)
{
case GIMP_CHANNEL_OP_ADD:
case GIMP_CHANNEL_OP_REPLACE:
gimp_channel_add_segment (mask, x0, i, j - x0, last);
break;
case GIMP_CHANNEL_OP_SUBTRACT:
gimp_channel_sub_segment (mask, x0, i, j - x0, last);
break;
default:
g_return_if_reached ();
break;
}
ellipse_center_x = x + a + straight_width;
}
}
gimp_channel_combine_segment (mask, op, x_start,
cur_y, cur_x - x_start, last_val);
}
}
......
......@@ -20,34 +20,43 @@
#define __GIMP_CHANNEL_COMBINE_H__
void gimp_channel_add_segment (GimpChannel *mask,
gint x,
gint y,
gint width,
gint value);
void gimp_channel_sub_segment (GimpChannel *mask,
gint x,
gint y,
gint width,
gint value);
void gimp_channel_combine_rect (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h);
void gimp_channel_combine_ellipse (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h,
gboolean antialias);
void gimp_channel_combine_mask (GimpChannel *mask,
GimpChannel *add_on,
GimpChannelOps op,
gint off_x,
gint off_y);
void gimp_channel_add_segment (GimpChannel *mask,
gint x,
gint y,
gint width,
gint value);
void gimp_channel_sub_segment (GimpChannel *mask,
gint x,
gint y,
gint width,
gint value);
void gimp_channel_combine_rect (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h);
void gimp_channel_combine_ellipse (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h,
gboolean antialias);
void gimp_channel_combine_ellipse_rect (GimpChannel *mask,
GimpChannelOps op,
gint rect_x,
gint rect_y,
gint rect_w,
gint rect_h,
gdouble a,
gdouble b,
gboolean antialias);
void gimp_channel_combine_mask (GimpChannel *mask,
GimpChannel *add_on,
GimpChannelOps op,
gint off_x,
gint off_y);
#endif /* __GIMP_CHANNEL_COMBINE_H__ */
......@@ -140,6 +140,63 @@ gimp_channel_select_ellipse (GimpChannel *channel,
}
}
void
gimp_channel_select_round_rect (GimpChannel *channel,
gint x,
gint y,
gint w,
gint h,
gdouble corner_radius_x,
gdouble corner_radius_y,
GimpChannelOps op,
gboolean antialias,
gboolean feather,
gdouble feather_radius_x,
gdouble feather_radius_y,
gboolean push_undo)
{
g_return_if_fail (GIMP_IS_CHANNEL (channel));
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
if (push_undo)
gimp_channel_push_undo (channel, Q_("command|Rounded Rectangle Select"));
/* if applicable, replace the current selection */
if (op == GIMP_CHANNEL_OP_REPLACE)
gimp_channel_clear (channel, NULL, FALSE);
/* if feathering for rect, make a new mask with the
* rectangle and feather that with the old mask
*/
if (feather || op == GIMP_CHANNEL_OP_INTERSECT)
{
GimpItem *item = GIMP_ITEM (channel);
GimpChannel *add_on;
add_on = gimp_channel_new_mask (gimp_item_get_image (item),
gimp_item_width (item),
gimp_item_height (item));
gimp_channel_combine_ellipse_rect (add_on, GIMP_CHANNEL_OP_ADD,
x, y, w, h,
corner_radius_x, corner_radius_y,
antialias);
if (feather)
gimp_channel_feather (add_on,
feather_radius_x,
feather_radius_y,
FALSE /* no undo */);
gimp_channel_combine_mask (channel, add_on, op, 0, 0);
g_object_unref (add_on);
}
else
{
gimp_channel_combine_ellipse_rect (channel, op, x, y, w, h,
corner_radius_x, corner_radius_y,
antialias);
}
}
/* select by GimpScanConvert functions */
......
......@@ -43,7 +43,19 @@ void gimp_channel_select_ellipse (GimpChannel *channel,
gdouble feather_radius_x,
gdouble feather_radius_y,
gboolean push_undo);
void gimp_channel_select_round_rect (GimpChannel *channel,
gint x,
gint y,
gint w,
gint h,
gdouble corner_radius_y,
gdouble corner_radius_x,
GimpChannelOps op,
gboolean antialias,
gboolean feather,
gdouble feather_radius_x,
gdouble feather_radius_y,
gboolean push_undo);
/* select by GimpScanConvert functions */
......
......@@ -121,7 +121,7 @@ gimp_ellipse_select_tool_draw (GimpDrawTool *draw_tool)
FALSE,
x1, y1,
x2 - x1, y2 - y1,
0, 23040,
0, 360 * 64,
FALSE);
gimp_rectangle_tool_draw (draw_tool);
......
......@@ -26,22 +26,39 @@
#include "tools-types.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimpwidgets-utils.h"
#include "gimprectangleoptions.h"
#include "gimprectangleselectoptions.h"
#include "gimprectangleselecttool.h"
#include "gimptooloptions-gui.h"
#include "gimp-intl.h"
static void gimp_rect_select_options_rectangle_options_iface_init (GimpRectangleOptionsInterface *iface);
enum
{
PROP_ROUND_CORNERS = GIMP_RECTANGLE_OPTIONS_PROP_LAST + 1,
PROP_CORNER_RADIUS
};
static void gimp_rect_select_options_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_rect_select_options_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE_WITH_CODE (GimpRectSelectOptions, gimp_rect_select_options,
GIMP_TYPE_SELECTION_OPTIONS,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_RECTANGLE_OPTIONS,
gimp_rect_select_options_rectangle_options_iface_init))
NULL))
static void
......@@ -49,8 +66,18 @@ gimp_rect_select_options_class_init (GimpRectSelectOptionsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gimp_rectangle_options_set_property;
object_class->get_property = gimp_rectangle_options_get_property;
object_class->set_property = gimp_rect_select_options_set_property;
object_class->get_property = gimp_rect_select_options_get_property;
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_ROUND_CORNERS,
"round-corners", NULL,
FALSE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_CORNER_RADIUS,
"corner-radius", NULL,
0.0, 100.0, 5.0,
GIMP_PARAM_STATIC_STRINGS);
gimp_rectangle_options_install_properties (object_class);
}
......@@ -61,24 +88,110 @@ gimp_rect_select_options_init (GimpRectSelectOptions *options)
}
static void
gimp_rect_select_options_rectangle_options_iface_init (GimpRectangleOptionsInterface *iface)
gimp_rect_select_options_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpRectSelectOptions *options = GIMP_RECT_SELECT_OPTIONS (object);
switch (property_id)
{
case PROP_ROUND_CORNERS:
options->round_corners = g_value_get_boolean (value);
break;
case PROP_CORNER_RADIUS:
options->corner_radius = g_value_get_double (value);
break;
default:
gimp_rectangle_options_set_property (object, property_id, value, pspec);
break;
}
}
static void
gimp_rect_select_options_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpRectSelectOptions *options = GIMP_RECT_SELECT_OPTIONS (object);
switch (property_id)
{
case PROP_ROUND_CORNERS:
g_value_set_boolean (value, options->round_corners);
break;
case PROP_CORNER_RADIUS:
g_value_set_double (value, options->corner_radius);
break;
default:
gimp_rectangle_options_get_property (object, property_id, value, pspec);
break;
}
}
GtkWidget *
gimp_rect_select_options_gui (GimpToolOptions *tool_options)
{
GtkWidget *vbox = gimp_selection_options_gui (tool_options);
GtkWidget *vbox_rectangle;
/* rectangle options */
vbox_rectangle = gimp_rectangle_options_gui (tool_options);
gtk_box_pack_start (GTK_BOX (vbox), vbox_rectangle, FALSE, FALSE, 0);
gtk_widget_show (vbox_rectangle);
g_object_set (GIMP_RECTANGLE_OPTIONS (tool_options),
"highlight", FALSE,
NULL);
GObject *config = G_OBJECT (tool_options);
GtkWidget *vbox = gimp_selection_options_gui (tool_options);
/* the round corners frame */
if (tool_options->tool_info->tool_type == GIMP_TYPE_RECT_SELECT_TOOL)
{
GtkWidget *frame;
GtkWidget *button;