Add "Tolerance" parameter for some tools like Magic Wand and Paint Bucket (Trent Gamblin idea).

This commit is contained in:
David Capello 2010-08-13 10:41:13 -03:00
parent 3ea39bb211
commit dec4bbc6a4
10 changed files with 93 additions and 28 deletions

View File

@ -31,9 +31,12 @@
<label text="Angle:" />
<slider min="0" max="180" name="brush_angle" cell_align="horizontal" />
<label text="Opacity:" />
<label text="Opacity:" name="opacity_label" />
<slider min="0" max="255" name="opacity" cell_align="horizontal" />
<label text="Tolerance:" name="tolerance_label" />
<slider min="0" max="255" name="tolerance" cell_align="horizontal" />
</grid>
<box name="brush_preview_box" /><!-- custom widget -->
</box>

View File

@ -51,6 +51,7 @@ static bool brush_size_slider_change_hook(JWidget widget, void *data);
static bool brush_angle_slider_change_hook(JWidget widget, void *data);
static bool brush_type_change_hook(JWidget widget, void *data);
static bool opacity_slider_change_hook(JWidget widget, void *data);
static bool tolerance_slider_change_hook(JWidget widget, void *data);
static bool spray_width_slider_change_hook(JWidget widget, void *data);
static bool air_speed_slider_change_hook(JWidget widget, void *data);
static bool tiled_check_change_hook(JWidget widget, void *data);
@ -94,6 +95,7 @@ static void on_current_tool_change()
Widget* brush_type = window->findChild("brush_type");
Widget* brush_preview = window->findChild("brush_preview");
Widget* opacity = window->findChild("opacity");
Widget* tolerance = window->findChild("tolerance");
Widget* spray_width = window->findChild("spray_width");
Widget* air_speed = window->findChild("air_speed");
@ -106,6 +108,7 @@ static void on_current_tool_change()
->getToolSettings(current_tool);
jslider_set_value(opacity, tool_settings->getOpacity());
jslider_set_value(tolerance, tool_settings->getTolerance());
jslider_set_value(brush_size, tool_settings->getPen()->getSize());
jslider_set_value(brush_angle, tool_settings->getPen()->getAngle());
jslider_set_value(spray_width, tool_settings->getSprayWidth());
@ -139,7 +142,7 @@ ConfigureTools::ConfigureTools()
void ConfigureTools::onExecute(Context* context)
{
JWidget tiled, tiled_x, tiled_y, snap_to_grid, view_grid, pixel_grid, set_grid;
JWidget brush_size, brush_angle, opacity;
JWidget brush_size, brush_angle, opacity, tolerance;
JWidget spray_width, air_speed;
JWidget brush_preview_box;
JWidget brush_type_box, brush_type;
@ -169,6 +172,7 @@ void ConfigureTools::onExecute(Context* context)
"brush_size", &brush_size,
"brush_angle", &brush_angle,
"opacity", &opacity,
"tolerance", &tolerance,
"spray_width", &spray_width,
"air_speed", &air_speed,
"brush_preview_box", &brush_preview_box,
@ -244,6 +248,7 @@ void ConfigureTools::onExecute(Context* context)
HOOK(brush_angle, JI_SIGNAL_SLIDER_CHANGE, brush_angle_slider_change_hook, brush_preview);
HOOK(brush_type, SIGNAL_GROUP_BUTTON_CHANGE, brush_type_change_hook, brush_preview);
HOOK(opacity, JI_SIGNAL_SLIDER_CHANGE, opacity_slider_change_hook, 0);
HOOK(tolerance, JI_SIGNAL_SLIDER_CHANGE, tolerance_slider_change_hook, 0);
HOOK(air_speed, JI_SIGNAL_SLIDER_CHANGE, air_speed_slider_change_hook, 0);
HOOK(spray_width, JI_SIGNAL_SLIDER_CHANGE, spray_width_slider_change_hook, 0);
HOOK(check_onionskin, JI_SIGNAL_CHECK_CHANGE, onionskin_check_change_hook, 0);
@ -382,6 +387,14 @@ static bool opacity_slider_change_hook(JWidget widget, void *data)
return false;
}
static bool tolerance_slider_change_hook(JWidget widget, void *data)
{
ISettings* settings = UIContext::instance()->getSettings();
Tool* current_tool = settings->getCurrentTool();
settings->getToolSettings(current_tool)->setTolerance(jslider_get_value(widget));
return false;
}
static bool spray_width_slider_change_hook(JWidget widget, void *data)
{
ISettings* settings = UIContext::instance()->getSettings();

View File

@ -42,7 +42,7 @@ double algo_spline_get_tan(double x0, double y0, double x1, double y1,
double x2, double y2, double x3, double y3,
double in_x);
void algo_floodfill(Image* image, int x, int y, void* data, AlgoHLine proc);
void algo_floodfill(Image* image, int x, int y, int tolerance, void* data, AlgoHLine proc);
void algo_polygon(int vertices, const int* points, void* data, AlgoHLine proc);

View File

@ -48,19 +48,54 @@ static int flood_count; /* number of flooded segments */
#define FLOOD_LINE(c) (((FLOODED_LINE *)_scratch_mem) + c)
static inline bool color_equal_32(ase_uint32 c1, ase_uint32 c2)
static inline bool color_equal_32(ase_uint32 c1, ase_uint32 c2, int tolerance)
{
return (c1 == c2) || (_rgba_geta(c1) == 0 && _rgba_geta(c2) == 0);
if (tolerance == 0)
return (c1 == c2) || (_rgba_geta(c1) == 0 && _rgba_geta(c2) == 0);
else {
int r1 = _rgba_getr(c1);
int g1 = _rgba_getg(c1);
int b1 = _rgba_getb(c1);
int a1 = _rgba_geta(c1);
int r2 = _rgba_getr(c2);
int g2 = _rgba_getg(c2);
int b2 = _rgba_getb(c2);
int a2 = _rgba_geta(c2);
if (a1 == 0 && a2 == 0)
return true;
return ((ABS(r1-r2) <= tolerance) &&
(ABS(g1-g2) <= tolerance) &&
(ABS(b1-b2) <= tolerance) &&
(ABS(a1-a2) <= tolerance));
}
}
static inline bool color_equal_16(ase_uint16 c1, ase_uint16 c2)
static inline bool color_equal_16(ase_uint16 c1, ase_uint16 c2, int tolerance)
{
return (c1 == c2) || (_graya_geta(c1) == 0 && _graya_geta(c2) == 0);
if (tolerance == 0)
return (c1 == c2) || (_graya_geta(c1) == 0 && _graya_geta(c2) == 0);
else {
int k1 = _graya_getv(c1);
int a1 = _graya_geta(c1);
int k2 = _graya_getv(c2);
int a2 = _graya_geta(c2);
if (a1 == 0 && a2 == 0)
return true;
return ((ABS(k1-k2) <= tolerance) &&
(ABS(a1-a2) <= tolerance));
}
}
static inline bool color_equal_8(ase_uint8 c1, ase_uint8 c2)
static inline bool color_equal_8(ase_uint8 c1, ase_uint8 c2, int tolerance)
{
return (c1 == c2);
if (tolerance == 0)
return (c1 == c2);
else
return ABS(c1-c2) <= tolerance;
}
@ -71,7 +106,7 @@ static inline bool color_equal_8(ase_uint8 c1, ase_uint8 c2)
* the part of the line which it has dealt with.
*/
static int flooder (Image *image, int x, int y,
int src_color, void *data, AlgoHLine proc)
int src_color, int tolerance, void *data, AlgoHLine proc)
{
FLOODED_LINE *p;
int left = 0, right = 0;
@ -84,18 +119,18 @@ static int flooder (Image *image, int x, int y,
ase_uint32 *address = ((ase_uint32 **)image->line)[y];
/* check start pixel */
if (!color_equal_32((int)*(address+x), src_color))
if (!color_equal_32((int)*(address+x), src_color, tolerance))
return x+1;
/* work left from starting point */
for (left=x-1; left>=0; left--) {
if (!color_equal_32((int)*(address+left), src_color))
if (!color_equal_32((int)*(address+left), src_color, tolerance))
break;
}
/* work right from starting point */
for (right=x+1; right<image->w; right++) {
if (!color_equal_32((int)*(address+right), src_color))
if (!color_equal_32((int)*(address+right), src_color, tolerance))
break;
}
}
@ -106,18 +141,18 @@ static int flooder (Image *image, int x, int y,
ase_uint16 *address = ((ase_uint16 **)image->line)[y];
/* check start pixel */
if (!color_equal_16((int)*(address+x), src_color))
if (!color_equal_16((int)*(address+x), src_color, tolerance))
return x+1;
/* work left from starting point */
for (left=x-1; left>=0; left--) {
if (!color_equal_16((int)*(address+left), src_color))
if (!color_equal_16((int)*(address+left), src_color, tolerance))
break;
}
/* work right from starting point */
for (right=x+1; right<image->w; right++) {
if (!color_equal_16((int)*(address+right), src_color))
if (!color_equal_16((int)*(address+right), src_color, tolerance))
break;
}
}
@ -128,18 +163,18 @@ static int flooder (Image *image, int x, int y,
ase_uint8 *address = ((ase_uint8 **)image->line)[y];
/* check start pixel */
if (!color_equal_8((int)*(address+x), src_color))
if (!color_equal_8((int)*(address+x), src_color, tolerance))
return x+1;
/* work left from starting point */
for (left=x-1; left>=0; left--) {
if (!color_equal_8((int)*(address+left), src_color))
if (!color_equal_8((int)*(address+left), src_color, tolerance))
break;
}
/* work right from starting point */
for (right=x+1; right<image->w; right++) {
if (!color_equal_8((int)*(address+right), src_color))
if (!color_equal_8((int)*(address+right), src_color, tolerance))
break;
}
}
@ -207,8 +242,8 @@ static int flooder (Image *image, int x, int y,
* segments which have already been drawn in order to minimise the required
* number of tests.
*/
static int check_flood_line(Image *image, int y, int left, int right,
int src_color, void *data, AlgoHLine proc)
static int check_flood_line(Image* image, int y, int left, int right,
int src_color, int tolerance, void *data, AlgoHLine proc)
{
int c;
FLOODED_LINE *p;
@ -228,7 +263,7 @@ static int check_flood_line(Image *image, int y, int left, int right,
c = p->next;
if (!c) {
left = flooder (image, left, y, src_color, data, proc);
left = flooder (image, left, y, src_color, tolerance, data, proc);
ret = true;
break;
}
@ -243,7 +278,7 @@ static int check_flood_line(Image *image, int y, int left, int right,
/* floodfill:
* Fills an enclosed area (starting at point x, y) with the specified color.
*/
void algo_floodfill(Image *image, int x, int y, void *data, AlgoHLine proc)
void algo_floodfill(Image* image, int x, int y, int tolerance, void *data, AlgoHLine proc)
{
int src_color;
int c, done;
@ -270,7 +305,7 @@ void algo_floodfill(Image *image, int x, int y, void *data, AlgoHLine proc)
}
/* start up the flood algorithm */
flooder(image, x, y, src_color, data, proc);
flooder(image, x, y, src_color, tolerance, data, proc);
/* continue as long as there are some segments still to test */
do {
@ -285,7 +320,7 @@ void algo_floodfill(Image *image, int x, int y, void *data, AlgoHLine proc)
if (p->flags & FLOOD_TODO_BELOW) {
p->flags &= ~FLOOD_TODO_BELOW;
if (check_flood_line(image, p->y+1, p->lpos, p->rpos,
src_color, data, proc)) {
src_color, tolerance, data, proc)) {
done = false;
p = FLOOD_LINE(c);
}
@ -295,7 +330,7 @@ void algo_floodfill(Image *image, int x, int y, void *data, AlgoHLine proc)
if (p->flags & FLOOD_TODO_ABOVE) {
p->flags &= ~FLOOD_TODO_ABOVE;
if (check_flood_line(image, p->y-1, p->lpos, p->rpos,
src_color, data, proc)) {
src_color, tolerance, data, proc)) {
done = false;
/* special case shortcut for going backwards */
if ((c < image->h) && (c > 0))

View File

@ -135,7 +135,7 @@ void Pen::regenerate_pen()
image_line(m_image, r+x3, r+y3, r+x4, r+y4, 1);
image_line(m_image, r+x4, r+y4, r+x1, r+y1, 1);
algo_floodfill(m_image, r, r, m_image, algo_hline);
algo_floodfill(m_image, r, r, 0, m_image, algo_hline);
break;
}

View File

@ -97,12 +97,14 @@ public:
virtual IPenSettings* getPen() = 0;
virtual int getOpacity() = 0;
virtual int getTolerance() = 0;
virtual bool getFilled() = 0;
virtual bool getPreviewFilled() = 0;
virtual int getSprayWidth() = 0;
virtual int getSpraySpeed() = 0;
virtual void setOpacity(int opacity) = 0;
virtual void setTolerance(int tolerance) = 0;
virtual void setFilled(bool state) = 0;
virtual void setSprayWidth(int width) = 0;
virtual void setSpraySpeed(int speed) = 0;

View File

@ -311,6 +311,7 @@ class UIToolSettingsImpl : public IToolSettings
Tool* m_tool;
UIPenSettingsImpl m_pen;
int m_opacity;
int m_tolerance;
bool m_filled;
bool m_previewFilled;
int m_spray_width;
@ -325,6 +326,8 @@ public:
m_opacity = get_config_int(cfg_section.c_str(), "Opacity", 255);
m_opacity = MID(0, m_opacity, 255);
m_tolerance = get_config_int(cfg_section.c_str(), "Tolerance", 0);
m_tolerance = MID(0, m_tolerance, 255);
m_filled = false;
m_previewFilled = get_config_bool(cfg_section.c_str(), "PreviewFilled", false);
m_spray_width = 16;
@ -348,6 +351,7 @@ public:
std::string cfg_section(getCfgSection());
set_config_int(cfg_section.c_str(), "Opacity", m_opacity);
set_config_int(cfg_section.c_str(), "Tolerance", m_tolerance);
set_config_int(cfg_section.c_str(), "PenType", m_pen.getType());
set_config_int(cfg_section.c_str(), "PenSize", m_pen.getSize());
set_config_int(cfg_section.c_str(), "PenAngle", m_pen.getAngle());
@ -364,12 +368,14 @@ public:
IPenSettings* getPen() { return &m_pen; }
int getOpacity() { return m_opacity; }
int getTolerance() { return m_tolerance; }
bool getFilled() { return m_filled; }
bool getPreviewFilled() { return m_previewFilled; }
int getSprayWidth() { return m_spray_width; }
int getSpraySpeed() { return m_spray_speed; }
void setOpacity(int opacity) { m_opacity = opacity; }
void setTolerance(int tolerance) { m_tolerance = tolerance; }
void setFilled(bool state) { m_filled = state; }
void setPreviewFilled(bool state) { m_previewFilled = state; }
void setSprayWidth(int width) { m_spray_width = width; }

View File

@ -74,7 +74,7 @@ class FloodFillPointShape : public ToolPointShape
public:
void transformPoint(IToolLoop* loop, int x, int y)
{
algo_floodfill(loop->getSrcImage(), x, y, loop, (AlgoHLine)doInkHline);
algo_floodfill(loop->getSrcImage(), x, y, loop->getTolerance(), loop, (AlgoHLine)doInkHline);
}
void getModifiedArea(IToolLoop* loop, int x, int y, Rect& area)
{

View File

@ -276,6 +276,9 @@ public:
// Returns the opacity to be used by the ink (ToolInk).
virtual int getOpacity() = 0;
// Returns the tolerance to be used by the ink (ToolInk).
virtual int getTolerance() = 0;
// Returns true if each scanline generated by a ToolPointShape must
// be "tiled". See the method ToolPointShape::doInkHline to check
// how this member is used. When tiled mode is activated, each

View File

@ -1784,6 +1784,7 @@ class ToolLoopImpl : public IToolLoop
Mask* m_mask;
Point m_maskOrigin;
int m_opacity;
int m_tolerance;
Point m_offset;
Point m_speed;
bool m_canceled;
@ -1888,6 +1889,7 @@ public:
Point(0, 0));
m_opacity = settings->getToolSettings(m_tool)->getOpacity();
m_tolerance = settings->getToolSettings(m_tool)->getTolerance();
m_speed.x = 0;
m_speed.y = 0;
@ -2032,6 +2034,7 @@ public:
int getSecondaryColor() { return m_secondary_color; }
void setSecondaryColor(int color) { m_secondary_color = color; }
int getOpacity() { return m_opacity; }
int getTolerance() { return m_tolerance; }
TiledMode getTiledMode() { return m_tiled_mode; }
bool getFilled() { return m_filled; }
bool getPreviewFilled() { return m_previewFilled; }