diff --git a/compiler/res/prog8lib/c64/graphics.p8 b/compiler/res/prog8lib/c64/graphics.p8 index 97bef4a42..c8d615b2b 100644 --- a/compiler/res/prog8lib/c64/graphics.p8 +++ b/compiler/res/prog8lib/c64/graphics.p8 @@ -110,6 +110,8 @@ graphics { sub circle(uword xcenter, ubyte ycenter, ubyte radius) { ; Midpoint algorithm + if radius==0 + return ubyte @zp ploty ubyte @zp xx = radius ubyte @zp yy = 0 @@ -148,8 +150,10 @@ graphics { sub disc(uword xcenter, ubyte ycenter, ubyte radius) { ; Midpoint algorithm, filled - ubyte xx = radius - ubyte yy = 0 + if radius==0 + return + ubyte @zp xx = radius + ubyte @zp yy = 0 byte decisionOver2 = 1-xx as byte while xx>=yy { diff --git a/compiler/res/prog8lib/cx16/gfx2.p8 b/compiler/res/prog8lib/cx16/gfx2.p8 index c7fbdfa6b..b9d79ff38 100644 --- a/compiler/res/prog8lib/cx16/gfx2.p8 +++ b/compiler/res/prog8lib/cx16/gfx2.p8 @@ -98,7 +98,31 @@ gfx2 { position(0, 0) } - sub horizontal_line(uword x, uword y, ubyte length, ubyte color) { + sub rect(uword x, uword y, uword width, uword height, ubyte color) { + if width==0 or height==0 + return + horizontal_line(x, y, width, color) + if height==1 + return + horizontal_line(x, y+height-1, width, color) + vertical_line(x, y+1, height-2, color) + if width==1 + return + vertical_line(x+width-1, y+1, height-2, color) + } + + sub fillrect(uword x, uword y, uword width, uword height, ubyte color) { + if width==0 + return + repeat height { + horizontal_line(x, y, width, color) + y++ + } + } + + sub horizontal_line(uword x, uword y, uword length, ubyte color) { + if length==0 + return when active_mode { 1 -> { ; 8bpp mode @@ -117,8 +141,108 @@ gfx2 { } } - sub circle(uword xcenter, uword ycenter, ubyte radius, ubyte color) { + sub vertical_line(uword x, uword y, uword height, ubyte color) { + ; TODO optimize this to use vera special increment mode + repeat height { + plot(x, y, color) + y++ + } + } + + sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) { + ; Bresenham algorithm. + ; This code special-cases various quadrant loops to allow simple ++ and -- operations. + ; TODO rewrite this in optimized assembly + if y1>y2 { + ; make sure dy is always positive to have only 4 instead of 8 special cases + swap(x1, x2) + swap(y1, y2) + } + word @zp dx = x2-x1 as word + word @zp dy = y2-y1 as word + + if dx==0 { + vertical_line(x1, y1, abs(dy)+1 as uword, 255) + return + } + if dy==0 { + if x1>x2 + x1=x2 + horizontal_line(x1, y1, abs(dx)+1 as uword, 255) + return + } + + word @zp d = 0 + ubyte positive_ix = true + if dx < 0 { + dx = -dx + positive_ix = false + } + dx *= 2 + dy *= 2 + cx16.r14 = x1 ; internal plot X + + if dx >= dy { + if positive_ix { + repeat { + plot(cx16.r14, y1, color) + if cx16.r14==x2 + return + cx16.r14++ + d += dy + if d > dx { + y1++ + d -= dx + } + } + } else { + repeat { + plot(cx16.r14, y1, color) + if cx16.r14==x2 + return + cx16.r14-- + d += dy + if d > dx { + y1++ + d -= dx + } + } + } + } + else { + if positive_ix { + repeat { + plot(cx16.r14, y1, color) + if y1 == y2 + return + y1++ + d += dx + if d > dy { + cx16.r14++ + d -= dy + } + } + } else { + repeat { + plot(cx16.r14, y1, color) + if y1 == y2 + return + y1++ + d += dx + if d > dy { + cx16.r14-- + d -= dy + } + } + } + } + } + + sub circle(uword @zp xcenter, uword @zp ycenter, ubyte radius, ubyte color) { ; Midpoint algorithm. + if radius==0 + return + ubyte @zp xx = radius ubyte @zp yy = 0 word @zp decisionOver2 = (1 as word)-xx @@ -157,28 +281,29 @@ gfx2 { } } - sub disc(uword xcenter, uword ycenter, ubyte radius, ubyte color) { + sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) { ; Midpoint algorithm, filled - ubyte @zp xx = radius + if radius==0 + return ubyte @zp yy = 0 - word @zp decisionOver2 = (1 as word)-xx + word @zp decisionOver2 = (1 as word)-radius - while xx>=yy { - horizontal_line(xcenter-xx, ycenter+yy, xx*2+1, color) - horizontal_line(xcenter-xx, ycenter-yy, xx*2+1, color) - horizontal_line(xcenter-yy, ycenter+xx, yy*2+1, color) - horizontal_line(xcenter-yy, ycenter-xx, yy*2+1, color) + while radius>=yy { + horizontal_line(xcenter-radius, ycenter+yy, radius*2+1, color) + horizontal_line(xcenter-radius, ycenter-yy, radius*2+1, color) + horizontal_line(xcenter-yy, ycenter+radius, yy*2+1, color) + horizontal_line(xcenter-yy, ycenter-radius, yy*2+1, color) yy++ if decisionOver2<=0 decisionOver2 += (yy as word)*2+1 else { - xx-- - decisionOver2 += (yy as word -xx)*2+1 + radius-- + decisionOver2 += (yy as word -radius)*2+1 } } } - sub plot(uword x, uword y, ubyte color) { + sub plot(uword @zp x, uword y, ubyte color) { ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1] uword addr ubyte value @@ -204,7 +329,7 @@ gfx2 { color = cx16.VERA_DATA0 } - sub position(uword x, uword y) { + sub position(uword @zp x, uword y) { when active_mode { 0 -> { cx16.r0 = y*(320/8) + x/8 @@ -280,7 +405,7 @@ gfx2 { } } - sub text(uword x, uword y, ubyte color, uword sctextptr) { + sub text(uword @zp x, uword y, ubyte color, uword sctextptr) { ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; You must also have called text_charset() first to select and prepare the character set to use. ; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 ! diff --git a/compiler/res/prog8lib/cx16/graphics.p8 b/compiler/res/prog8lib/cx16/graphics.p8 index 03a61cd5c..731810753 100644 --- a/compiler/res/prog8lib/cx16/graphics.p8 +++ b/compiler/res/prog8lib/cx16/graphics.p8 @@ -44,6 +44,8 @@ graphics { ;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK ; Midpoint algorithm + if radius==0 + return ubyte @zp xx = radius ubyte @zp yy = 0 byte @zp decisionOver2 = 1-xx as byte @@ -94,8 +96,10 @@ graphics { ; cx16.r3 = radius*2 ; cx16.GRAPH_draw_oval(true) ; currently this call is not implemented on cx16, does a BRK - ubyte xx = radius - ubyte yy = 0 + if radius==0 + return + ubyte @zp xx = radius + ubyte @zp yy = 0 byte decisionOver2 = 1-xx as byte while xx>=yy { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 6de394cb6..0ceb58999 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -94,7 +94,7 @@ Full-screen multicolor bitmap graphics routines, available on the Cx16 machine o - clearing screen, switching screen mode - drawing pixels - drawing text inside the bitmap -- **TODO:** lines, circles, discs. +- lines, rectangles, circles, discs. palette (cx16 only) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 78a02761a..44edbf0c7 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,7 @@ TODO ==== +- implement gfx2's rect, fillrect, horizontal_line and vertical_line in graphics modules as well. - detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' diff --git a/examples/cx16/testgfx2.p8 b/examples/cx16/testgfx2.p8 index 1fe7ab271..a45af13ed 100644 --- a/examples/cx16/testgfx2.p8 +++ b/examples/cx16/testgfx2.p8 @@ -13,10 +13,6 @@ main { for mode in modes { gfx2.screen_mode(mode) draw() - ubyte tp - for tp in 0 to 15 { - gfx2.text(19+tp,20+tp*11, 5, @"ScreenCODE text! 1234![]<>#$%&*()") - } cx16.wait(200) } @@ -25,6 +21,45 @@ main { } sub draw() { + + gfx2.rect(10,10, 1, 1, 255) + gfx2.rect(20,10, 2, 1, 255) + gfx2.rect(30,10, 3, 1, 255) + gfx2.rect(40,10, 1, 2, 255) + gfx2.rect(50,10, 1, 3, 255) + gfx2.rect(60,10, 2, 2, 255) + gfx2.rect(70,10, 3, 3, 255) + gfx2.rect(80,10, 4, 4, 255) + gfx2.rect(90,10, 5, 5, 255) + gfx2.rect(100,10, 8, 8, 255) + gfx2.rect(110,10, 20, 5, 255) + gfx2.rect(80, 80, 200, 140, 255) + + gfx2.fillrect(10,40, 1, 1, 5) + gfx2.fillrect(20,40, 2, 1, 5) + gfx2.fillrect(30,40, 3, 1, 5) + gfx2.fillrect(40,40, 1, 2, 5) + gfx2.fillrect(50,40, 1, 3, 5) + gfx2.fillrect(60,40, 2, 2, 5) + gfx2.fillrect(70,40, 3, 3, 5) + gfx2.fillrect(80,40, 4, 4, 5) + gfx2.fillrect(90,40, 5, 5, 5) + gfx2.fillrect(100,40, 8, 8, 5) + gfx2.fillrect(110,40, 20, 5, 5) + gfx2.fillrect(82, 82, 200-4, 140-4, 5) + + ubyte i + for i in 0 to 255 step 4 { + uword x1 = ((gfx2.width-256)/2 as uword) + sin8u(i) + uword y1 = (gfx2.height-128)/2 + cos8u(i)/2 + uword x2 = ((gfx2.width-64)/2 as uword) + sin8u(i)/4 + uword y2 = (gfx2.height-64)/2 + cos8u(i)/4 + gfx2.line(x1, y1, x2, y2, i) + } + + cx16.wait(60) + gfx2.clear_screen() + ubyte radius for radius in 110 downto 8 step -4 { @@ -32,10 +67,11 @@ main { } gfx2.disc(gfx2.width/2, gfx2.height/2, 108, 255) + + ubyte tp + for tp in 0 to 15 { + gfx2.text(19+tp,20+tp*11, 5, @"ScreenCODE text! 1234![]<>#$%&*()") + } + } } - -gfx3 { - - -} diff --git a/examples/test.p8 b/examples/test.p8 index 1c816a981..9c9fbe496 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,6 +7,15 @@ main { sub start () { + uword length + + if length>256 { + repeat length-1 + gfx2.next_pixel(color) + } else { + repeat (length-1) as ubyte ; TODO lsb(length-1) doesn't work!?!?!? + gfx2.next_pixel(color) + } test_stack.test()