this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

flood fill paint function (#2683)

* flood fill paint function

* increase max paint stack to 4000

* optional paint argument bordercolor

* replace painter stack with queue

* fix janet and wren bindings for paint function

authored by

Matt Zykan and committed by
GitHub
e63ab8d2 2d4415fe

+257 -1
+12
src/api.h
··· 632 632 tic_mem*, s32 x, s32 y, s32 a, s32 b, u8 color) \ 633 633 \ 634 634 \ 635 + macro(paint, \ 636 + "paint(x y color bordercolor=-1)", \ 637 + \ 638 + "This function fills a contiguous area with a new color.\n" \ 639 + "If bordercolor is given fill will extend to color boundary.", \ 640 + 4, \ 641 + 3, \ 642 + 0, \ 643 + void, \ 644 + tic_mem*, s32 x, s32 y, u8 color, u8 bordercolor) \ 645 + \ 646 + \ 635 647 macro(tri, \ 636 648 "tri(x1 y1 x2 y2 x3 y3 color)", \ 637 649 \
+16
src/api/janet.c
··· 66 66 static Janet janet_circb(int32_t argc, Janet* argv); 67 67 static Janet janet_elli(int32_t argc, Janet* argv); 68 68 static Janet janet_ellib(int32_t argc, Janet* argv); 69 + static Janet janet_paint(int32_t argc, Janet* argv); 69 70 static Janet janet_tri(int32_t argc, Janet* argv); 70 71 static Janet janet_trib(int32_t argc, Janet* argv); 71 72 static Janet janet_ttri(int32_t argc, Janet* argv); ··· 130 131 {"circb", janet_circb, NULL}, 131 132 {"elli", janet_elli, NULL}, 132 133 {"ellib", janet_ellib, NULL}, 134 + {"paint", janet_paint, NULL}, 133 135 {"tri", janet_tri, NULL}, 134 136 {"trib", janet_trib, NULL}, 135 137 {"ttri", janet_ttri, NULL}, ··· 830 832 831 833 tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core; 832 834 core->api.ellib(tic, x, y, a, b, color); 835 + return janet_wrap_nil(); 836 + } 837 + 838 + static Janet janet_paint(int32_t argc, Janet* argv) 839 + { 840 + janet_arity(argc, 3, 4); 841 + 842 + s32 x = janet_getinteger(argv, 0); 843 + s32 y = janet_getinteger(argv, 1); 844 + u8 color = janet_getinteger(argv, 2); 845 + u8 bordercolor = janet_optnumber(argv, argc, 3, 255); 846 + 847 + tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core; 848 + core->api.paint(tic, x, y, color, bordercolor); 833 849 return janet_wrap_nil(); 834 850 } 835 851
+14
src/api/js.c
··· 769 769 return JS_UNDEFINED; 770 770 } 771 771 772 + static JSValue js_paint(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv) 773 + { 774 + s32 x = getInteger(ctx, argv[0]); 775 + s32 y = getInteger(ctx, argv[1]); 776 + s32 color = getInteger(ctx, argv[2]); 777 + s32 bordercolor = getInteger2(ctx, argv[3], -1); 778 + 779 + tic_core* core = getCore(ctx); tic_mem* tic = (tic_mem*)core; 780 + 781 + core->api.paint(tic, x, y, color, bordercolor); 782 + 783 + return JS_UNDEFINED; 784 + } 785 + 772 786 static JSValue js_tri(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv) 773 787 { 774 788 float pt[6];
+21
src/api/luaapi.c
··· 209 209 return 0; 210 210 } 211 211 212 + static s32 lua_paint(lua_State* lua) 213 + { 214 + s32 top = lua_gettop(lua); 215 + 216 + if(top >= 3 && top <= 4) 217 + { 218 + s32 x = getLuaNumber(lua, 1); 219 + s32 y = getLuaNumber(lua, 2); 220 + s32 color = getLuaNumber(lua, 3); 221 + s32 bordercolor = top >= 4 ? getLuaNumber(lua, 4) : -1; 222 + 223 + tic_core* core = getLuaCore(lua); 224 + tic_mem* tic = (tic_mem*)core; 225 + 226 + core->api.paint(tic, x, y, color, bordercolor); 227 + } 228 + else luaL_error(lua, "invalid parameters, paint(x y color [bordercolor])\n"); 229 + 230 + return 0; 231 + } 232 + 212 233 static s32 lua_pix(lua_State* lua) 213 234 { 214 235 s32 top = lua_gettop(lua);
+13
src/api/mruby.c
··· 270 270 return mrb_nil_value(); 271 271 } 272 272 273 + static mrb_value mrb_paint(mrb_state* mrb, mrb_value self) 274 + { 275 + mrb_int x, y, color; 276 + mrb_int bordercolor = -1; 277 + mrb_get_args(mrb, "iii|i", &x, &y, &color, &bordercolor); 278 + 279 + tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 280 + 281 + core->api.paint(tic, x, y, color, bordercolor); 282 + 283 + return mrb_nil_value(); 284 + } 285 + 273 286 static mrb_value mrb_tri(mrb_state* mrb, mrb_value self) 274 287 { 275 288 mrb_float x1, y1, x2, y2, x3, y3;
+22
src/api/python.c
··· 235 235 return 0; 236 236 } 237 237 238 + static int py_paint(pkpy_vm* vm) 239 + { 240 + int x; 241 + int y; 242 + int color; 243 + int bordercolor; 244 + 245 + pkpy_to_int(vm, 0, &x); 246 + pkpy_to_int(vm, 1, &y); 247 + pkpy_to_int(vm, 2, &color); 248 + pkpy_to_int(vm, 3, &bordercolor); 249 + tic_core* core; get_core(vm, &core); tic_mem* tic = (tic_mem*)core; 250 + if(pkpy_check_error(vm)) 251 + return 0; 252 + 253 + core->api.paint(tic, x, y, color, bordercolor); 254 + return 0; 255 + } 256 + 238 257 static int py_clip(pkpy_vm* vm) 239 258 { 240 259 ··· 1249 1268 1250 1269 pkpy_push_function(vm, "music(track=-1, frame=-1, row=-1, loop=True, sustain=False, tempo=-1, speed=-1)", py_music); 1251 1270 pkpy_setglobal_2(vm, "music"); 1271 + 1272 + pkpy_push_function(vm, "paint(x: int, y: int, color: int, bordercolor=-1)", py_paint); 1273 + pkpy_setglobal_2(vm, "paint"); 1252 1274 1253 1275 pkpy_push_function(vm, "peek(addr: int, bits=8) -> int", py_peek); 1254 1276 pkpy_setglobal_2(vm, "peek");
+12
src/api/scheme.c
··· 569 569 core->api.ellib(tic, x, y, a, b, color); 570 570 return s7_nil(sc); 571 571 } 572 + s7_pointer scheme_paint(s7_scheme* sc, s7_pointer args) 573 + { 574 + // paint(x y color bordercolor=-1) 575 + const int argn = s7_list_length(sc, args); 576 + tic_core* core = getSchemeCore(sc); tic_mem* tic = (tic_mem*)core; 577 + const s32 x = s7_integer(s7_car(args)); 578 + const s32 y = s7_integer(s7_cadr(args)); 579 + const s32 color = s7_integer(s7_caddr(args)); 580 + const s32 bordercolor = argn >= 4 ? s7_integer(s7_cadddr(args)) : -1; 581 + core->api.paint(tic, x, y, color, bordercolor); 582 + return s7_nil(sc); 583 + } 572 584 s7_pointer scheme_tri(s7_scheme* sc, s7_pointer args) 573 585 { 574 586 // tri(x1 y1 x2 y2 x3 y3 color)
+20
src/api/squirrel.c
··· 423 423 return 0; 424 424 } 425 425 426 + static SQInteger squirrel_paint(HSQUIRRELVM vm) 427 + { 428 + SQInteger top = sq_gettop(vm); 429 + 430 + if(top >= 4 && top <= 5) 431 + { 432 + s32 x = getSquirrelNumber(vm, 2); 433 + s32 y = getSquirrelNumber(vm, 3); 434 + s32 color = getSquirrelNumber(vm, 4); 435 + s32 bordercolor = top >= 5 ? getSquirrelNumber(vm, 5) : -1; 436 + 437 + tic_core* core = getSquirrelCore(vm); tic_mem* tic = (tic_mem*)core; 438 + 439 + core->api.paint(tic, x, y, color, bordercolor); 440 + } 441 + else return sq_throwerror(vm, "invalid parameters, paint(x,y,color,[bordercolor=-1])\n"); 442 + 443 + return 0; 444 + } 445 + 426 446 static SQInteger squirrel_tri(HSQUIRRELVM vm) 427 447 { 428 448 SQInteger top = sq_gettop(vm);
+13
src/api/wasm.c
··· 223 223 m3ApiSuccess(); 224 224 } 225 225 226 + m3ApiRawFunction(wasmtic_paint) 227 + { 228 + m3ApiGetArg (int32_t, x) 229 + m3ApiGetArg (int32_t, y) 230 + m3ApiGetArg (int8_t, color) 231 + m3ApiGetArg (int8_t, bordercolor) 232 + 233 + tic_core* core = getWasmCore(runtime); tic_mem* tic = (tic_mem*)core; 234 + core->api.paint(tic, x, y, color, bordercolor); 235 + 236 + m3ApiSuccess(); 237 + } 238 + 226 239 m3ApiRawFunction(wasmtic_rect) 227 240 { 228 241 m3ApiGetArg (int32_t, x)
+17
src/api/wren.c
··· 98 98 foreign static circb(x, y, radius, color)\n\ 99 99 foreign static elli(x, y, a, b, color)\n\ 100 100 foreign static ellib(x, y, a, b, color)\n\ 101 + foreign static paint(x, y, color)\n\ 102 + foreign static paint(x, y, color, bordercolor)\n\ 101 103 foreign static rect(x, y, w, h, color)\n\ 102 104 foreign static rectb(x, y, w, h, color)\n\ 103 105 foreign static tri(x1, y1, x2, y2, x3, y3, color)\n\ ··· 977 979 core->api.ellib(tic, x, y, a, b, color); 978 980 } 979 981 982 + static void wren_paint(WrenVM* vm) 983 + { 984 + s32 top = wrenGetSlotCount(vm); 985 + s32 x = getWrenNumber(vm, 1); 986 + s32 y = getWrenNumber(vm, 2); 987 + s32 color = getWrenNumber(vm, 3); 988 + s32 bordercolor = top > 4 ? getWrenNumber(vm, 4) : -1; 989 + 990 + tic_core* core = getWrenCore(vm); tic_mem* tic = (tic_mem*)core; 991 + 992 + core->api.paint(tic, x, y, color, bordercolor); 993 + } 994 + 980 995 static void wren_rect(WrenVM* vm) 981 996 { 982 997 s32 x = getWrenNumber(vm, 1); ··· 1548 1563 if (strcmp(signature, "static TIC.circb(_,_,_,_)" ) == 0) return wren_circb; 1549 1564 if (strcmp(signature, "static TIC.elli(_,_,_,_,_)" ) == 0) return wren_elli; 1550 1565 if (strcmp(signature, "static TIC.ellib(_,_,_,_,_)" ) == 0) return wren_ellib; 1566 + if (strcmp(signature, "static TIC.paint(_,_,_)" ) == 0) return wren_paint; 1567 + if (strcmp(signature, "static TIC.paint(_,_,_,_)" ) == 0) return wren_paint; 1551 1568 if (strcmp(signature, "static TIC.rect(_,_,_,_,_)" ) == 0) return wren_rect; 1552 1569 if (strcmp(signature, "static TIC.rectb(_,_,_,_,_)" ) == 0) return wren_rectb; 1553 1570 if (strcmp(signature, "static TIC.tri(_,_,_,_,_,_,_)" ) == 0) return wren_tri;
+97 -1
src/core/draw.c
··· 80 80 tic_api_poke4((tic_mem*)core, y * TIC80_WIDTH + x, color); 81 81 } 82 82 83 - static u8 getPixel(tic_core* core, s32 x, s32 y) 83 + static inline u8 getPixel(tic_core* core, s32 x, s32 y) 84 84 { 85 85 return x < 0 || y < 0 || x >= TIC80_WIDTH || y >= TIC80_HEIGHT 86 86 ? 0 ··· 613 613 setPixel((tic_core*)tic, x1, y1, color); 614 614 } 615 615 616 + // Queue frame for floodFill. 617 + // Filled horizontal segment of scanline y for xl <= x <= xr. 618 + // Parent segment was on line y – dy. dy = 1 or –1. 619 + typedef struct 620 + { 621 + s32 y; 622 + s32 xl; 623 + s32 xr; 624 + s32 dy; 625 + } FillSegment; 626 + 627 + #define FILLQUEUESIZE 400 628 + static struct 629 + { 630 + FillSegment seg[FILLQUEUESIZE]; 631 + size_t ini; // index of empty next in 632 + size_t outi; // index of next out 633 + } fillQueue; 634 + 635 + static inline void fillEnqueue(tic_core* tic, s32 y, s32 xl, s32 xr, s32 dy) 636 + { 637 + size_t nextini = (fillQueue.ini + 1) % FILLQUEUESIZE; 638 + if (nextini == fillQueue.outi) 639 + return; // queue full 640 + if (y + dy < tic->state.clip.t || y + dy >= tic->state.clip.b) 641 + return; 642 + FillSegment* qseg = &fillQueue.seg[fillQueue.ini]; 643 + qseg->y = y; 644 + qseg->xl = xl; 645 + qseg->xr = xr; 646 + qseg->dy = dy; 647 + fillQueue.ini = nextini; 648 + } 649 + 650 + static inline bool fillDequeue(s32* y, s32* xl, s32* xr, s32* dy) 651 + { 652 + if (fillQueue.ini == fillQueue.outi) 653 + return false; // queue empty 654 + FillSegment* qseg = &fillQueue.seg[fillQueue.outi]; 655 + *y = qseg->y + qseg->dy; 656 + *xl = qseg->xl; 657 + *xr = qseg->xr; 658 + *dy = qseg->dy; 659 + fillQueue.outi = (fillQueue.outi + 1) % FILLQUEUESIZE; 660 + return true; 661 + } 662 + 663 + static inline bool floodFillInside(u8 pix, u8 paint, u8 border, u8 original) 664 + { 665 + return border == 255 ? pix == original : pix != paint && pix != border; 666 + } 667 + 668 + // "A Seed Fill Algorithm", Paul S. Heckbert, Graphics Gems, Andrew Glassner 669 + // https://github.com/erich666/GraphicsGems/blob/master/gems/SeedFill.c 670 + static void floodFill(tic_core* tic, s32 x, s32 y, u8 color, u8 border) 671 + { 672 + if (x < tic->state.clip.l || y < tic->state.clip.t || x >= tic->state.clip.r || y >= tic->state.clip.b) 673 + return; 674 + u8 ov = getPixel(tic, x, y); 675 + if (ov == color || ov == border) 676 + return; 677 + fillQueue.ini = fillQueue.outi = 0; 678 + fillEnqueue(tic, y, x, x, 1); // needed in some cases 679 + fillEnqueue(tic, y + 1, x, x, -1); // seed segment 680 + s32 l, x1, x2, dy; 681 + while (fillDequeue(&y, &x1, &x2, &dy)) 682 + { 683 + // segment of scan line y-dy for x1<=x<=x2 was previously filled, 684 + // now explore adjacent pixels in scan line y 685 + for (x = x1; x >= tic->state.clip.l && floodFillInside(getPixel(tic, x, y), color, border, ov); x--) 686 + setPixelFast(tic, x, y, color); 687 + if (x >= x1) 688 + goto floodFill_skip; 689 + l = x + 1; 690 + if (l < x1) 691 + fillEnqueue(tic, y, l, x1 - 1, -dy); // check leak left 692 + x = x1 + 1; 693 + do { 694 + for (; x < tic->state.clip.r && floodFillInside(getPixel(tic, x, y), color, border, ov); x++) 695 + setPixelFast(tic, x, y, color); 696 + fillEnqueue(tic, y, l, x - 1, dy); 697 + if (x > x2 + 1) 698 + fillEnqueue(tic, y, x2 + 1, x - 1, -dy); // check leak right 699 + floodFill_skip: 700 + for (x++; x <= x2 && !floodFillInside(getPixel(tic, x, y), color, border, ov); x++); 701 + l = x; 702 + } while (x <= x2); 703 + } 704 + } 705 + 616 706 typedef union 617 707 { 618 708 struct ··· 914 1004 void tic_api_line(tic_mem* memory, float x0, float y0, float x1, float y1, u8 color) 915 1005 { 916 1006 drawLine(memory, x0, y0, x1, y1, mapColor(memory, color)); 1007 + } 1008 + 1009 + void tic_api_paint(tic_mem* memory, s32 x, s32 y, u8 color, u8 bordercolor) 1010 + { 1011 + bordercolor = bordercolor == 255 ? 255 : mapColor(memory, bordercolor); 1012 + floodFill((tic_core*)memory, x, y, mapColor(memory, color), bordercolor); 917 1013 } 918 1014 919 1015 #if defined(BUILD_DEPRECATED)