added gimp_scan_convert_compose(), an alternative to

2005-08-06  Sven Neumann  <sven@gimp.org>

	* app/core/gimpscanconvert.[ch]: added gimp_scan_convert_compose(),
	an alternative to gimp_scan_convert_render() that allows to compose
	strokes on a drawable.

	* app/tools/gimpforegroundselecttool.c: use the new function to
	get rid of temporary channels for applying the strokes. Also fixed
	calculation of stroke width from display scale.
This commit is contained in:
Sven Neumann 2005-08-06 14:54:13 +00:00 committed by Sven Neumann
parent 39f4e7bdb3
commit 68f09c7cb6
4 changed files with 173 additions and 56 deletions

View File

@ -1,3 +1,13 @@
2005-08-06 Sven Neumann <sven@gimp.org>
* app/core/gimpscanconvert.[ch]: added gimp_scan_convert_compose(),
an alternative to gimp_scan_convert_render() that allows to compose
strokes on a drawable.
* app/tools/gimpforegroundselecttool.c: use the new function to
get rid of temporary channels for applying the strokes. Also fixed
calculation of stroke width from display scale.
2005-08-06 Michael Natterer <mitch@gimp.org>
* app/widgets/gimpuimanager.c (gimp_ui_manager_ui_popup): applied
@ -7,7 +17,7 @@
2005-08-06 Sven Neumann <sven@gimp.org>
* app/display/gimpdisplayshell-draw.c (gimp_display_shell_draw_pen):
* app/tools/gimpforegroundselecttool.c: ccorrectly handle a stroke
* app/tools/gimpforegroundselecttool.c: correctly handle a stroke
consisting of just a single point.
2005-08-06 Sven Neumann <sven@gimp.org>

View File

