Continuing to improve fill anti-aliasing

This commit is contained in:
Bill Kendrick 2021-03-08 01:18:00 -08:00
parent 3b5a0a1586
commit 866d5190bf
3 changed files with 64 additions and 40 deletions

View file

@ -27,7 +27,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt) (See COPYING.txt)
Last updated: March 7, 2021 Last updated: March 8, 2021
$Id$ $Id$
*/ */
@ -60,7 +60,7 @@
double 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); 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); void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * last, 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);
/* Returns how similar colors 'c1' and 'c2' are */ /* Returns how similar colors 'c1' and 'c2' are */
@ -107,9 +107,9 @@ int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr)
} }
} }
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 do_flood_fill(SDL_Surface * screen, SDL_Surface * last, 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(screen, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched); simulate_flood_fill(screen, last, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched);
} }
@ -123,14 +123,15 @@ Uint32 blend(SDL_Surface * canvas, Uint32 draw_colr, Uint32 old_colr, double pct
new_g = (Uint8) (((float) old_g) * (1.00 - pct) + ((float) draw_g * 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)); new_b = (Uint8) (((float) old_b) * (1.00 - pct) + ((float) draw_b * pct));
return SDL_MapRGB(canvas->format, draw_r, draw_g, draw_b);
return SDL_MapRGB(canvas->format, new_r, new_g, new_b); 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) { void simulate_flood_fill(SDL_Surface * screen, SDL_Surface * last, 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); simulate_flood_fill_outside_check(screen, last, 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) void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * last, 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, narrowFillL, narrowFillR, i, outside; int fillL, fillR, narrowFillL, narrowFillR, i, outside;
double in_line, closeness; double in_line, closeness;
@ -142,8 +143,11 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
if (!would_flood_fill(canvas, cur_colr, old_colr)) if (!would_flood_fill(canvas, cur_colr, old_colr))
return; return;
if (x < 0 || x >= canvas->w || y < 0 || y >= canvas->h)
return;
/* Don't re-visit the same pixel */ /* Don't re-visit the same pixel */
if (touched[(y * canvas->w) + x]) if (touched && touched[(y * canvas->w) + x])
return; return;
if (y < *y1) if (y < *y1)
@ -171,7 +175,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
/* Find left side, filling along the way */ /* Find left side, filling along the way */
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL /* - 1 */, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillL /* - 1 */, y);
in_line = colors_close(canvas, px_colr, old_colr); in_line = colors_close(canvas, px_colr, old_colr);
outside = 0; outside = 0;
while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD) while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD)
@ -186,11 +190,11 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
touched[(y * canvas->w) + fillL] = (255 - ((Uint8) (in_line * 85))); touched[(y * canvas->w) + fillL] = (255 - ((Uint8) (in_line * 85)));
} }
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillL, y);
putpixels[canvas->format->BytesPerPixel] (canvas, fillL, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0)); putpixels[canvas->format->BytesPerPixel] (canvas, fillL, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0));
fillL--; fillL--;
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillL, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillL, y);
if (fillL >= 0) if (fillL >= 0)
{ {
@ -218,7 +222,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
/* Find right side, filling along the way */ /* Find right side, filling along the way */
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR + 1, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillR + 1, y);
in_line = colors_close(canvas, px_colr, old_colr); in_line = colors_close(canvas, px_colr, old_colr);
outside = 0; outside = 0;
while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD) while (in_line < COLOR_MATCH_WIDE && outside < WIDE_MATCH_THRESHOLD)
@ -232,11 +236,11 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
if (touched != NULL) { if (touched != NULL) {
touched[(y * canvas->w) + fillR] = (255 - ((Uint8) (in_line * 85))); touched[(y * canvas->w) + fillR] = (255 - ((Uint8) (in_line * 85)));
} }
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillR, y);
putpixels[canvas->format->BytesPerPixel] (canvas, fillR, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0)); putpixels[canvas->format->BytesPerPixel] (canvas, fillR, y, blend(canvas, cur_colr, px_colr, (3.0 - in_line) / 3.0));
fillR++; fillR++;
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, fillR, y); px_colr = getpixels[last->format->BytesPerPixel] (last, fillR, y);
if (fillR < canvas->w) if (fillR < canvas->w)
{ {
@ -265,7 +269,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
for (i = narrowFillL; i <= narrowFillR; i++) for (i = narrowFillL; i <= narrowFillR; i++)
{ {
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, i, y - 1); px_colr = getpixels[last->format->BytesPerPixel] (last, i, y - 1);
closeness = colors_close(canvas, px_colr, old_colr); closeness = colors_close(canvas, px_colr, old_colr);
if (y > 0 && if (y > 0 &&
( (
@ -274,10 +278,10 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
) )
) )
{ {
simulate_flood_fill_outside_check(screen, canvas, i, y - 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1); simulate_flood_fill_outside_check(screen, last, canvas, i, y - 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1);
} }
px_colr = getpixels[canvas->format->BytesPerPixel] (canvas, i, y + 1); px_colr = getpixels[last->format->BytesPerPixel] (last, i, y + 1);
closeness = colors_close(canvas, px_colr, old_colr); closeness = colors_close(canvas, px_colr, old_colr);
if (y < canvas->h && if (y < canvas->h &&
( (
@ -286,7 +290,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * canva
) )
) )
{ {
simulate_flood_fill_outside_check(screen, canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1); simulate_flood_fill_outside_check(screen, last, canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1);
} }
} }
} }
@ -321,25 +325,26 @@ void draw_linear_gradient(SDL_Surface * canvas, SDL_Surface * last,
https://stackoverflow.com/questions/521493/creating-a-linear-gradient-in-2d-array) */ https://stackoverflow.com/questions/521493/creating-a-linear-gradient-in-2d-array) */
C = (A * xx) + (B * yy); C = (A * xx) + (B * yy);
/* FIXME: Blend gradient color based on touched[] */
if (C < C1) { if (C < C1) {
/* At/beyond the click spot (opposite direction of mouse); solid color */ /* At/beyond the click spot (opposite direction of mouse); solid color */
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, draw_color); ratio = 0.0;
} else if (C >= C2) { } else if (C >= C2) {
/* At/beyond the mouse; completely faded out */ /* At/beyond the mouse; completely faded out */
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, old_colr); ratio = 1.0;
} else { } else {
/* The actual gradient... */ /* The actual gradient... */
ratio = (C - C1) / (C2 - C1); ratio = (C - C1) / (C2 - C1);
new_r = (Uint8) (((float) old_r) * ratio + ((float) draw_r * (1.00 - ratio)));
new_g = (Uint8) (((float) old_g) * ratio + ((float) draw_g * (1.00 - ratio)));
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);
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, new_colr);
} }
/* Apply fuzziness at any antialiased edges we detected */
ratio = (ratio * ((float) touched[yy * canvas->w + xx] / 255.0));
new_r = (Uint8) (((float) old_r) * ratio + ((float) draw_r * (1.0 - ratio)));
new_g = (Uint8) (((float) old_g) * ratio + ((float) draw_g * (1.0 - ratio)));
new_b = (Uint8) (((float) old_b) * ratio + ((float) draw_b * (1.0 - ratio)));
new_colr = SDL_MapRGB(canvas->format, new_r, new_g, new_b);
putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, new_colr);
} }
} }
} }
@ -382,12 +387,14 @@ void draw_radial_gradient(SDL_Surface * canvas, int x_left, int y_top, int x_rig
old_colr = getpixels[canvas->format->BytesPerPixel] (canvas, xx, yy); old_colr = getpixels[canvas->format->BytesPerPixel] (canvas, xx, yy);
SDL_GetRGB(old_colr, canvas->format, &old_r, &old_g, &old_b); SDL_GetRGB(old_colr, canvas->format, &old_r, &old_g, &old_b);
/* Apply fuzziness at any antialiased edges we detected */
ratio = (ratio * ((float) touched[yy * canvas->w + xx] / 255.0));
new_r = (Uint8) (((float) old_r) * ratio + ((float) draw_r * (1.00 - ratio))); new_r = (Uint8) (((float) old_r) * ratio + ((float) draw_r * (1.00 - ratio)));
new_g = (Uint8) (((float) old_g) * ratio + ((float) draw_g * (1.00 - ratio))); new_g = (Uint8) (((float) old_g) * ratio + ((float) draw_g * (1.00 - ratio)));
new_b = (Uint8) (((float) old_b) * ratio + ((float) draw_b * (1.00 - ratio))); 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); 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); putpixels[canvas->format->BytesPerPixel] (canvas, xx, yy, new_colr);
} }
} }

