From fcf1f66a03de5178698e590fb8c27d51998505a1 Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Tue, 16 Nov 2021 23:49:52 -0800 Subject: [PATCH 1/5] Revert Fill stack explosion prevention Not the way to do it. --- docs/CHANGES.txt | 4 ++-- src/fill.c | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index bdc289664..172e64f2a 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -8,7 +8,7 @@ http://www.tuxpaint.org/ $Id$ -2021.November.15 (0.9.27) +2021.November.16 (0.9.27) * New Magic Tools: ---------------- * "Lightning" - Draws a bolt of lightning striking between @@ -194,7 +194,7 @@ $Id$ (e.g., when returning from the "Open" dialog). (Closes https://sourceforge.net/p/tuxpaint/feature-requests/186/) - * Attempt to void crashing (by blowing up the stack) when doing + * WIP - Attempt to void crashing (by blowing up the stack) when doing a flood-fill of a complicated shape on a large canvas (e.g., `tuxpaint --3000x2000` with `starters/mosaic.svg`). (h/t Yang for reporting, and Pere for confirming) diff --git a/src/fill.c b/src/fill.c index 5664a4869..f82ced30c 100644 --- a/src/fill.c +++ b/src/fill.c @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA (See COPYING.txt) - Last updated: November 15, 2021 + Last updated: November 16, 2021 $Id$ */ @@ -64,7 +64,7 @@ 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 * 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, Uint32 cnt); +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); void draw_brush_fill_single(SDL_Surface * canvas, int x, int y, Uint32 draw_color, Uint8 * touched); @@ -133,10 +133,10 @@ Uint32 blend(SDL_Surface * canvas, Uint32 draw_colr, Uint32 old_colr, double pct } 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, last, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched, 0, 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 * 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, Uint32 cnt) +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; double in_line, closeness; @@ -144,11 +144,6 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * last, Uint32 px_colr; Uint8 touch_byt; - /* Don't blow up the stack! */ - /* FIXME: Would be better to do this a more reliable way */ - if (cnt >= 20000) - return; - /* "Same" color? No need to fill */ if (!would_flood_fill(canvas, cur_colr, old_colr)) return; @@ -317,7 +312,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * last, ) ) { - simulate_flood_fill_outside_check(screen, last, canvas, i, y - 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1, cnt + 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[last->format->BytesPerPixel] (last, i, y + 1); @@ -329,7 +324,7 @@ void simulate_flood_fill_outside_check(SDL_Surface * screen, SDL_Surface * last, ) ) { - simulate_flood_fill_outside_check(screen, last, canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1, cnt + 1); + simulate_flood_fill_outside_check(screen, last, canvas, i, y + 1, cur_colr, old_colr, x1, y1, x2, y2, touched, y_outside + 1); } } } From 2f912bd5d724df5802eb47747c53206d3d1bf1f3 Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Wed, 17 Nov 2021 00:30:35 -0800 Subject: [PATCH 2/5] WIP Prepare to retrofit fill routine w/ span fill Add a queue and queue helper functions (disabled via #ifdef at the moment). --- src/fill.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/fill.c b/src/fill.c index f82ced30c..b5f4e16ef 100644 --- a/src/fill.c +++ b/src/fill.c @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA (See COPYING.txt) - Last updated: November 16, 2021 + Last updated: November 17, 2021 $Id$ */ @@ -60,12 +60,101 @@ #define WIDE_MATCH_THRESHOLD 3 +// #define USE_QUEUE + +#ifdef USE_QUEUE + +/* Queue for span filling + (https://en.wikipedia.org/wiki/Flood_fill#Span_Filling) +*/ + +#define QUEUE_SIZE_CHUNK 1024 + +typedef struct queue_s { + int x1, x2, y, yd; +} queue_t; + +queue_t * queue = NULL; +int queue_size = 0, queue_start = 0, queue_end = 0; + +#endif + /* Local function prototypes: */ 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 * 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); void draw_brush_fill_single(SDL_Surface * canvas, int x, int y, Uint32 draw_color, Uint8 * touched); +#ifdef USE_QUEUE +void init_queue(void); +void add_to_queue(int x1, int x2, int y, int yd); +int remove_from_queue(int * x1, int * x2, int * y, int * yd); +void cleanup_queue(void); +#endif + +#ifdef USE_QUEUE +void init_queue(void) { + queue_size = 0; + queue_start = 0; + queue_end = 0; + + queue = (queue_t *) realloc(queue, sizeof(queue_t) * QUEUE_SIZE_CHUNK); + if (queue == NULL) + { + fprintf(stderr, "Fill queue cannot be malloc()'d\n"); + return; + } + + queue_size = QUEUE_SIZE_CHUNK; +} + +void add_to_queue(int x1, int x2, int y, int yd) { + /* Reallocate if we need more space */ + if (queue_end + 1 > queue_size) + { + queue_t * tmp; + tmp = (queue_t *) realloc(queue, sizeof(queue_t) * (queue_size + QUEUE_SIZE_CHUNK)); + if (tmp == NULL) + { + fprintf(stderr, "Fill queue cannot be realloc()'d\n"); + return; + } + queue_size += QUEUE_SIZE_CHUNK; + queue = tmp; + } + + queue[queue_end].x1 = x1; + queue[queue_end].x2 = x2; + queue[queue_end].y = y; + queue[queue_end].yd = yd; + + queue_end++; +} + +int remove_from_queue(int * x1, int * x2, int * y, int * yd) { + if (queue_start == queue_end) + return 0; + + *x1 = queue[queue_start].x1; + *x2 = queue[queue_start].x2; + *y = queue[queue_start].y; + *yd = queue[queue_start].yd; + + queue_start++; + + return 1; +} + +void cleanup_queue(void) { + if (queue != NULL) + free(queue); + + queue_size = 0; + queue_start = 0; + queue_end = 0; +} + +#endif /* Returns how similar colors 'c1' and 'c2' are */ From 07165fc3aee388d3a6c20d00758fa30dce2e82a9 Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Wed, 17 Nov 2021 00:55:30 -0800 Subject: [PATCH 3/5] WIP More work towards span filling Antialiased edge stuff will be hard to port over :( --- src/fill.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/src/fill.c b/src/fill.c index b5f4e16ef..7db89ae9d 100644 --- a/src/fill.c +++ b/src/fill.c @@ -74,8 +74,9 @@ typedef struct queue_s { int x1, x2, y, yd; } queue_t; -queue_t * queue = NULL; +queue_t * queue; int queue_size = 0, queue_start = 0, queue_end = 0; +static unsigned char progbar_anim = 0; #endif @@ -90,6 +91,7 @@ void init_queue(void); void add_to_queue(int x1, int x2, int y, int yd); int remove_from_queue(int * x1, int * x2, int * y, int * yd); void cleanup_queue(void); +void track_extents_and_progbar(int x, int y, int * extent_x1, int * extent_y1, int * extent_x2, int * extent_y2, SDL_Surface * screen, SDL_Surface * canvas); #endif #ifdef USE_QUEUE @@ -98,7 +100,7 @@ void init_queue(void) { queue_start = 0; queue_end = 0; - queue = (queue_t *) realloc(queue, sizeof(queue_t) * QUEUE_SIZE_CHUNK); + queue = (queue_t *) malloc(sizeof(queue_t) * QUEUE_SIZE_CHUNK); if (queue == NULL) { fprintf(stderr, "Fill queue cannot be malloc()'d\n"); @@ -154,6 +156,25 @@ void cleanup_queue(void) { queue_end = 0; } +void track_extents_and_progbar(int x, int y, int * extent_x1, int * extent_y1, int * extent_x2, int * extent_y2, SDL_Surface * screen, SDL_Surface * canvas) { + if (x < *extent_x1) + *extent_x1 = x; + if (x > *extent_x2) + *extent_x2 = x; + + if (y < *extent_y1) + *extent_y1 = y; + if (y > *extent_y2) + *extent_y2 = y; + + progbar_anim++; + if ((progbar_anim % 4) == 0) + { + show_progress_bar(screen); + playsound(canvas, 1, SND_FILL, 1, x, SNDDIST_NEAR); + } +} + #endif @@ -221,8 +242,69 @@ Uint32 blend(SDL_Surface * canvas, Uint32 draw_colr, Uint32 old_colr, double pct return SDL_MapRGB(canvas->format, new_r, new_g, new_b); } -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, last, canvas, x, y, cur_colr, old_colr, x1, y1, x2, y2, touched, 0); +void simulate_flood_fill(SDL_Surface * screen, SDL_Surface * last, SDL_Surface * canvas, int x, int y, Uint32 cur_colr, Uint32 old_colr, int * extent_x1, int * extent_y1, int * extent_x2, int * extent_y2, Uint8 * touched) { +#ifdef USE_QUEUE + int x1, x2, dy; + + /* "Same" color? No need to fill */ + if (!would_flood_fill(canvas, cur_colr, old_colr)) + return; + + if (x < 0 || x >= canvas->w || y < 0 || y >= canvas->h) + return; + + /* Don't re-visit the same pixel */ + if (touched && touched[(y * canvas->w) + x]) + return; + + /* Queue up the first things to work on: */ + init_queue(); + + add_to_queue(x, x, y, 1); + add_to_queue(x, x, y - 1, -1); + + /* Do the work (possibly queuing more, as we go) */ + while (remove_from_queue(&x1, &x2, &y, &dy)) + { + x = x1; + + if (Inside(x, y)) + { + while (Inside(x - 1, y)) + { + Set(x - 1, y); + track_extents_and_progbar(x - 1, y, extent_x1, extent_y1, extent_x2, extent_y2, screen, canvas); + + x = x - 1; + } + } + + if (x < x1) + add_to_queue(x, x1 - 1, y - dy, -dy); + + while (x1 < x2) + { + Set(x1, y); + track_extents_and_progbar(x1, y, extent_x1, extent_y1, extent_x2, extent_y2, screen, canvas); + + x1 = x1 + 1; + } + + add_to_queue(x, x1 - 1, y + dy, dy); + + if (x1 - 1 > x2) + add_to_queue(x2 + 1, x1 - 1, y - dy, -dy); + + while (x1 < x2 && !Inside(x1, y)) + x1++; + + x = x1; + } + + cleanup_queue(); +#else + simulate_flood_fill_outside_check(screen, last, canvas, x, y, cur_colr, old_colr, extent_x1, extent_y1, extent_x2, extent_y2, touched, 0); +#endif } 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) From 0b9abc90059e0bb9c13155b22338cedbed2af44c Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Fri, 19 Nov 2021 00:50:58 -0800 Subject: [PATCH 4/5] Forgot to mention Reflection Magic in CHANGES.txt --- docs/CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index 172e64f2a..8e7073d40 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -8,7 +8,7 @@ http://www.tuxpaint.org/ $Id$ -2021.November.16 (0.9.27) +2021.November.19 (0.9.27) * New Magic Tools: ---------------- * "Lightning" - Draws a bolt of lightning striking between @@ -25,6 +25,9 @@ $Id$ style effects!) (Closes https://sourceforge.net/p/tuxpaint/feature-requests/204/) + * "Reflection" - Create a reflection of part of the image, + below (as in a lake reflection), above, to the left, or to the right. + * "Smooth Rainbow" - A smooth, gradient variation of the classic "Rainbow" Magic tool. From dffd026955af8a14426bb4f4fc5da836552fb52b Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Fri, 19 Nov 2021 08:18:13 -0500 Subject: [PATCH 5/5] Update macOS version --- macos/Info.plist | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/macos/Info.plist b/macos/Info.plist index 413ee8049..b6e7726db 100644 --- a/macos/Info.plist +++ b/macos/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable tuxpaint CFBundleGetInfoString - 0.9.26, Copyright 2009-2021, Tux Paint Development Team + 0.9.27, Copyright 2009-2021, Tux Paint Development Team CFBundleIconFile tuxpaint.icns CFBundleIdentifier @@ -19,10 +19,10 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.26 + 0.9.27 CFBundleSignature TXPT CFBundleVersion - 2021-06-13 + 2021-11-19