@ -37,33 +37,35 @@
struct _GimpScanConvert
{
gdouble ratio_xy;
gdouble ratio_xy;
/* stuff necessary for the _add_polygons API... :-/ */
gboolean got_first;
gboolean need_closing;
GimpVector2 first;
GimpVector2 prev;
gboolean got_first;
gboolean need_closing;
GimpVector2 first;
GimpVector2 prev;
gboolean have_open;
guint num_nodes;
ArtVpath *vpath;
gboolean have_open;
guint num_nodes;
ArtVpath *vpath;
ArtSVP *svp; /* Sorted vector path
ArtSVP *svp; /* Sorted vector path
(extension no longer possible) */
/* stuff necessary for the rendering callback */
guchar *buf;
gint rowstride;
gint x0, x1;
gboolean antialias;
gboolean value;
GimpChannelOps op;
guchar *buf;
gint rowstride;
gint x0, x1;
gboolean antialias;
gboolean value;
};
/* private functions */
static void gimp_scan_convert_render_internal (GimpScanConvert *sc,
GimpChannelOps op,
TileManager *tile_manager,
gint off_x,
gint off_y,
@ -72,11 +74,16 @@ static void gimp_scan_convert_render_internal (GimpScanConvert *sc,
static void gimp_scan_convert_finish (GimpScanConvert *sc);
static void gimp_scan_convert_close_add_points (GimpScanConvert *sc);
static void gimp_scan_convert_render_callback (gpointer user_data,
gint y,
gint start_value,
ArtSVPRenderAAStep *steps,
gint n_steps);
static void gimp_scan_convert_render_callback (gpointer user_data,
gint y,
gint start_value,
ArtSVPRenderAAStep *steps,
gint n_steps);
static void gimp_scan_convert_compose_callback (gpointer user_data,
gint y,
gint start_value,
ArtSVPRenderAAStep *steps,
gint n_steps);
/* public functions */
@ -448,7 +455,7 @@ gimp_scan_convert_render (GimpScanConvert *sc,
g_return_if_fail (sc != NULL);
g_return_if_fail (tile_manager != NULL);
gimp_scan_convert_render_internal (sc,
gimp_scan_convert_render_internal (sc, GIMP_CHANNEL_OP_REPLACE,
tile_manager, off_x, off_y,
antialias, 255);
}
@ -463,13 +470,29 @@ gimp_scan_convert_render_value (GimpScanConvert *sc,
g_return_if_fail (sc != NULL);
g_return_if_fail (tile_manager != NULL);
gimp_scan_convert_render_internal (sc,
gimp_scan_convert_render_internal (sc, GIMP_CHANNEL_OP_REPLACE,
tile_manager, off_x, off_y,
FALSE, value);
}
void
gimp_scan_convert_compose (GimpScanConvert *sc,
GimpChannelOps op,
TileManager *tile_manager,
gint off_x,
gint off_y)
{
g_return_if_fail (sc != NULL);
g_return_if_fail (tile_manager != NULL);
gimp_scan_convert_render_internal (sc, op,
tile_manager, off_x, off_y,
FALSE, 255);
}
static void
gimp_scan_convert_render_internal (GimpScanConvert *sc,
GimpChannelOps op,
TileManager *tile_manager,
gint off_x,
gint off_y,
@ -478,6 +501,7 @@ gimp_scan_convert_render_internal (GimpScanConvert *sc,
{
PixelRegion maskPR;
gpointer pr;
gpointer callback;
gimp_scan_convert_finish (sc);
@ -493,6 +517,11 @@ gimp_scan_convert_render_internal (GimpScanConvert *sc,
sc->antialias = antialias;
sc->value = value;
sc->op = op;
callback = (op == GIMP_CHANNEL_OP_REPLACE ?
gimp_scan_convert_render_callback :
gimp_scan_convert_compose_callback);
for (pr = pixel_regions_register (1, &maskPR);
pr != NULL;
@ -508,8 +537,7 @@ gimp_scan_convert_render_internal (GimpScanConvert *sc,
off_y + maskPR.y,
sc->x1,
off_y + maskPR.y + maskPR.h,
gimp_scan_convert_render_callback, sc);
callback, sc);
}
}
@ -639,3 +667,87 @@ gimp_scan_convert_render_callback (gpointer user_data,
#undef VALUE_TO_PIXEL
}
static inline void
compose (GimpChannelOps op,
guchar *buf,
guchar value,
gint len)
{
switch (op)
{
case GIMP_CHANNEL_OP_ADD:
if (value)
memset (buf, value, len);
break;
case GIMP_CHANNEL_OP_SUBTRACT:
if (value)
memset (buf, 0, len);
break;
case GIMP_CHANNEL_OP_REPLACE:
memset (buf, value, len);
break;
case GIMP_CHANNEL_OP_INTERSECT:
do
{
if (*buf)
*buf = value;
buf++;
}
while (--len);
break;
}
}
static void
gimp_scan_convert_compose_callback (gpointer user_data,
gint y,
gint start_value,
ArtSVPRenderAAStep *steps,
gint n_steps)
{
GimpScanConvert *sc = user_data;
gint cur_value = start_value;
gint k, run_x0, run_x1;
#define VALUE_TO_PIXEL(x) (((x) & (1 << 23) ? 255 : 0))
if (n_steps > 0)
{
run_x1 = steps[0].x;
if (run_x1 > sc->x0)
compose (sc->op, sc->buf,
VALUE_TO_PIXEL (cur_value),
run_x1 - sc->x0);
for (k = 0; k < n_steps - 1; k++)
{
cur_value += steps[k].delta;
run_x0 = run_x1;
run_x1 = steps[k + 1].x;
if (run_x1 > run_x0)
compose (sc->op, sc->buf + run_x0 - sc->x0,
VALUE_TO_PIXEL (cur_value),
run_x1 - run_x0);
}
cur_value += steps[k].delta;
if (sc->x1 > run_x1)
compose (sc->op, sc->buf + run_x1 - sc->x0,
VALUE_TO_PIXEL (cur_value),
sc->x1 - run_x1);
}
else
{
compose (sc->op, sc->buf,
VALUE_TO_PIXEL (cur_value),
sc->x1 - sc->x0);
}
sc->buf += sc->rowstride;
#undef VALUE_TO_PIXEL
}

View File

@ -87,5 +87,14 @@ void gimp_scan_convert_render_value (GimpScanConvert *sc,
gint off_y,
guchar value);
/* This is a variant of gimp_scan_convert_render() that composes the
* (unaliased) scan conversion with the existing drawable content.
*/
void gimp_scan_convert_compose (GimpScanConvert *sc,
GimpChannelOps op,
TileManager *tile_manager,
gint off_x,
gint off_y);
#endif /* __GIMP_SCAN_CONVERT_H__ */

View File

@ -106,11 +106,11 @@ static void gimp_foreground_select_tool_apply (GimpForegroundSelectTool *fg
GimpDisplay *gdisp);
static void gimp_foreground_select_tool_stroke (GimpChannel *mask,
GimpDisplay *gdisp,
FgSelectStroke *stroke);
static void gimp_foreground_select_tool_push_stroke (GimpForegroundSelectTool *fg_select,
GimpForegroundSelectOptions *options);
static void gimp_foreground_select_tool_push_stroke (GimpForegroundSelectTool *fg_select,
GimpDisplay *gdisp,
GimpForegroundSelectOptions *options);
static GimpFreeSelectToolClass *parent_class = NULL;
@ -418,7 +418,7 @@ gimp_foreground_select_tool_button_release (GimpTool *tool,
gimp_tool_control_halt (tool->control);
gimp_foreground_select_tool_push_stroke (fg_select, options);
gimp_foreground_select_tool_push_stroke (fg_select, gdisp, options);
gimp_foreground_select_tool_select (GIMP_FREE_SELECT_TOOL (tool), gdisp);
}
@ -524,15 +524,15 @@ gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel,
0, 0, 127);
gimp_scan_convert_free (scan_convert);
/* apply foreground and background markers */
for (list = fg_select->strokes; list; list = list->next)
gimp_foreground_select_tool_stroke (mask, list->data);
/* restrict working area to double the size of the bounding box */
gimp_channel_bounds (mask, &x, &y, &x2, &y2);
width = x2 - x;
height = y2 - y;
/* apply foreground and background markers */
for (list = fg_select->strokes; list; list = list->next)
gimp_foreground_select_tool_stroke (mask, gdisp, list->data);
/* restrict working area to double the size of the bounding box */
x = MAX (0, x - width / 2);
y = MAX (0, y - height / 2);
width = MIN (width * 2, gimp_item_width (GIMP_ITEM (mask)) - x);
@ -627,20 +627,9 @@ gimp_foreground_select_tool_apply (GimpForegroundSelectTool *fg_select,
static void
gimp_foreground_select_tool_stroke (GimpChannel *mask,
GimpDisplay *gdisp,
FgSelectStroke *stroke)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (gdisp->shell);
GimpScanConvert *scan_convert = gimp_scan_convert_new ();
GimpItem *item = GIMP_ITEM (mask);
GimpChannel *channel = gimp_channel_new (gimp_item_get_image (item),
gimp_item_width (item),
gimp_item_height (item),
"tmp", NULL);
/* FIXME: We should be able to get away w/o a temporary drawable
* by doing some changes to GimpScanConvert.
*/
GimpScanConvert *scan_convert = gimp_scan_convert_new ();
if (stroke->num_points == 1)
{
@ -661,34 +650,31 @@ gimp_foreground_select_tool_stroke (GimpChannel *mask,
}
gimp_scan_convert_stroke (scan_convert,
SCALEFACTOR_Y (shell) * stroke->width,
stroke->width,
GIMP_JOIN_MITER, GIMP_CAP_ROUND, 10.0,
0.0, NULL);
gimp_scan_convert_render (scan_convert,
gimp_drawable_data (GIMP_DRAWABLE (channel)),
0, 0, FALSE);
gimp_scan_convert_free (scan_convert);
gimp_channel_combine_mask (mask, channel,
gimp_scan_convert_compose (scan_convert,
stroke->background ?
GIMP_CHANNEL_OP_SUBTRACT : GIMP_CHANNEL_OP_ADD,
gimp_drawable_data (GIMP_DRAWABLE (mask)),
0, 0);
g_object_unref (channel);
gimp_scan_convert_free (scan_convert);
}
static void
gimp_foreground_select_tool_push_stroke (GimpForegroundSelectTool *fg_select,
GimpDisplay *gdisp,
GimpForegroundSelectOptions *options)
{
FgSelectStroke *stroke;
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (gdisp->shell);
FgSelectStroke *stroke;
g_return_if_fail (fg_select->stroke != NULL);
stroke = g_new (FgSelectStroke, 1);
stroke->background = options->background;
stroke->width = options->stroke_width;
stroke->width = options->stroke_width / SCALEFACTOR_Y (shell);
stroke->num_points = fg_select->stroke->len;
stroke->points = (GimpVector2 *) g_array_free (fg_select->stroke, FALSE);