WIP: Fill tool improvement related to antialiasing

Fill tools do a better job at filling around
antialiased edges, and apply some blending.
(Blending does not yet occur with gradient tools.)
This commit is contained in:
Bill Kendrick 2021-03-07 01:00:53 -08:00
parent f40dc07f37
commit 3b5a0a1586
4 changed files with 158 additions and 49 deletions

View file

@ -8,7 +8,7 @@ http://www.tuxpaint.org/
$Id$
2021.March.3 (0.9.26)
2021.March.7 (0.9.26)
* New Features
------------
* Larger UI buttons
@ -64,6 +64,9 @@ $Id$
* Other Improvements
------------------
* [WIP] Fill tools do a better job at filling around
antialiased edges, and apply some blending.
* Reduce CPU usage by increasing delay in main loop
from 1ms to 10ms, and only using SDL's Timer subsystem
when scrolling happens.

View file

@ -27,7 +27,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: September 14, 2019
Last updated: March 7, 2021
$Id$
*/
@ -43,14 +43,28 @@
#include "rgblinear.h"
#include "playsound.h"
#include "pixels.h"
#include "progressbar.h"
/* How close colors need to be to match all the time */
#define COLOR_MATCH_NARROW 0.04
/* How close colors can be to match for a few pixels */
#define COLOR_MATCH_WIDE 0.60
/* How many pixels can we allow a wide match before stopping? */
#define WIDE_MATCH_THRESHOLD 3
/* Local function prototypes: */
int colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2);
double colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2);
Uint32 blend(SDL_Surface * canvas, Uint32 draw_colr, Uint32 old_colr, double pct);
void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched, int y_outside);
int colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2)
/* Returns how similar colors 'c1' and 'c2' are */
double colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2)
{
Uint8 r1, g1, b1, r2, g2, b2;
@ -58,7 +72,7 @@ int colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2)
{
/* Get it over with quick, if possible! */
return 1;
return 0.0;
}
else
{
@ -79,14 +93,13 @@ int colors_close(SDL_Surface * canvas, Uint32 c1, Uint32 c2)
// dark grey, brown, purple
// light grey, tan
// red, orange
return r + g + b < 0.04;
return (r + g + b);
}
}
int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr)
{
if (cur_colr == old_colr || colors_close(canvas, cur_colr, old_colr))
if (colors_close(canvas, cur_colr, old_colr) < COLOR_MATCH_NARROW)
{
return 0;
} else {
@ -94,19 +107,43 @@ int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr)
}
}
void do_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2)
void do_flood_fill(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched)
{
simulate_flood_fill(canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, NULL);
simulate_flood_fill(screen, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched);
}
void simulate_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched)
Uint32 blend(SDL_Surface * canvas, Uint32 draw_colr, Uint32 old_colr, double pct) {
Uint8 old_r, old_g, old_b, draw_r, draw_g, draw_b, new_r, new_g, new_b;
SDL_GetRGB(draw_colr, canvas->format, &draw_r, &draw_g, &draw_b);
SDL_GetRGB(old_colr, canvas->format, &old_r, &old_g, &old_b);
new_r = (Uint8) (((float) old_r) * (1.00 - pct) + ((float) draw_r * pct));
new_g = (Uint8) (((float) old_g) * (1.00 - pct) + ((float) draw_g * pct));
new_b = (Uint8) (((float) old_b) * (1.00 - pct) + ((float) draw_b * pct));
return SDL_MapRGB(canvas->format, new_r, new_g, new_b);
}
void simulate_flood_fill(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched) {
simulate_flood_fill_outside_check(screen, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched, 0);
}
void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched, int y_outside)
{
int fillL, fillR, i, in_line;
int fillL, fillR, narrowFillL, narrowFillR, i, outside;
double in_line, closeness;
static unsigned char prog_anim;
Uint32 px_colr;
if (cur_colr == old_colr || colors_close(canvas, cur_colr, old_colr))
/* "Same" color? No need to fill */
if (!would_flood_fill(canvas, cur_colr, old_colr))
return;
/* Don't re-visit the same pixel */
if (touched[(y * canvas->w) + x])
return;
if (y < *y1)
@ -121,30 +158,55 @@ void simulate_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Ui
fillL = x;
fillR = x;
narrowFillL = x;
narrowFillR = x;
prog_anim++;
if ((prog_anim % 4) == 0)
{
/* FIXME: api->update_progress_bar(); */
show_progress_bar(screen);
playsound(canvas, 1, SND_FILL, 1, x, SNDDIST_NEAR);
}
/* Find left side, filling along the way */
in_line = 1;
while (in_line)
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL /* - 1 */, y);
in_line = colors_close(canvas, px_colr, old_colr);
outside = 0;
while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD)
{
if (touched != NULL) {
touched[(y * canvas->w) + fillL] = 1;
if (in_line > COLOR_MATCH_NARROW) {
outside++;
} else {
narrowFillL = fillL;
}
putpixels[canvas->format->BytesPerPixel] (canvas, fillL, y, cur_colr);
if (touched != NULL) {
touched[(y * canvas->w) + fillL] = (255 - ((Uint8) (in_line * 85)));
}
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL, y);
putpixels[canvas->format->BytesPerPixel] (canvas, fillL, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0));
fillL--;
in_line = (fillL < 0) ? 0 : colors_close(canvas, getpixels[canvas->format->BytesPerPixel] (canvas, fillL, y), old_colr);
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL, y);
if (fillL >= 0)
{
in_line = colors_close(canvas, px_colr, old_colr);
}
else
{
in_line = 3.0;
}
}
if (touched != NULL && fillL >= 0)
{
touched[(y * canvas->w) + fillL] = (255 - ((Uint8) (in_line * 85)));
}
if (fillL < *x1)
{
@ -153,18 +215,42 @@ void simulate_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Ui
fillL++;
/* Find right side, filling along the way */
in_line = 1;
while (in_line)
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR + 1, y);
in_line = colors_close(canvas, px_colr, old_colr);
outside = 0;
while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD)
{
if (touched != NULL) {
touched[(y * canvas->w) + fillR] = 1;
if (in_line > COLOR_MATCH_NARROW) {
outside++;
} else {
narrowFillR = fillR;
}
putpixels[canvas->format->BytesPerPixel] (canvas, fillR, y, cur_colr);
if (touched != NULL) {
touched[(y * canvas->w) + fillR] = (255 - ((Uint8) (in_line * 85)));
}
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR, y);
putpixels[canvas->format->BytesPerPixel] (canvas, fillR, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0));
fillR++;
in_line = (fillR >= canvas->w) ? 0 : colors_close(canvas, getpixels[canvas->format->BytesPerPixel] (canvas, fillR, y), old_colr);
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR, y);
if (fillR < canvas->w)
{
in_line = colors_close(canvas, px_colr, old_colr);
}
else
{
in_line = 3.0;
}
}
if (touched != NULL && fillR < canvas->w)
{
touched[(y * canvas->w) + fillR] = (255 - ((Uint8) (in_line * 85)));
}
if (fillR > *x2)
@ -177,13 +263,31 @@ void simulate_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Ui
/* Search top and bottom */
for (i = fillL; i <= fillR; i++)
for (i = narrowFillL; i <= narrowFillR; i++)
{
if (y > 0 && colors_close(canvas, getpixels[canvas->format->BytesPerPixel] (canvas, i, y - 1), old_colr))
simulate_flood_fill(canvas, i, y - 1, cur_colr, old_colr, x1, y1, x2, y2, touched);
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, i, y - 1);
closeness = colors_close(canvas, px_colr, old_colr);
if (y > 0 &&
(
closeness < COLOR_MATCH_NARROW ||
(closeness < COLOR_MATCH_WIDE && y_outside < WIDE_MATCH_THRESHOLD)
)
)
{
simulate_flood_fill_outside_check(screen, canvas, i, y - 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1);
}
if (y < canvas->h && colors_close(canvas, getpixels[canvas->format->BytesPerPixel] (canvas, i, y + 1), old_colr))
simulate_flood_fill(canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched);
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, i, y + 1);
closeness = colors_close(canvas, px_colr, old_colr);
if (y < canvas->h &&
(
closeness < COLOR_MATCH_NARROW ||
(closeness < COLOR_MATCH_WIDE && y_outside < WIDE_MATCH_THRESHOLD)
)
)
{
simulate_flood_fill_outside_check(screen, canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1);
}
}
}
@ -194,7 +298,6 @@ void draw_linear_gradient(SDL_Surface * canvas, SDL_Surface * last,
) {
Uint32 old_colr, new_colr;
int xx, yy;
float xd, yd;
Uint8 draw_r, draw_g, draw_b, old_r, old_g, old_b, new_r, new_g, new_b;
float A, B, C, C1, C2, ratio;
@ -218,6 +321,7 @@ void draw_linear_gradient(SDL_Surface * canvas, SDL_Surface * last,
https://stackoverflow.com/questions/521493/creating-a-linear-gradient-in-2d-array) */
C = (A * xx) + (B * yy);
/* FIXME: Blend gradient color based on touched[] */
if (C < C1) {
/* At/beyond the click spot (opposite direction of mouse); solid color */
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, draw_color);
@ -283,6 +387,7 @@ void draw_radial_gradient(SDL_Surface * canvas, int x_left, int y_top, int x_rig
new_b = (Uint8) (((float) old_b) * ratio + ((float) draw_b * (1.00 - ratio)));
new_colr = SDL_MapRGB(canvas->format, new_r, new_g, new_b);
/* FIXME: Blend gradient color based on touched[] */
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, new_colr);
}
}

