From e15bc68c9b3a9505e4f3e8c36362030f0bbff920 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 23 May 2023 00:27:42 +0200 Subject: [PATCH] added gfx2.fill() flood fill routine --- compiler/res/prog8lib/cx16/gfx2.p8 | 119 ++++++++++++++++++++++++++++- docs/source/libraries.rst | 1 + docs/source/todo.rst | 3 +- examples/cx16/testgfx2.p8 | 17 ++++- 4 files changed, 137 insertions(+), 3 deletions(-) diff --git a/compiler/res/prog8lib/cx16/gfx2.p8 b/compiler/res/prog8lib/cx16/gfx2.p8 index 21e33abf7..f87c280aa 100644 --- a/compiler/res/prog8lib/cx16/gfx2.p8 +++ b/compiler/res/prog8lib/cx16/gfx2.p8 @@ -547,7 +547,7 @@ _done } } - sub plot(uword @zp x, uword y, ubyte color) { + sub plot(uword @zp x, uword @zp y, ubyte @zp color) { ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1] ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100] ubyte[4] @shared shift4c = [6,4,2,0] @@ -750,6 +750,123 @@ _done } } + sub fill(word @zp x, word @zp y, ubyte new_color) { + ; Non-recursive scanline flood fill. + ; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm + ; with the fixes applied to the seedfill_4 routine as mentioned in the comments. + const ubyte MAXDEPTH = 48 + word[MAXDEPTH] @shared stack_xl + word[MAXDEPTH] @shared stack_xr + word[MAXDEPTH] @shared stack_y + byte[MAXDEPTH] @shared stack_dy + cx16.r12L = 0 ; stack pointer + word x1 + word x2 + byte dy + cx16.r10L = new_color + sub push_stack(word sxl, word sxr, word sy, byte sdy) { + if cx16.r12L==MAXDEPTH + return + cx16.r0s = sy+sdy + if cx16.r0s>=0 and cx16.r0s<=height-1 { +;; stack_xl[cx16.r12L] = sxl +;; stack_xr[cx16.r12L] = sxr +;; stack_y[cx16.r12L] = sy +;; stack_dy[cx16.r12L] = sdy +;; cx16.r12L++ + %asm {{ + lda cx16.r12L + asl a + tay + lda sxl + sta stack_xl,y + lda sxl+1 + sta stack_xl+1,y + lda sxr + sta stack_xr,y + lda sxr+1 + sta stack_xr+1,y + lda sy + sta stack_y,y + lda sy+1 + sta stack_y+1,y + ldy cx16.r12L + lda sdy + sta stack_dy,y + inc cx16.r12L + }} + } + } + sub pop_stack() { +;; cx16.r12L-- +;; x1 = stack_xl[cx16.r12L] +;; x2 = stack_xr[cx16.r12L] +;; y = stack_y[cx16.r12L] +;; dy = stack_dy[cx16.r12L] +;; y += dy + %asm {{ + dec cx16.r12L + lda cx16.r12L + asl a + tay + lda stack_xl,y + sta x1 + lda stack_xl+1,y + sta x1+1 + lda stack_xr,y + sta x2 + lda stack_xr+1,y + sta x2+1 + lda stack_y,y + sta y + lda stack_y+1,y + sta y+1 + ldy cx16.r12L + lda stack_dy,y + sta dy + }} + y+=dy + } + cx16.r11L = pget(x as uword, y as uword) ; old_color + if cx16.r11L == cx16.r10L + return + if x<0 or x > width-1 or y<0 or y > height-1 + return + push_stack(x, x, y, 1) + push_stack(x, x, y + 1, -1) + word left = 0 + while cx16.r12L { + pop_stack() + x = x1 + while x >= 0 and pget(x as uword, y as uword) == cx16.r11L { + plot(x as uword, y as uword, cx16.r10L) + x-- + } + if x>= x1 + goto skip + + left = x + 1 + if left < x1 + push_stack(left, x1 - 1, y, -dy) + x = x1 + 1 + + do { + while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L { + plot(x as uword, y as uword, cx16.r10L) + x++ + } + push_stack(left, x - 1, y, dy) + if x > x2 + 1 + push_stack(x2 + 1, x - 1, y, -dy) +skip: + x++ + while x <= x2 and pget(x as uword, y as uword) != cx16.r11L + x++ + left = x + } until x>x2 + } + } + sub position(uword @zp x, uword y) { ubyte bank when active_mode { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index cd41c3094..15bc9fef3 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -381,6 +381,7 @@ Full-screen multicolor bitmap graphics routines, available on the Cx16 machine o - clearing screen, switching screen mode, also back to text mode is possible. - drawing and reading individual pixels - drawing lines, rectangles, filled rectangles, circles, discs +- flood fill - drawing text inside the bitmap - in monochrome mode, it's possible to use a stippled drawing pattern to simulate a shade of gray. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 2b661be60..bf51aae79 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -14,6 +14,8 @@ For 9.0 major changes - DONE: for loops now skip the whole loop if from value already outside the loop range (this is what all other programming languages also do) - DONE: asmsub params or return values passed in cpu flags (like carry) now must be declared as booleans (previously ubyte was still accepted). - DONE: (on cx16) added diskio.save_raw() to save without the 2 byte prg header +- DONE: added sys.irqsafe_xxx irqd routines +- DONE: added gfx2.fill() flood fill routine - [much work:] add special (u)word array type (or modifier such as @fast or @split? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction.... @@ -61,7 +63,6 @@ Libraries: - c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking) - optimize several inner loops in gfx2 even further? - add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)? -- add a flood fill (span fill/scanline fill) routine to gfx2? Expressions: diff --git a/examples/cx16/testgfx2.p8 b/examples/cx16/testgfx2.p8 index 5263e3b33..01a35c9aa 100644 --- a/examples/cx16/testgfx2.p8 +++ b/examples/cx16/testgfx2.p8 @@ -9,8 +9,10 @@ main { sub start() { + gfx2.screen_mode(4) + demofill() + sys.wait(2*60) gfx2.screen_mode(5) - demo1() sys.wait(2*60) demo2() @@ -19,6 +21,19 @@ main { txt.print("done!\n") } + sub demofill() { + + gfx2.circle(160, 120, 110, 1) + gfx2.rect(180, 5, 25, 190, 1) + gfx2.line(100, 150, 240, 10, 1) + gfx2.line(101, 150, 241, 10, 1) + ;gfx2.monochrome_stipple(true) + sys.wait(60) + gfx2.fill(100,100,2) + ;gfx2.monochrome_stipple(false) + gfx2.fill(182,140,4) + } + sub demo1() { uword yy = 10 uword xx