Skip to content

Commit

Permalink
flood fill paint function
Browse files Browse the repository at this point in the history
  • Loading branch information
anescient committed Sep 13, 2024
1 parent 70ee0b6 commit 9b7c0a6
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,17 @@ enum
tic_mem*, s32 x, s32 y, s32 a, s32 b, u8 color) \
\
\
macro(paint, \
"paint(x y color)", \
\
"This function fills an area.", \
3, \
3, \
0, \
void, \
tic_mem*, s32 x, s32 y, u8 color) \
\
\
macro(tri, \
"tri(x1 y1 x2 y2 x3 y3 color)", \
\
Expand Down
14 changes: 14 additions & 0 deletions src/api/janet.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static Janet janet_circ(int32_t argc, Janet* argv);
static Janet janet_circb(int32_t argc, Janet* argv);
static Janet janet_elli(int32_t argc, Janet* argv);
static Janet janet_ellib(int32_t argc, Janet* argv);
static Janet janet_paint(int32_t argc, Janet* argv);
static Janet janet_tri(int32_t argc, Janet* argv);
static Janet janet_trib(int32_t argc, Janet* argv);
static Janet janet_ttri(int32_t argc, Janet* argv);
Expand Down Expand Up @@ -833,6 +834,19 @@ static Janet janet_ellib(int32_t argc, Janet* argv)
return janet_wrap_nil();
}

static Janet janet_paint(int32_t argc, Janet* argv)
{
janet_fixarity(argc, 3);

s32 x = janet_getinteger(argv, 0);
s32 y = janet_getinteger(argv, 1);
u8 color = janet_getinteger(argv, 2);

tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core;
core->api.paint(tic, x, y, color);
return janet_wrap_nil();
}

