util.c
#include <math.h>
#include "util.h"
#ifndef NDEBUG
void lil_dumpstack(lua_State* L){
const int top = lua_gettop(L);
for(int i = 1; i <= top; i++){
printf("[%d %d] %s ", i, -(top - (i - 1)), luaL_typename(L,i));
switch(lua_type(L, i)){
case LUA_TNUMBER:
printf("%g\n", lua_tonumber(L,i));
break;
case LUA_TSTRING:
printf("%s\n", lua_tostring(L,i));
break;
case LUA_TBOOLEAN:
printf("%s\n", lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNIL:
printf("%s\n", "nil");
break;
default:
printf("%p\n", lua_topointer(L,i));
break;
}
}
puts("------");
}
#else
#define lil_dumpstack()
#endif
lil_Colour lil_getColour(lua_State* L, int index, lil_Colour* def){
lil_Colour col = {{0, 0, 0, 0}};
if(lua_isnoneornil(L, index)){
if(def)
return *def;
else
luaL_argerror(L, index, "Need a colour (float table 0.0-1.0): {r, g, b, a}");
} else {
if(lua_istable(L, index)){
for(int i = 0; i < 4; i++){
lua_rawgeti(L, index, i+1);
const int t = lua_type(L, -1);
if(t == LUA_TNUMBER){
col.arr[i] = clamp1(lua_tonumber(L, -1)) * 0xff;
} else {
if(i == 3 && t == LUA_TNIL){
col.a = 0xff;
} else {
luaL_argerror(L, index, "Invalid colour table");
}
}
}
lua_pop(L, 4);
} else {
static const char* const errStr = "Valid formats are: RGB RGBA RRGGBB RRGGBBAA, and can ";
size_t len;
const char* str = luaL_checklstring(L, index, &len);
if(!len)
luaL_argerror(L, index, errStr);
if(str[0] == '#'){
len--;
str++;
}
if(len != 3 && len != 4 && len != 6 && len != 8)
luaL_argerror(L, index, errStr);
char tm[3];
tm[1] = '\0';
tm[2] = '\0';
char* nptr;
int t;
col.a = 0xff;
#define XX(I) \
tm[0] = str[0+((I)*2)]; tm[1] = str[1+((I)*2)]; \
t = strtoul(tm, &nptr, 16); \
if(nptr != tm + 2) luaL_argerror(L, index, errStr); \
col.arr[I] = t;
#define X(I) \
tm[0] = str[I]; \
t = strtoul(tm, &nptr, 16); \
if(nptr != tm + 1) luaL_argerror(L, index, errStr); \
col.arr[I] = t * 0x11;
switch(len){
case 4:
X(3);
case 3:
X(0);
X(1);
X(2);
break;
case 8:
XX(3);
case 6:
XX(0);
XX(1);
XX(2);
break;
}
#undef XX
#undef X
}
}
return col;
};
uintmax_t lil_getStringFlags(lua_State* L, int index, const char* list, const char* def){
uintmax_t flags = 0;
const char* str;
if(lua_isnoneornil(L, index)){
if(def){
str = def;
goto parse;
}
} else {
if(lua_isstring(L, index)){
str = lua_tostring(L, index);
goto parse;
}
}
luaL_argerror(L, index, "Need string flags");
lua_error(L);
return 0;
parse:
for(int i = 0; str[i]; i++){
for(int f = 0; list[f]; f++){
if(str[i] == list[f])
flags |= 1 << f;
}
}
return flags;
}
enum lil_BlendMode lil_getOptBlendMode(lua_State* L, int i){
enum lil_BlendMode m = LIL_BMALPHA;
if(lua_type(L, i) == LUA_TTABLE){
lua_getfield(L, i, "blend");
m = luaL_checkoption(L, -1, "alpha", lil_compMethodList);
lua_pop(L, 1);
}
return m;
}
enum lil_SampleMode lil_getOptSampleMode(lua_State* L, int i, lil_Number xs, lil_Number ys){
int top = lua_gettop(L);
bool up = xs + ys < 2.0;
enum lil_SampleMode m = up ? LIL_SMBILINEAR : LIL_SMBOX;
if(lua_type(L, i) == LUA_TTABLE){
lua_getfield(L, i, "sample");
if(lua_type(L, -1) == LUA_TTABLE)
lua_rawgeti(L, -1, up ? 1 : 2);
m = luaL_checkoption(L, -1, "bilinear", lil_sampleMethodList);
}
lua_pop(L, lua_gettop(L) - top);
return m;
}
int lil_getOptEnum(lua_State* L, int i, const char* key, const char* const* list, const char* const def){
if(lua_type(L, i) != LUA_TTABLE){
for(int i = 0; list[i]; i++)
if(strcmp(list[i], def) == 0)
return i;
assert(false);
}
lua_getfield(L, i, key);
int e = luaL_checkoption(L, -1, def, list);
lua_pop(L, 1);
return e;
}
int lil_getOptInt(lua_State* L, int i, const char* key, int def){
if(lua_type(L, i) != LUA_TTABLE)
return def;
lua_getfield(L, i, key);
if(lua_type(L, -1) != LUA_TNUMBER){
lua_pop(L, 1);
return def;
}
int n = lua_tointeger(L, -1);
lua_pop(L, 1);
return n;
}
lil_Number lil_getOptFloat(lua_State* L, int i, const char* key, lil_Number def){
if(lua_type(L, i) != LUA_TTABLE)
return def;
lua_getfield(L, i, key);
if(lua_type(L, -1) != LUA_TNUMBER){
lua_pop(L, 1);
return def;
}
lil_Number n = lua_tonumber(L, -1);
lua_pop(L, 1);
return n;
}
void lil_getOpt2dVec(lua_State* L, int i, const char* key, int* r1, int* r2){
if(lua_type(L, i) != LUA_TTABLE)
return;
lua_getfield(L, i, key);
if(lua_type(L, -1) != LUA_TTABLE){
lua_pop(L, 1);
return;
}
lua_rawgeti(L, -1, 1);
*r1 = luaL_checkinteger(L, -1);
lua_rawgeti(L, -2, 2);
*r2 = luaL_checkinteger(L, -1);
lua_pop(L, 2);
}
bool lil_getOptBool(lua_State* L, int i, const char* key, int def){
if(lua_type(L, i) != LUA_TTABLE)
return def;
lua_getfield(L, i, key);
int n = lua_toboolean(L, -1);
lua_pop(L, 1);
return n;
}
void lil_getOptRect(lua_State* L, int i, const char* key, int* xr, int* yr, int* wr, int* hr){
if(lua_type(L, i) != LUA_TTABLE)
return;
lua_getfield(L, i, key);
if(lua_type(L, -1) != LUA_TTABLE){
lua_pop(L, 1);
return;
}
lua_rawgeti(L, -1, 1);
*xr = luaL_checkinteger(L, -1);
lua_rawgeti(L, -2, 2);
*yr = luaL_checkinteger(L, -1);
lua_rawgeti(L, -3, 3);
*wr = luaL_checkinteger(L, -1);
lua_rawgeti(L, -4, 4);
*hr = luaL_checkinteger(L, -1);
lua_pop(L, 5);
}
void lil_getExportOpts(lua_State* L, int i, lil_Number* qualityRet, lil_Number* speedRet){
lil_Number quality = LIL_IMG_COMPRESSION_QUALITY;
lil_Number speed = LIL_IMG_COMPRESSION_SPEED;
if(lua_type(L, i) != LUA_TTABLE)
goto end;
lua_getfield(L, i, "speed");
if(lua_type(L, -1) == LUA_TNUMBER)
speed = clamp1(lua_tonumber(L, -1));
lua_getfield(L, i, "quality");
if(lua_type(L, -1) == LUA_TNUMBER)
quality = clamp1(lua_tonumber(L, -1));
lua_pop(L, 2);
end:
if(qualityRet)
*qualityRet = quality;
if(speedRet)
*speedRet = speed;
}
void lil_getOptColour(lua_State* L, int i, const char* key, lil_Colour* c){
if(lua_type(L, i) != LUA_TTABLE)
return;
lua_getfield(L, i, key);
lil_Colour c2 = lil_getColour(L, -1, c); c->c = c2.c;
lua_pop(L, 1);
}
static int lil_constrainRect_(lua_State* L, const lil_Image* bot, int* bx, int* by, int* tx, int* ty, int tw, int th, int* w, int* h){
assert(tw > 0 && th > 0);
if(*bx < 0){ *w += *bx;
*tx -= *bx;
*bx = 0;
}
if(*by < 0){
*h += *by;
*ty -= *by;
*by = 0;
}
if(*tx < 0 || *ty < 0)
luaL_error(L, "tx or ty < 0");
if(*w == 0 || *h == 0)
return 1;
if(*w <= 0 || *h <= 0)
luaL_error(L, "w or h <= 0");
if(*bx >= bot->w || *by >= bot->h || *tx >= tw || *ty >= th) return 1;
if(*bx + *w > bot->w) *w = bot->w - *bx;
if(*by + *h > bot->h)
*h = bot->h - *by;
if(*tx + *w > tw) *w = tw - *tx;
if(*ty + *h > th)
*h = th - *ty;
assert(*tx >= 0 && *ty >= 0 && *bx >= 0 && *by >= 0);
assert(*w + *bx <= bot->w && *h + *by <= bot->h);
assert(*w + *tx <= tw && *h + *ty <= th);
assert(*w > 0 && *h > 0);
return 0;
}
int lil_constrainRectImg(lua_State* L, const lil_Image* bot, int* bx, int* by, const lil_Image* top, int* tx, int* ty, int* w, int* h){
return lil_constrainRect_(L, bot, bx, by, tx, ty, top->w, top->h, w, h);
}
int lil_constrainRect(lua_State* L, const lil_Image* bot, int* bx, int* by, int* w, int* h){
return lil_constrainRect_(L, bot, bx, by, bx, by, bot->w, bot->h, w, h);
}