imgDraw.c
#include <math.h>
#include <assert.h>
#include "inc.h"
#define ENTRY(id) static LUAFUNC(imgDraw_ ## id)
ENTRY(fill){
const lil_Image* img = lil_getImage(L, 1);
const lil_Colour col = lil_getColour(L, 2, NULL);
lil_fillImage(img, col);
lua_pushvalue(L, 1);
return 1;
}
ENTRY(rect){
const lil_Image* img = lil_getImage(L, 1);
const lil_Colour col = lil_getColour(L, 2, NULL);
int ox = lua_tointeger(L, 3);
int oy = lua_tointeger(L, 4);
int w = luaL_checkinteger(L, 5);
int h = luaL_checkinteger(L, 6);
int corn = clamp(lil_getOptInt(L, 7, "corners", 0), 0, min(w, h) / 2);
const enum lil_BlendMode comp = lil_getOptBlendMode(L, 7);
if(corn){
for(int i = 0; i < 3; i++){ int mx, my, mw, mh;
switch(i){
case 0: mx = ox + corn; my = oy; mw = w - corn * 2; mh = corn; break;
case 1: mx = ox; my = oy + corn; mw = w; mh = h - corn * 2; break;
case 2: mx = ox + corn; my = oy + h - corn; mw = w - corn * 2; mh = corn; break;
}
if(lil_constrainRect(L, img, &mx, &my, &mw, &mh))
continue;
for(int y = my; y < mh + my; y++){
for(int x = mx; x < mw + mx; x++){
assert(x >= 0 && x < img->w && y >= 0 && y < img->h);
size_t o = XY(img, x, y);
lil_composite(comp, &img->d[o], &col);
}
}
}
for(int px = -corn; px < 0; px++){ int height = sqrt(corn * corn - px * px);
for(int py = -height; py < 0; py++){
for(int i = 0; i < 4; i++){
int x, y;
switch(i){
case 0: x = px + ox + corn; y = py + oy + corn; break;
case 1: x = ox - px + w - corn - 1; y = py + oy + corn; break;
case 2: x = ox + px + corn; y = oy - py + h - corn - 1; break;
case 3: x = ox - px + w - corn - 1; y = oy - py + h - corn - 1; break;
}
if(x < 0 || x >= img->w || y < 0 || y >= img->h)
continue;
size_t o = XY(img, x, y);
lil_composite(comp, &img->d[o], &col);
}
}
}
} else {
if(lil_constrainRect(L, img, &ox, &oy, &w, &h))
return 1;
for(int y = oy; y < h + oy; y++){
for(int x = ox; x < w + ox; x++){
assert(x >= 0 && x < img->w && y >= 0 && y < img->h);
size_t o = XY(img, x, y);
lil_composite(comp, &img->d[o], &col);
}
}
}
lua_pushvalue(L, 1);
return 1;
}
ENTRY(circle){
const lil_Image* img = lil_getImage(L, 1);
const lil_Colour col = lil_getColour(L, 2, NULL);
const int ox = luaL_optinteger(L, 3, img->w / 2);
const int oy = luaL_optinteger(L, 4, img->h / 2);
const int r = luaL_optinteger(L, 5, img->w < img->h ? img->w / 2 : img->h / 2);
const enum lil_BlendMode bm = lil_getOptBlendMode(L, 7);
#pragma omp parallel for
for(int px = -r; px < r; px++){
float fheight = sqrt(r * r - px * px);
int height = fheight;
int x = px + ox;
if(x < 0 || x >= img->w)
continue;
for(int py = -height; py < height; py++){
int y = py + oy;
if(y < 0 || y >= img->h)
continue;
assert(x >= 0 && x < img->w && y >= 0 && y < img->h);
size_t o = XY(img, x, y);
lil_composite(bm, &img->d[o], &col);
}
}
lua_pushvalue(L, 1);
return 1;
}
#ifdef LIL_USE_FREETYPE
ENTRY(text){
const lil_Image* img = lil_getImage(L, 1);
const lil_Font* font = lil_getFont(L, 2);
const char* text = luaL_checkstring(L, 3);
size_t len;
wchar_t* wtext = lil_utf8ToWchar((const unsigned char*)text, strlen(text), &len);
const int ox = luaL_optinteger(L, 4, 0);
const int oy = luaL_optinteger(L, 5, 0);
const enum lil_BlendMode bm = lil_getOptBlendMode(L, 6);
lil_writeTextToImage(font, wtext, len, 0, 0, img, bm, ox, oy);
lua_pushvalue(L, 1);
free(wtext);
return 1;
}
#endif
static int polyEdgeSorter(const void* a, const void* b){
return *(lil_Number*)a - *(lil_Number*)b;
}
static void drawPolygon(lua_State* L, const lil_Image* img, lil_Colour col, lil_Mat points, enum lil_BlendMode bm){
#define X 0
#define Y 1
#define SS 8
int maxx = INT_MIN, maxy = INT_MIN, minx = INT_MAX, miny = INT_MAX;
for(int p = 0; p < points.h; p++){
lil_Number x = points.d[p*2+X], y = points.d[p*2+Y];
if(isnan(x) || isnan(y) || isinf(x) || isinf(y))
luaL_error(L, "Point has NAN or INF: {%f, %f}", x, y);
maxx = max(maxx, x);
maxy = max(maxy, y);
minx = min(minx, x);
miny = min(miny, y);
}
maxx = min(maxx + 1, img->w);
maxy = min(maxy + 1, img->h);
minx = max(minx, 0);
miny = max(miny, 0);
if(maxx < minx || maxy < miny || !(maxx | minx | maxy | miny))
return;
const size_t roww = maxx - minx;
#pragma omp parallel
{
lil_Number* edges = malloc(sizeof(*edges) * (points.h-1));
lil_Number* row = malloc(sizeof(*row) * roww);
lil_Colour lcol = col;
if(edges && row){
#pragma omp for
for(int py = miny; py < maxy; py++){
#ifdef __STDC_IEC_559__
memset(row, 0, sizeof(*row) * roww);
#else
for(int x = 0; x < roww; x++)
row[x] = 0;
#endif
for(int sy = 0; sy < SS; sy++){
const lil_Number y = py + (sy * (1./SS) * 0.5);
int ne = 0;
for(int p = 0; p < points.h-1; p++){
lil_Number* p1 = &points.d[p*2];
lil_Number* p2 = p1 + 2;
if((p1[Y] >= y && p2[Y] < y) || (p1[Y] < y && p2[Y] >= y))
edges[ne++] = p1[X] + (y - p1[Y]) / (p1[Y] - p2[Y]) * (p1[X] - p2[X]);
}
qsort(edges, ne, sizeof(*edges), polyEdgeSorter);
if(ne){
bool t = false;
int e = 0;
for(int x = minx; x < maxx; x++){
lil_Number f = 1;
while(x >= edges[e]){
t = !t;
f = 1 - (edges[e] - (int)edges[e]);
e++;
if(!t)
row[x - minx] += (1 - f);
if(e == ne)
goto nextScanLine;
}
if(t)
row[x - minx] += f;
}
}
nextScanLine: (void)0;
}
for(int x = 0; x < roww; x++){
if(row[x] > 0){
lcol.a = col.a * (min(row[x], SS) * (1./SS));
lil_composite(bm, &img->d[XY(img, x + minx, py)], &lcol);
}
}
}
}
free(edges);
free(row);
}
#undef SS
}
ENTRY(poly){
const lil_Image* img = lil_getImage(L, 1);
const lil_Colour col = lil_getColour(L, 2, NULL);
lil_Mat points = lil_mat_get(L, 3);
if(points.w != 2){
lil_mat_free(&points);
luaL_argerror(L, 3, "Invalid vertex list");
}
if(lil_mat_resize(&points, 2, points.h+1)){
lil_mat_free(&points);
return 0;
}
points.d[(points.h-1)*2+X] = points.d[X];
points.d[(points.h-1)*2+Y] = points.d[Y];
const enum lil_BlendMode bm = lil_getOptBlendMode(L, 4);
drawPolygon(L, img, col, points, bm);
lil_mat_free(&points);
lua_pushvalue(L, 1);
return 1;
}
ENTRY(line){
const lil_Image* img = lil_getImage(L, 1);
const lil_Colour col = lil_getColour(L, 2, NULL);
const lil_Number x1 = luaL_checknumber(L, 3);
const lil_Number y1 = luaL_checknumber(L, 4);
const lil_Number x2 = luaL_checknumber(L, 5);
const lil_Number y2 = luaL_checknumber(L, 6);
const lil_Number w = lil_getOptFloat(L, 7, "width", 1) / 2.;
const enum lil_BlendMode bm = lil_getOptBlendMode(L, 7);
lil_Mat points = { 2, 5 }; lil_mat_init(L, &points);
const lil_Number x = (x1 + x2) / 2.;
const lil_Number y = (y1 + y2) / 2.;
const lil_Number l = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) / 2.;
const lil_Number a = atan2(y1 - y2, x1 - x2);
const lil_Number s = sin(a);
const lil_Number c = cos(a);
points.d[0*2+X] = x + l * c - w * s;
points.d[0*2+Y] = y + w * c + l * s;
points.d[1*2+X] = x - l * c - w * s;
points.d[1*2+Y] = y + w * c - l * s;
points.d[2*2+X] = x - l * c + w * s;
points.d[2*2+Y] = y - w * c - l * s;
points.d[3*2+X] = x + l * c + w * s;
points.d[3*2+Y] = y - w * c + l * s;
points.d[4*2+X] = points.d[0*2+X];
points.d[4*2+Y] = points.d[0*2+Y];
drawPolygon(L, img, col, points, bm);
lil_mat_free(&points);
lua_pushvalue(L, 1);
return 1;
#undef X
#undef Y
}
#undef ENTRY
#define ENTRY(id) { #id, LUAFUNCD(imgDraw_ ## id) }
LUAREG(imgDrawLib) = {
ENTRY(fill),
ENTRY(rect),
ENTRY(circle),
ENTRY(text),
ENTRY(poly),
ENTRY(line),
{ NULL, NULL }
};
#undef ENTRY