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); // TODO
	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){ // negative coords
		*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) // end coords outside bounds
		return 1;
	if(*bx + *w > bot->w) // shrink w/h to fit within image
		*w = bot->w - *bx;
	if(*by + *h > bot->h)
		*h = bot->h - *by;
	if(*tx + *w > tw) // other image too
		*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);
}
generated by LDoc 1.4.6 Last updated 2023-04-13 13:58:34