View file

@ -27,7 +27,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt) (See COPYING.txt)
Last updated: March 7, 2021 Last updated: March 8, 2021
$Id$ $Id$
*/ */
@ -37,8 +37,8 @@
#include "SDL.h" #include "SDL.h"
int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr); int would_flood_fill(SDL_Surface * canvas, Uint32 cur_colr, Uint32 old_colr);
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 do_flood_fill(SDL_Surface * screen, SDL_Surface * last, 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 simulate_flood_fill(SDL_Surface * screen, SDL_Surface * last, 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, void draw_linear_gradient(SDL_Surface * canvas, SDL_Surface * last,
int x_left, int y_top, int x_right, int y_bottom, 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); int x1, int y1, int x2, int y2, Uint32 draw_color, Uint8 * touched);

View file

@ -3,7 +3,7 @@
Tux Paint - A simple drawing program for children. Tux Paint - A simple drawing program for children.
Copyright (c) 2002-2020 Copyright (c) 2002-2021
by various contributors; see AUTHORS.txt by various contributors; see AUTHORS.txt
http://www.tuxpaint.org/ http://www.tuxpaint.org/
@ -22,7 +22,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt) (See COPYING.txt)
June 14, 2002 - March 7, 2021 June 14, 2002 - March 8, 2021
*/ */
@ -1831,7 +1831,7 @@ static unsigned cur_color;
static int cur_tool, cur_brush, old_tool; static int cur_tool, cur_brush, old_tool;
static int cur_stamp[MAX_STAMP_GROUPS]; static int cur_stamp[MAX_STAMP_GROUPS];
static int cur_shape, cur_magic; static int cur_shape, cur_magic;
static int cur_font, cur_eraser, cur_fill; static int cur_font, cur_eraser, cur_fill, fill_drag_started;
static int cursor_left, cursor_x, cursor_y, cursor_textwidth; /* canvas-relative */ static int cursor_left, cursor_x, cursor_y, cursor_textwidth; /* canvas-relative */
static int old_cursor_x, old_cursor_y; static int old_cursor_x, old_cursor_y;
static int cur_label, cur_select; static int cur_label, cur_select;
@ -4543,6 +4543,8 @@ static void mainloop(void)
if (would_flood_fill(canvas, draw_color, canv_color)) if (would_flood_fill(canvas, draw_color, canv_color))
{ {
int x1, y1, x2, y2; int x1, y1, x2, y2;
SDL_Surface * last;
int undo_ctr;
/* We only bother recording an undo buffer /* We only bother recording an undo buffer
(which may kill our redos) if we're about (which may kill our redos) if we're about
@ -4551,6 +4553,14 @@ static void mainloop(void)
x1 = x2 = old_x; x1 = x2 = old_x;
y1 = y2 = old_y; y1 = y2 = old_y;
if (cur_undo > 0)
undo_ctr = cur_undo - 1;
else
undo_ctr = NUM_UNDO_BUFS - 1;
last = undo_bufs[undo_ctr];
for (y1 = 0; y1 < canvas->h; y1++) { for (y1 = 0; y1 < canvas->h; y1++) {
for (x1 = 0; x1 < canvas->w; x1++) { for (x1 = 0; x1 < canvas->w; x1++) {
sim_flood_touched[(y1 * canvas->w) + x1] = 0; sim_flood_touched[(y1 * canvas->w) + x1] = 0;
@ -4560,7 +4570,7 @@ static void mainloop(void)
if (cur_fill == FILL_FLOOD) if (cur_fill == FILL_FLOOD)
{ {
/* Flood fill a solid color */ /* Flood fill a solid color */
do_flood_fill(screen, canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched); do_flood_fill(screen, last, canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
update_canvas(x1, y1, x2, y2); update_canvas(x1, y1, x2, y2);
} }
@ -4573,7 +4583,7 @@ static void mainloop(void)
canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask); canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
SDL_BlitSurface(canvas, NULL, tmp_canvas, NULL); SDL_BlitSurface(canvas, NULL, tmp_canvas, NULL);
simulate_flood_fill(screen, tmp_canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched); simulate_flood_fill(screen, last, tmp_canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
SDL_FreeSurface(tmp_canvas); SDL_FreeSurface(tmp_canvas);
sim_flood_x1 = x1; sim_flood_x1 = x1;
@ -4592,6 +4602,7 @@ static void mainloop(void)
/* Start a linear gradient */ /* Start a linear gradient */
draw_linear_gradient(canvas, canvas, sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2, draw_linear_gradient(canvas, canvas, sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2,
fill_x, fill_y, old_x, old_y + 1, draw_color, sim_flood_touched); fill_x, fill_y, old_x, old_y + 1, draw_color, sim_flood_touched);
fill_drag_started = 1;
} }
update_canvas(x1, y1, x2, y2); update_canvas(x1, y1, x2, y2);
@ -5219,6 +5230,10 @@ static void mainloop(void)
update_screen_rect(&kbd_rect); update_screen_rect(&kbd_rect);
// SDL_Flip(screen); // SDL_Flip(screen);
} }
else if (cur_tool == TOOL_FILL)
{
fill_drag_started = 0;
}
} }
button_down = 0; button_down = 0;
} }
@ -5526,7 +5541,7 @@ static void mainloop(void)
sz = calc_eraser_size(cur_eraser); sz = calc_eraser_size(cur_eraser);
rect_xor(new_x - sz / 2, new_y - sz / 2, new_x + sz / 2, new_y + sz / 2); rect_xor(new_x - sz / 2, new_y - sz / 2, new_x + sz / 2, new_y + sz / 2);
} }
else if (cur_tool == TOOL_FILL && cur_fill == FILL_GRADIENT_LINEAR) else if (cur_tool == TOOL_FILL && cur_fill == FILL_GRADIENT_LINEAR && fill_drag_started)
{ {
Uint32 draw_color, canv_color; Uint32 draw_color, canv_color;
int undo_ctr; int undo_ctr;
@ -24674,7 +24689,7 @@ static void setup(void)
printf("%s\n", tmp_str); printf("%s\n", tmp_str);
#endif #endif
safe_snprintf(tmp_str, sizeof(tmp_str), "© 20022020 Bill Kendrick et al."); safe_snprintf(tmp_str, sizeof(tmp_str), "© 20022021 Bill Kendrick et al.");
tmp_surf = render_text(medium_font, tmp_str, black); tmp_surf = render_text(medium_font, tmp_str, black);
dest.x = 10; dest.x = 10;
dest.y = WINDOW_HEIGHT - img_progress->h - (tmp_surf->h * 2); dest.y = WINDOW_HEIGHT - img_progress->h - (tmp_surf->h * 2);
@ -25276,6 +25291,8 @@ static void claim_to_be_ready(void)
cur_magic = 0; cur_magic = 0;
cur_font = 0; cur_font = 0;
cur_eraser = 0; cur_eraser = 0;
cur_fill = 0;
fill_drag_started = 0;
cur_label = LABEL_LABEL; cur_label = LABEL_LABEL;
cur_select = 0; cur_select = 0;
cursor_left = -1; cursor_left = -1;