static Janet janet_tri(int32_t argc, Janet* argv)
{
janet_fixarity(argc, 7);
Expand Down
13 changes: 13 additions & 0 deletions src/api/js.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,19 @@ static JSValue js_ellib(JSContext *ctx, JSValueConst this_val, s32 argc, JSValue
return JS_UNDEFINED;
}

static JSValue js_paint(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv)
{
s32 x = getInteger(ctx, argv[0]);
s32 y = getInteger(ctx, argv[1]);
s32 color = getInteger(ctx, argv[2]);

tic_core* core = getCore(ctx); tic_mem* tic = (tic_mem*)core;

core->api.paint(tic, x, y, color);

return JS_UNDEFINED;
}

static JSValue js_tri(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv)
{
float pt[6];
Expand Down
20 changes: 20 additions & 0 deletions src/api/luaapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,26 @@ static s32 lua_cls(lua_State* lua)
return 0;
}

static s32 lua_paint(lua_State* lua)
{
s32 top = lua_gettop(lua);

if(top == 3)
{
s32 x = getLuaNumber(lua, 1);
s32 y = getLuaNumber(lua, 2);
s32 color = getLuaNumber(lua, 3);

tic_core* core = getLuaCore(lua);
tic_mem* tic = (tic_mem*)core;

core->api.paint(tic, x, y, color);
}
else luaL_error(lua, "invalid parameters, paint(x y color)\n");

return 0;
}

static s32 lua_pix(lua_State* lua)
{
s32 top = lua_gettop(lua);
Expand Down
12 changes: 12 additions & 0 deletions src/api/mruby.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ static mrb_value mrb_ellib(mrb_state* mrb, mrb_value self)
return mrb_nil_value();
}

static mrb_value mrb_paint(mrb_state* mrb, mrb_value self)
{
mrb_int x, y, color;
mrb_get_args(mrb, "iii", &x, &y, &color);

tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core;

core->api.paint(tic, x, y, color);

return mrb_nil_value();
}

static mrb_value mrb_tri(mrb_state* mrb, mrb_value self)
{
mrb_float x1, y1, x2, y2, x3, y3;
Expand Down
20 changes: 20 additions & 0 deletions src/api/python.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,23 @@ static int py_ellib(pkpy_vm* vm)
return 0;
}

static int py_paint(pkpy_vm* vm)
{
int x;
int y;
int color;

pkpy_to_int(vm, 0, &x);
pkpy_to_int(vm, 1, &y);
pkpy_to_int(vm, 2, &color);
tic_core* core; get_core(vm, &core); tic_mem* tic = (tic_mem*)core;
if(pkpy_check_error(vm))
return 0;

core->api.paint(tic, x, y, color);
return 0;
}

static int py_clip(pkpy_vm* vm)
{

Expand Down Expand Up @@ -1212,6 +1229,9 @@ static bool setup_c_bindings(pkpy_vm* vm) {
pkpy_push_function(vm, "ellib(x: int, y: int, a: int, b: int, color: int)", py_ellib);
pkpy_setglobal_2(vm, "ellib");

pkpy_push_function(vm, "paint(x: int, y: int, color: int)", py_paint);
pkpy_setglobal_2(vm, "paint");

pkpy_push_function(vm, "exit()", py_exit);
pkpy_setglobal_2(vm, "exit");

Expand Down
10 changes: 10 additions & 0 deletions src/api/scheme.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,16 @@ s7_pointer scheme_ellib(s7_scheme* sc, s7_pointer args)
core->api.ellib(tic, x, y, a, b, color);
return s7_nil(sc);
}
s7_pointer scheme_paint(s7_scheme* sc, s7_pointer args)
{
// ellib(x y a b color)
tic_core* core = getSchemeCore(sc); tic_mem* tic = (tic_mem*)core;
const s32 x = s7_integer(s7_car(args));
const s32 y = s7_integer(s7_cadr(args));
const s32 color = s7_integer(s7_caddr(args));
core->api.paint(tic, x, y, color);
return s7_nil(sc);
}
s7_pointer scheme_tri(s7_scheme* sc, s7_pointer args)
{
// tri(x1 y1 x2 y2 x3 y3 color)
Expand Down
19 changes: 19 additions & 0 deletions src/api/squirrel.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,25 @@ static SQInteger squirrel_ellib(HSQUIRRELVM vm)
return 0;
}

static SQInteger squirrel_paint(HSQUIRRELVM vm)
{
SQInteger top = sq_gettop(vm);

if(top == 6)
{
s32 x = getSquirrelNumber(vm, 2);
s32 y = getSquirrelNumber(vm, 3);
s32 color = getSquirrelNumber(vm, 4);

tic_core* core = getSquirrelCore(vm); tic_mem* tic = (tic_mem*)core;

core->api.paint(tic, x, y, color);
}
else return sq_throwerror(vm, "invalid parameters, paint(x,y,color)\n");

return 0;
}

static SQInteger squirrel_tri(HSQUIRRELVM vm)
{
SQInteger top = sq_gettop(vm);
Expand Down
12 changes: 12 additions & 0 deletions src/api/wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ m3ApiRawFunction(wasmtic_ellib)
m3ApiSuccess();
}

m3ApiRawFunction(wasmtic_paint)
{
m3ApiGetArg (int32_t, x)
m3ApiGetArg (int32_t, y)
m3ApiGetArg (int8_t, color)

tic_core* core = getWasmCore(runtime); tic_mem* tic = (tic_mem*)core;
core->api.paint(tic, x, y, color);

m3ApiSuccess();
}

m3ApiRawFunction(wasmtic_rect)
{
m3ApiGetArg (int32_t, x)
Expand Down
11 changes: 11 additions & 0 deletions src/api/wren.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,17 @@ static void wren_ellib(WrenVM* vm)
core->api.ellib(tic, x, y, a, b, color);
}

static void wren_paint(WrenVM* vm)
{
s32 x = getWrenNumber(vm, 1);
s32 y = getWrenNumber(vm, 2);
s32 color = getWrenNumber(vm, 3);

tic_core* core = getWrenCore(vm); tic_mem* tic = (tic_mem*)core;

core->api.paint(tic, x, y, color);
}

static void wren_rect(WrenVM* vm)
{
s32 x = getWrenNumber(vm, 1);
Expand Down
75 changes: 75 additions & 0 deletions src/core/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,76 @@ static void drawLine(tic_mem* tic, float x0, float y0, float x1, float y1, u8 co
setPixel((tic_core*)tic, x1, y1, color);
}

// Stack frame for floodFill.
// Filled horizontal segment of scanline y for xl # x # xr.
// Parent segment was on line y – dy. dy = 1 or –1.
typedef struct
{
s32 y;
s32 xl;
s32 xr;
s32 dy;
} FillSegment;

#define FILLSTACKMAX 1000
static FillSegment FillStack[FILLSTACKMAX];

// Push a segment on the stack unless it's clipped or stack is full.
static inline void floodFillPush(tic_core* tic, size_t* si, s32 y, s32 xl, s32 xr, s32 dy)
{
if (*si >= FILLSTACKMAX || y + dy < tic->state.clip.t || y + dy >= tic->state.clip.b)
return;
FillStack[*si].y = y;
FillStack[*si].xl = xl;
FillStack[*si].xr = xr;
FillStack[*si].dy = dy;
(*si)++;
}

// "A Seed Fill Algorithm", Paul S. Heckbert, Graphics Gems, Andrew Glassner
// https://github.com/erich666/GraphicsGems/blob/master/gems/SeedFill.c
static void floodFill(tic_core* tic, s32 x, s32 y, u8 color)
{
if (x < tic->state.clip.l || y < tic->state.clip.t || x >= tic->state.clip.r || y >= tic->state.clip.b)
return;
u8 ov = getPixel(tic, x, y);
if (ov == color)
return;
size_t si = 0; // stack count, index of free top
floodFillPush(tic, &si, y, x, x, 1); // needed in some cases
floodFillPush(tic, &si, y + 1, x, x, -1); // seed segment
s32 l, x1, x2, dy;
while (si > 0)
{
// pop segment off stack and fill a neighboring scan line
si--;
dy = FillStack[si].dy;
y = FillStack[si].y + dy;
x1 = FillStack[si].xl;
x2 = FillStack[si].xr;
// segment of scan line y-dy for x1<=x<=x2 was previously filled,
// now explore adjacent pixels in scan line y
for (x = x1; x >= tic->state.clip.l && getPixel(tic, x, y) == ov; x--)
setPixelFast(tic, x, y, color);
if (x >= x1)
goto floodFill_skip;
l = x + 1;
if (l < x1)
floodFillPush(tic, &si, y, l, x1 - 1, -dy); // check leak left
x = x1 + 1;
do {
for (; x < tic->state.clip.r && getPixel(tic, x, y) == ov; x++)
setPixelFast(tic, x, y, color);
floodFillPush(tic, &si, y, l, x - 1, dy);
if (x > x2 + 1)
floodFillPush(tic, &si, y, x2 + 1, x - 1, -dy); // check leak right
floodFill_skip:
for (x++; x <= x2 && getPixel(tic, x, y) != ov; x++);
l = x;
} while (x <= x2);
}
}

typedef union
{
struct
Expand Down Expand Up @@ -916,6 +986,11 @@ void tic_api_line(tic_mem* memory, float x0, float y0, float x1, float y1, u8 co
drawLine(memory, x0, y0, x1, y1, mapColor(memory, color));
}

void tic_api_paint(tic_mem* memory, s32 x, s32 y, u8 color)
{
floodFill((tic_core*)memory, x, y, mapColor(memory, color));
}

#if defined(BUILD_DEPRECATED)
#include "draw_dep.c"
#endif

0 comments on commit 9b7c0a6

Please sign in to comment.