View file

@ -4,7 +4,7 @@
Fill tool
Tux Paint - A simple drawing program for children.
Copyright (c) 2002-2019 by Bill Kendrick and others; see AUTHORS.txt
Copyright (c) 2002-2021 by Bill Kendrick and others; see AUTHORS.txt
bill@newbreedsoftware.com
http://www.tuxpaint.org/
@ -27,7 +27,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: February 20, 2021
Last updated: March 7, 2021
$Id$
*/
@ -37,8 +37,8 @@
#include "SDL.h"
int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr);
void do_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2);
void simulate_flood_fill(SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched);
void do_flood_fill(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched);
void simulate_flood_fill(SDL_Surface * screen, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * x1, int * y1, int * x2, int * y2, Uint8 * touched);
void draw_linear_gradient(SDL_Surface * canvas, SDL_Surface * last,
int x_left, int y_top, int x_right, int y_bottom,
int x1, int y1, int x2, int y2, Uint32 draw_color, Uint8 * touched);

View file

@ -22,7 +22,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
June 14, 2002 - February 20, 2021
June 14, 2002 - March 7, 2021
*/
@ -4551,10 +4551,16 @@ static void mainloop(void)
x1 = x2 = old_x;
y1 = y2 = old_y;
for (y1 = 0; y1 < canvas->h; y1++) {
for (x1 = 0; x1 < canvas->w; x1++) {
sim_flood_touched[(y1 * canvas->w) + x1] = 0;
}
}
if (cur_fill == FILL_FLOOD)
{
/* Flood fill a solid color */
do_flood_fill(canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2);
do_flood_fill(screen, canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
update_canvas(x1, y1, x2, y2);
}
@ -4562,18 +4568,12 @@ static void mainloop(void)
{
SDL_Surface * tmp_canvas;
for (y1 = 0; y1 < canvas->h; y1++) {
for (x1 = 0; x1 < canvas->w; x1++) {
sim_flood_touched[(y1 * canvas->w) + x1] = 0;
}
}
tmp_canvas = SDL_CreateRGBSurface(canvas->flags,
canvas->w, canvas->h, canvas->format->BitsPerPixel,
canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
SDL_BlitSurface(canvas, NULL, tmp_canvas, NULL);
simulate_flood_fill(tmp_canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
simulate_flood_fill(screen, tmp_canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
SDL_FreeSurface(tmp_canvas);
sim_flood_x1 = x1;
@ -4596,6 +4596,8 @@ static void mainloop(void)
update_canvas(x1, y1, x2, y2);
}
draw_tux_text(TUX_GREAT, tool_tips[TOOL_FILL], 1);
}
}
else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
@ -4725,7 +4727,6 @@ static void mainloop(void)
}
do_render_cur_text(0);
}
button_down = 1;