diff --git a/data/widgets/tools_configuration.xml b/data/widgets/tools_configuration.xml
index 41927e4ad..33a9b9a05 100644
--- a/data/widgets/tools_configuration.xml
+++ b/data/widgets/tools_configuration.xml
@@ -31,9 +31,12 @@
-
+
+
+
+
diff --git a/src/commands/cmd_configure_tools.cpp b/src/commands/cmd_configure_tools.cpp
index 0e3e8b7f2..1051e777f 100644
--- a/src/commands/cmd_configure_tools.cpp
+++ b/src/commands/cmd_configure_tools.cpp
@@ -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();
diff --git a/src/raster/algo.h b/src/raster/algo.h
index bba333978..0ff96c00e 100644
--- a/src/raster/algo.h
+++ b/src/raster/algo.h
@@ -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);
diff --git a/src/raster/algofill.cpp b/src/raster/algofill.cpp
index 999efb6ca..6b056cce1 100644
--- a/src/raster/algofill.cpp
+++ b/src/raster/algofill.cpp
@@ -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; rightw; 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; rightw; 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; rightw; 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))
diff --git a/src/raster/pen.cpp b/src/raster/pen.cpp
index 17dfbe108..169e812bc 100644
--- a/src/raster/pen.cpp
+++ b/src/raster/pen.cpp
@@ -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;
}
diff --git a/src/settings/settings.h b/src/settings/settings.h
index f85993f43..b54f891f2 100644
--- a/src/settings/settings.h
+++ b/src/settings/settings.h
@@ -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;
diff --git a/src/settings/ui_settings_impl.cpp b/src/settings/ui_settings_impl.cpp
index 0dff04980..a640e4f69 100644
--- a/src/settings/ui_settings_impl.cpp
+++ b/src/settings/ui_settings_impl.cpp
@@ -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; }
diff --git a/src/tools/point_shapes.h b/src/tools/point_shapes.h
index e8a2fac83..0a00c91a5 100644
--- a/src/tools/point_shapes.h
+++ b/src/tools/point_shapes.h
@@ -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)
{
diff --git a/src/tools/tool.h b/src/tools/tool.h
index 8972e146a..348e2966e 100644
--- a/src/tools/tool.h
+++ b/src/tools/tool.h
@@ -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
diff --git a/src/widgets/editor/editor.cpp b/src/widgets/editor/editor.cpp
index d7efaa694..30edfae12 100644
--- a/src/widgets/editor/editor.cpp
+++ b/src/widgets/editor/editor.cpp
@@ -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; }