Commit 39545263 authored by Simon Budig's avatar Simon Budig Committed by Simon Budig

app/pdb/tools_cmds.c app/tools/gimperasertool.c app/tools/gimperasertool.h

2001-10-30  Simon Budig   <simon@gimp.org>

        * app/pdb/tools_cmds.c
        * app/tools/gimperasertool.c
        * app/tools/gimperasertool.h
        * tools/pdbgen/pdb/tools.pdb

        Added a "color erase" feature to the eraser. This is ported from
        the colortoalpha plugin and is utterly cool.

        The "Color Erase" Option should be disabled when the drawable
        has no alpha channel, however, I have no idea how to do this.
parent 008742c2
2001-10-30 Simon Budig <simon@gimp.org>
* app/pdb/tools_cmds.c
* app/tools/gimperasertool.c
* app/tools/gimperasertool.h
* tools/pdbgen/pdb/tools.pdb
Added a "color erase" feature to the eraser. This is ported from
the colortoalpha plugin and is utterly cool.
The "Color Erase" Option should be disabled when the drawable
has no alpha channel, however, I have no idea how to do this.
2001-10-29 Sven Neumann <sven@gimp.org>
* app/base/temp-buf.c (temp_buf_to_gray): rewrote so gcc-3.0 doesn't
......
......@@ -25,6 +25,7 @@
#include "tools-types.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "paint-funcs/paint-funcs.h"
......@@ -40,10 +41,14 @@
#include "libgimp/gimpintl.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpcolor/gimprgb.h"
#define ERASER_DEFAULT_HARD FALSE
#define ERASER_DEFAULT_INCREMENTAL FALSE
#define ERASER_DEFAULT_ANTI_ERASE FALSE
#define ERASER_DEFAULT_COLOR_ERASE FALSE
typedef struct _EraserOptions EraserOptions;
......@@ -59,6 +64,10 @@ struct _EraserOptions
gboolean anti_erase;
gboolean anti_erase_d;
GtkWidget *anti_erase_w;
gboolean color_erase;
gboolean color_erase_d;
GtkWidget *color_erase_w;
};
......@@ -78,16 +87,20 @@ static void gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
PaintPressureOptions *pressure_options,
gboolean hard,
gboolean incremental,
gboolean anti_erase);
gboolean anti_erase,
gboolean color_erase);
static EraserOptions * gimp_eraser_tool_options_new (void);
static void gimp_eraser_tool_options_reset (GimpToolOptions *tool_options);
static void gimp_eraser_tool_colortoalpha (GimpRGB *src,
const GimpRGB *color);
/* local variables */
static gboolean non_gui_hard = ERASER_DEFAULT_HARD;
static gboolean non_gui_incremental = ERASER_DEFAULT_INCREMENTAL;
static gboolean non_gui_anti_erase = ERASER_DEFAULT_ANTI_ERASE;
static gboolean non_gui_color_erase = ERASER_DEFAULT_COLOR_ERASE;
static EraserOptions *eraser_options = NULL;
......@@ -214,6 +227,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
gboolean hard;
gboolean incremental;
gboolean anti_erase;
gboolean color_erase;
if (eraser_options)
{
......@@ -221,6 +235,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
hard = eraser_options->hard;
incremental = eraser_options->paint_options.incremental;
anti_erase = eraser_options->anti_erase;
color_erase = eraser_options->color_erase;
}
else
{
......@@ -228,6 +243,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
hard = non_gui_hard;
incremental = non_gui_incremental;
anti_erase = non_gui_anti_erase;
color_erase = non_gui_color_erase;
}
switch (state)
......@@ -241,7 +257,8 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
pressure_options,
hard,
incremental,
anti_erase);
anti_erase,
color_erase);
break;
case FINISH_PAINT:
......@@ -258,7 +275,8 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
PaintPressureOptions *pressure_options,
gboolean hard,
gboolean incremental,
gboolean anti_erase)
gboolean anti_erase,
gboolean color_erase)
{
GimpImage *gimage;
GimpContext *context;
......@@ -266,6 +284,13 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
TempBuf *area;
guchar col[MAX_CHANNELS];
gdouble scale;
TempBuf *orig;
gint x, y, x1, y1, x2, y2;
PixelRegion srcPR, destPR;
gpointer pr;
GimpRGB bgcolor, color;
guchar *s, *d;
if (! (gimage = gimp_drawable_gimage (drawable)))
return;
......@@ -283,26 +308,119 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
if (! (area = gimp_paint_tool_get_paint_area (paint_tool, drawable, scale)))
return;
/* set the alpha channel */
col[area->bytes - 1] = OPAQUE_OPACITY;
/* color the pixels */
color_pixels (temp_buf_data (area), col,
area->width * area->height, area->bytes);
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is being worked on */
gimp_paint_tool_paste_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
anti_erase ? ANTI_ERASE_MODE : ERASE_MODE,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
if (anti_erase || !color_erase)
{
/* set the alpha channel */
col[area->bytes - 1] = OPAQUE_OPACITY;
/* color the pixels */
color_pixels (temp_buf_data (area), col,
area->width * area->height, area->bytes);
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is being
* worked on */
gimp_paint_tool_paste_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
anti_erase ? (color_erase ? BEHIND_MODE : ANTI_ERASE_MODE ) : ERASE_MODE,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
}
else
{
/* This is Simons evil Eraser Hack. Code is borrowed from the
* colortoalpha plugin by Seth Burgess. Algorithm has been
* described on IRC by clahey.
*/
#warning "Simons evil Eraser Hack"
gimp_rgb_set_uchar (&bgcolor, col[0], col[1], col[2]);
if (!gimp_drawable_has_alpha (drawable))
return;
/* is this necessary? */
x1 = CLAMP (area->x, 0, gimp_drawable_width (drawable));
y1 = CLAMP (area->y, 0, gimp_drawable_height (drawable));
x2 = CLAMP (area->x + area->width,
0, gimp_drawable_width (drawable));
y2 = CLAMP (area->y + area->height,
0, gimp_drawable_height (drawable));
if (!(x2 - x1) || !(y2 - y1))
return;
/* get the original image */
orig = gimp_paint_tool_get_orig_image (paint_tool, drawable, x1, y1, x2, y2);
srcPR.bytes = orig->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = x2 - x1;
srcPR.h = y2 - y1;
srcPR.rowstride = srcPR.bytes * orig->width;
srcPR.data = temp_buf_data (orig);
/* configure the destination */
destPR.bytes = area->bytes;
destPR.x = 0; destPR.y = 0;
destPR.w = srcPR.w;
destPR.h = srcPR.h;
destPR.rowstride = destPR.bytes * area->width;
destPR.data = temp_buf_data (area);
/* I am not sure, if this really is necessary.
* Probably the orig Tempbuf has the same size as the area Tempbuf */
pr = pixel_regions_register (2, &srcPR, &destPR);
for (; pr != NULL; pr = pixel_regions_process (pr))
{
s = srcPR.data;
d = destPR.data;
for (y = 0; y < destPR.h; y++)
{
for (x = 0; x < destPR.w; x++)
{
gimp_rgba_set_uchar (&color,
s[x*srcPR.bytes ],
s[x*srcPR.bytes + 1],
s[x*srcPR.bytes + 2],
s[x*srcPR.bytes + 3]);
gimp_eraser_tool_colortoalpha (&color, &bgcolor);
gimp_rgba_get_uchar (&color,
&(d[x*destPR.bytes ]),
&(d[x*destPR.bytes + 1]),
&(d[x*destPR.bytes + 2]),
&(d[x*destPR.bytes + 3]));
}
s += srcPR.rowstride;
d += destPR.rowstride;
}
}
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is
* being worked on */
gimp_paint_tool_replace_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
}
}
......@@ -316,6 +434,7 @@ eraser_non_gui_default (GimpDrawable *drawable,
gboolean hard = ERASER_DEFAULT_HARD;
gboolean incremental = ERASER_DEFAULT_INCREMENTAL;
gboolean anti_erase = ERASER_DEFAULT_ANTI_ERASE;
gboolean color_erase = ERASER_DEFAULT_COLOR_ERASE;
EraserOptions *options = eraser_options;
......@@ -324,10 +443,11 @@ eraser_non_gui_default (GimpDrawable *drawable,
hard = options->hard;
incremental = options->paint_options.incremental;
anti_erase = options->anti_erase;
color_erase = options->color_erase;
}
return eraser_non_gui (drawable, num_strokes, stroke_array,
hard, incremental, anti_erase);
hard, incremental, anti_erase, color_erase);
}
gboolean
......@@ -336,7 +456,8 @@ eraser_non_gui (GimpDrawable *drawable,
gdouble *stroke_array,
gint hard,
gint incremental,
gboolean anti_erase)
gboolean anti_erase,
gboolean color_erase)
{
static GimpEraserTool *non_gui_eraser = NULL;
......@@ -357,6 +478,7 @@ eraser_non_gui (GimpDrawable *drawable,
non_gui_hard = hard;
non_gui_incremental = incremental;
non_gui_anti_erase = anti_erase;
non_gui_color_erase = color_erase;
paint_tool->startx = paint_tool->lastx = stroke_array[0];
paint_tool->starty = paint_tool->lasty = stroke_array[1];
......@@ -398,6 +520,7 @@ gimp_eraser_tool_options_new (void)
options->hard = options->hard_d = ERASER_DEFAULT_HARD;
options->anti_erase = options->anti_erase_d = ERASER_DEFAULT_ANTI_ERASE;
options->color_erase = options->color_erase_d = ERASER_DEFAULT_COLOR_ERASE;
/* the main vbox */
vbox = ((GimpToolOptions *) options)->main_vbox;
......@@ -422,6 +545,16 @@ gimp_eraser_tool_options_new (void)
options->anti_erase_d);
gtk_widget_show (options->anti_erase_w);
/* the color_erase toggle */
options->color_erase_w = gtk_check_button_new_with_label (_("Color Erase"));
gtk_box_pack_start (GTK_BOX (vbox), options->color_erase_w, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (options->color_erase_w), "toggled",
G_CALLBACK (gimp_toggle_button_update),
&options->color_erase);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->color_erase_w),
options->color_erase_d);
gtk_widget_show (options->color_erase_w);
return options;
}
......@@ -438,4 +571,71 @@ gimp_eraser_tool_options_reset (GimpToolOptions *tool_options)
options->hard_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->anti_erase_w),
options->anti_erase_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->color_erase_w),
options->color_erase_d);
}
static void
gimp_eraser_tool_colortoalpha (GimpRGB *src,
const GimpRGB *color)
{
GimpRGB alpha;
alpha.a = src->a;
if (color->r < 0.0001)
alpha.r = src->r;
else if ( src->r > color->r )
alpha.r = (src->r - color->r) / (1.0 - color->r);
else if (src->r < color->r)
alpha.r = (color->r - src->r) / color->r;
else alpha.r = 0.0;
if (color->g < 0.0001)
alpha.g = src->g;
else if ( src->g > color->g )
alpha.g = (src->g - color->g) / (1.0 - color->g);
else if ( src->g < color->g )
alpha.g = (color->g - src->g) / (color->g);
else alpha.g = 0.0;
if (color->b < 0.0001)
alpha.b = src->b;
else if ( src->b > color->b )
alpha.b = (src->b - color->b) / (1.0 - color->b);
else if ( src->b < color->b )
alpha.b = (color->b - src->b) / (color->b);
else alpha.b = 0.0;
if ( alpha.r > alpha.g )
{
if ( alpha.r > alpha.b )
{
src->a = alpha.r;
}
else
{
src->a = alpha.b;
}
}
else if ( alpha.g > alpha.b )
{
src->a = alpha.g;
}
else
{
src->a = alpha.b;
}
if (src->a < 0.0001)
return;
src->r = (src->r - color->r) / src->a + color->r;
src->g = (src->g - color->g) / src->a + color->g;
src->b = (src->b - color->b) / src->a + color->b;
src->a *= alpha.a;
}
......@@ -55,7 +55,8 @@ gboolean eraser_non_gui (GimpDrawable *drawable,
gdouble *stroke_array,
gint hardness,
gint method,
gboolean anti_erase);
gboolean anti_erase,
gboolean color_erase);
gboolean eraser_non_gui_default (GimpDrawable *paint_core,
gint num_strokes,
gdouble *stroke_array);
......
......@@ -1148,7 +1148,7 @@ eraser_invoker (Gimp *gimp,
success = FALSE;
if (success)
success = eraser_non_gui (drawable, num_strokes, strokes, hardness, method, TRUE);
success = eraser_non_gui (drawable, num_strokes, strokes, hardness, method, TRUE, FALSE);
return procedural_db_return_args (&eraser_proc, success);
}
......
......@@ -25,6 +25,7 @@
#include "tools-types.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "paint-funcs/paint-funcs.h"
......@@ -40,10 +41,14 @@
#include "libgimp/gimpintl.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpcolor/gimprgb.h"
#define ERASER_DEFAULT_HARD FALSE
#define ERASER_DEFAULT_INCREMENTAL FALSE
#define ERASER_DEFAULT_ANTI_ERASE FALSE
#define ERASER_DEFAULT_COLOR_ERASE FALSE
typedef struct _EraserOptions EraserOptions;
......@@ -59,6 +64,10 @@ struct _EraserOptions
gboolean anti_erase;
gboolean anti_erase_d;
GtkWidget *anti_erase_w;
gboolean color_erase;
gboolean color_erase_d;
GtkWidget *color_erase_w;
};
......@@ -78,16 +87,20 @@ static void gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
PaintPressureOptions *pressure_options,
gboolean hard,
gboolean incremental,
gboolean anti_erase);
gboolean anti_erase,
gboolean color_erase);
static EraserOptions * gimp_eraser_tool_options_new (void);
static void gimp_eraser_tool_options_reset (GimpToolOptions *tool_options);
static void gimp_eraser_tool_colortoalpha (GimpRGB *src,
const GimpRGB *color);
/* local variables */
static gboolean non_gui_hard = ERASER_DEFAULT_HARD;
static gboolean non_gui_incremental = ERASER_DEFAULT_INCREMENTAL;
static gboolean non_gui_anti_erase = ERASER_DEFAULT_ANTI_ERASE;
static gboolean non_gui_color_erase = ERASER_DEFAULT_COLOR_ERASE;
static EraserOptions *eraser_options = NULL;
......@@ -214,6 +227,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
gboolean hard;
gboolean incremental;
gboolean anti_erase;
gboolean color_erase;
if (eraser_options)
{
......@@ -221,6 +235,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
hard = eraser_options->hard;
incremental = eraser_options->paint_options.incremental;
anti_erase = eraser_options->anti_erase;
color_erase = eraser_options->color_erase;
}
else
{
......@@ -228,6 +243,7 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
hard = non_gui_hard;
incremental = non_gui_incremental;
anti_erase = non_gui_anti_erase;
color_erase = non_gui_color_erase;
}
switch (state)
......@@ -241,7 +257,8 @@ gimp_eraser_tool_paint (GimpPaintTool *paint_tool,
pressure_options,
hard,
incremental,
anti_erase);
anti_erase,
color_erase);
break;
case FINISH_PAINT:
......@@ -258,7 +275,8 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
PaintPressureOptions *pressure_options,
gboolean hard,
gboolean incremental,
gboolean anti_erase)
gboolean anti_erase,
gboolean color_erase)
{
GimpImage *gimage;
GimpContext *context;
......@@ -266,6 +284,13 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
TempBuf *area;
guchar col[MAX_CHANNELS];
gdouble scale;
TempBuf *orig;
gint x, y, x1, y1, x2, y2;
PixelRegion srcPR, destPR;
gpointer pr;
GimpRGB bgcolor, color;
guchar *s, *d;
if (! (gimage = gimp_drawable_gimage (drawable)))
return;
......@@ -283,26 +308,119 @@ gimp_eraser_tool_motion (GimpPaintTool *paint_tool,
if (! (area = gimp_paint_tool_get_paint_area (paint_tool, drawable, scale)))
return;
/* set the alpha channel */
col[area->bytes - 1] = OPAQUE_OPACITY;
/* color the pixels */
color_pixels (temp_buf_data (area), col,
area->width * area->height, area->bytes);
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is being worked on */
gimp_paint_tool_paste_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
anti_erase ? ANTI_ERASE_MODE : ERASE_MODE,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
if (anti_erase || !color_erase)
{
/* set the alpha channel */
col[area->bytes - 1] = OPAQUE_OPACITY;
/* color the pixels */
color_pixels (temp_buf_data (area), col,
area->width * area->height, area->bytes);
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is being
* worked on */
gimp_paint_tool_paste_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
anti_erase ? (color_erase ? BEHIND_MODE : ANTI_ERASE_MODE ) : ERASE_MODE,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
}
else
{
/* This is Simons evil Eraser Hack. Code is borrowed from the
* colortoalpha plugin by Seth Burgess. Algorithm has been
* described on IRC by clahey.
*/
#warning "Simons evil Eraser Hack"
gimp_rgb_set_uchar (&bgcolor, col[0], col[1], col[2]);
if (!gimp_drawable_has_alpha (drawable))
return;
/* is this necessary? */
x1 = CLAMP (area->x, 0, gimp_drawable_width (drawable));
y1 = CLAMP (area->y, 0, gimp_drawable_height (drawable));
x2 = CLAMP (area->x + area->width,
0, gimp_drawable_width (drawable));
y2 = CLAMP (area->y + area->height,
0, gimp_drawable_height (drawable));
if (!(x2 - x1) || !(y2 - y1))
return;
/* get the original image */
orig = gimp_paint_tool_get_orig_image (paint_tool, drawable, x1, y1, x2, y2);
srcPR.bytes = orig->bytes;
srcPR.x = 0; srcPR.y = 0;
srcPR.w = x2 - x1;
srcPR.h = y2 - y1;
srcPR.rowstride = srcPR.bytes * orig->width;
srcPR.data = temp_buf_data (orig);
/* configure the destination */
destPR.bytes = area->bytes;
destPR.x = 0; destPR.y = 0;
destPR.w = srcPR.w;
destPR.h = srcPR.h;
destPR.rowstride = destPR.bytes * area->width;
destPR.data = temp_buf_data (area);
/* I am not sure, if this really is necessary.
* Probably the orig Tempbuf has the same size as the area Tempbuf */
pr = pixel_regions_register (2, &srcPR, &destPR);
for (; pr != NULL; pr = pixel_regions_process (pr))
{
s = srcPR.data;
d = destPR.data;
for (y = 0; y < destPR.h; y++)
{
for (x = 0; x < destPR.w; x++)
{
gimp_rgba_set_uchar (&color,
s[x*srcPR.bytes ],
s[x*srcPR.bytes + 1],
s[x*srcPR.bytes + 2],
s[x*srcPR.bytes + 3]);
gimp_eraser_tool_colortoalpha (&color, &bgcolor);
gimp_rgba_get_uchar (&color,
&(d[x*destPR.bytes ]),
&(d[x*destPR.bytes + 1]),
&(d[x*destPR.bytes + 2]),
&(d[x*destPR.bytes + 3]));
}
s += srcPR.rowstride;
d += destPR.rowstride;
}
}
opacity = 255 * gimp_context_get_opacity (context);
if (pressure_options->opacity)
opacity = opacity * 2.0 * paint_tool->curpressure;
/* paste the newly painted canvas to the gimage which is
* being worked on */
gimp_paint_tool_replace_canvas (paint_tool, drawable,
MIN (opacity, 255),
gimp_context_get_opacity (context) * 255,
hard ? HARD : (pressure_options->pressure ? PRESSURE : SOFT),
scale,
incremental ? INCREMENTAL : CONSTANT);
}
}
......@@ -316,6 +434,7 @@ eraser_non_gui_default (GimpDrawable *drawable,
gboolean hard = ERASER_DEFAULT_HARD;
gboolean incremental = ERASER_DEFAULT_INCREMENTAL;
gboolean anti_erase = ERASER_DEFAULT_ANTI_ERASE;
gboolean color_erase = ERASER_DEFAULT_COLOR_ERASE;
EraserOptions *options = eraser_options;
......@@ -324,10 +443,11 @@ eraser_non_gui_default (GimpDrawable *drawable,
hard = options->hard;
incremental = options->paint_options.incremental;
anti_erase = options->anti_erase;
color_erase = options->color_erase;
}
return eraser_non_gui (drawable, num_strokes, stroke_array,
hard, incremental, anti_erase);
hard, incremental, anti_erase, color_erase);
}
gboolean
......@@ -336,7 +456,8 @@ eraser_non_gui (GimpDrawable *drawable,
gdouble *stroke_array,
gint hard,