img.c
#include <math.h>
#include "inc.h"
lil_Image** lil_getImagePointer(lua_State* L, const int i){
lua_getfield(L, i, "ud");
lil_Image** imgp = ((lil_Image**)luaL_checkudata(L, -1, LIL_IMG_UD_MT));
lua_pop(L, 1);
return imgp;
}
lil_Image* lil_getImage(lua_State* L, const int i){
return *lil_getImagePointer(L, i);
}
static lil_Image* lil_newImage_(lua_State* L, const int w, const int h){
assert(w && h);
#ifdef LIL_USE_LIMITS
if(((size_t)w * (size_t)h) > LIL_MAX_IMAGE)
return NULL;
#endif
lil_Image* img = malloc(sizeof(lil_Image));
img->w = w;
img->h = h;
return img;
}
lil_Image* lil_newImageCalloc(lua_State* L, const int w, const int h){
lil_Image* img = lil_newImage_(L, w, h);
if(!img) return NULL;
img->d = calloc(w * h, sizeof(lil_Colour));
if(!img->d){
free(img);
return NULL;
}
return img;
}
lil_Image* lil_newImage(lua_State* L, const int w, const int h){
lil_Image* img = lil_newImage_(L, w, h);
if(!img) return NULL;
img->d = malloc(w * h * sizeof(lil_Colour));
if(!img->d){
free(img);
return NULL;
}
return img;
}
lil_Image* lil_cloneImage(lua_State* L, const lil_Image* img){
lil_Image* newImg = lil_newImage(L, img->w, img->h);
if(!newImg) return NULL;
memcpy(newImg->dptr, img->dptr, img->w * img->h * sizeof(img->d[0]));
return newImg;
}
void lil_freeImage(const lil_Image* img){
free(img->d);
free((void*)img);
}
void lil_pushImage(lua_State* L, const lil_Image* img){
lua_newtable(L);
*((lil_Image**)lua_newuserdata(L, sizeof(lil_Image*))) = (lil_Image*)img;
luaL_getmetatable(L, LIL_IMG_UD_MT);
lua_setmetatable(L, -2);
lua_setfield(L, -2, "ud");
lua_pushinteger(L, img->w);
lua_setfield(L, -2, "w");
lua_pushinteger(L, img->h);
lua_setfield(L, -2, "h");
luaL_getmetatable(L, LIL_IMG_MT);
lua_setmetatable(L, -2);
}
void lil_setImageSize(lua_State* L, lil_Image* img){
lua_pushinteger(L, img->w);
lua_setfield(L, -2, "w");
lua_pushinteger(L, img->h);
lua_setfield(L, -2, "h");
}
void lil_fillImage(const lil_Image* img, const lil_Colour col){
if((col.r == col.g && col.g == col.b && col.b == col.a) || !col.a){
memset(img->dptr, col.a, img->w * img->h * sizeof(lil_Colour));
} else {
for(int o = 0; o < img->w; o++)
img->d[o] = col;
for(int o = 1; o < img->h; o++)
memcpy(&img->d[XY(img, 0, o)], &img->d[0], img->w * sizeof(lil_Colour));
}
}
static LUAFUNC(img_resize){
lil_Image** imgp = lil_getImagePointer(L, 1);
const lil_Image* img = *imgp;
const int w = luaL_checkinteger(L, 2);
const int h = luaL_optinteger(L, 3, w);
luaL_argcheck(L, w > 0, 2, "Invalid width");
luaL_argcheck(L, h > 0, 3, "Invalid height");
if(w == img->w && h == img->h){
lua_pushvalue(L, 1);
return 1;
}
lil_Image* imgNew = lil_newImage(L, w, h);
if(!imgNew)
return 0;
lil_Number scaleX = (lil_Number)img->w / (lil_Number)w;
lil_Number scaleY = (lil_Number)img->h / (lil_Number)h;
enum lil_SampleMode sm = lil_getOptSampleMode(L, 4, scaleX, scaleY);
#pragma omp parallel for
for(int y = 0; y < h; y++)
for(int x = 0; x < w; x++)
imgNew->d[(y * w) + x] = lil_sampleImage(sm, img, (lil_Number)x * scaleX, (lil_Number)y * scaleY, scaleX, scaleY);
*imgp = imgNew;
lil_freeImage(img);
lua_pushvalue(L, 1);
lil_setImageSize(L, imgNew);
return 1;
}
static LUAFUNC(img_crop){
lil_Image** imgp = lil_getImagePointer(L, 1);
const lil_Image* img = *imgp;
const int x = luaL_checkinteger(L, 2);
const int y = luaL_checkinteger(L, 3);
const int w = luaL_checkinteger(L, 4);
const int h = luaL_checkinteger(L, 5);
luaL_argcheck(L, x >= 0, 2, "");
luaL_argcheck(L, y >= 0, 3, "");
luaL_argcheck(L, w > 0 && x + w <= img->w, 4, "");
luaL_argcheck(L, h > 0 && y + h <= img->h, 5, "");
lil_Image* imgNew = lil_newImage(L, w, h);
if(!imgNew)
return 0;
for(int py = 0; py < h; py++){
const size_t os = XY(img, x, y + py);
const size_t od = XY(imgNew, 0, py);
memcpy(&imgNew->d[od], &img->d[os], w * 4);
}
*imgp = imgNew;
lil_freeImage(img);
lua_pushvalue(L, 1);
lil_setImageSize(L, imgNew);
return 1;
}
static LUAFUNC(img_getPixel){
const lil_Image* img = lil_getImage(L, 1);
const lil_Number x = luaL_checknumber(L, 2);
const lil_Number y = luaL_checknumber(L, 3);
if(x >= img->w || y >= img->h || x < 0 || y < 0)
return 0;
const enum lil_SampleMode sm = lil_getOptSampleMode(L, 6, 1, 1);
const lil_Colour col = lil_sampleImage(sm, img, x, y, 1, 1);
lua_pushnumber(L, (lil_Number)col.r / (lil_Number)0xff);
lua_pushnumber(L, (lil_Number)col.g / (lil_Number)0xff);
lua_pushnumber(L, (lil_Number)col.b / (lil_Number)0xff);
lua_pushnumber(L, (lil_Number)col.a / (lil_Number)0xff);
return 4;
}
static LUAFUNC(img_setPixel){
const lil_Image* img = lil_getImage(L, 1);
const lil_Number r = luaL_checknumber(L, 2);
const lil_Number g = luaL_checknumber(L, 3);
const lil_Number b = luaL_checknumber(L, 4);
const lil_Number a = luaL_checknumber(L, 5);
const int x = luaL_checkinteger(L, 6);
const int y = luaL_checkinteger(L, 7);
if(x >= img->w || y >= img->h || x < 0 || y < 0)
return 0;
const lil_Colour col = {{ r * 0xff, g * 0xff, b * 0xff, a * 0xff }};
img->d[XY(img, x, y)] = col;
return 4;
}
static LUAFUNC(img_clone){
const lil_Image* img = lil_getImage(L, 1);
const lil_Image* imgNew = lil_cloneImage(L, img);
if(!imgNew)
return 0;
lil_pushImage(L, imgNew);
return 1;
}
static LUAFUNC(img_comp){
const lil_Image* bot = lil_getImage(L, 1);
const lil_Image* top = lil_getImage(L, 2);
int bx = luaL_optnumber(L, 3, 0);
int by = luaL_optnumber(L, 4, 0);
int tx = 0;
int ty = 0;
int w = top->w;
int h = top->h;
lil_getOptRect(L, 5, "crop", &tx, &ty, &w, &h);
lua_pushvalue(L, 1);
if(lil_constrainRectImg(L, bot, &bx, &by, top, &tx, &ty, &w, &h))
return 1;
const enum lil_BlendMode comp = lil_getOptBlendMode(L, 5);
#pragma omp parallel for
for(int y = 0; y < h; y++){
for(int x = 0; x < w; x++){
assert(x >= 0 && x + bx < bot->w && y >= 0 && y + by < bot->h);
assert(x >= 0 && x + tx < top->w && y >= 0 && y + ty < top->h);
const size_t b = XY(bot, x + bx, y + by);
const size_t t = XY(top, x + tx, y + ty);
lil_composite(comp, &bot->d[b], &top->d[t]);
}
}
return 1;
}
static LUAFUNC(img_rotate){
lil_Image** imgp = lil_getImagePointer(L, 1);
const lil_Image* img = *imgp;
const lil_Number rad = degToRad(luaL_checknumber(L, 2));
const lil_Number s = sin(rad);
const lil_Number c = cos(rad);
const lil_Number px = ((lil_Number)img->w)/2.;
const lil_Number py = ((lil_Number)img->h)/2.;
lil_Image* newImg = lil_newImage(L, img->w, img->h);
if(!newImg)
return 0;
#pragma omp parallel for
for(int y = 0; y < img->h; y++){
for(int x = 0; x < img->w; x++){
float srcX = ((x-px) * c + (y-py) * s) + px;
float srcY = ((y-py) * c - (x-px) * s) + py;
if(srcX >= img->w || srcX < 0 || srcY >= img->h || srcY < 0){
newImg->d[XY(img, x, y)].c = 0;
continue;
}
newImg->d[XY(img, x, y)] = lil_sampleImage(LIL_SMBILINEAR, img, srcX, srcY, 1, 1);
}
}
*imgp = newImg;
lil_freeImage(img);
lua_pushvalue(L, 1);
return 1;
}
static LUAFUNC(img_tostring){
const lil_Image* img = lil_getImage(L, 1);
lua_getfield(L, 1, "path");
if(lua_type(L, -1) == LUA_TSTRING){
const char* path = lua_tostring(L, -1);
lua_pushfstring(L, "lil.Img (%dx%d [%s] %p)", img->w, img->h, path, img);
} else {
lua_pushfstring(L, "lil.Img (%dx%d %p)", img->w, img->h, img);
}
return 1;
}
static LUAFUNC(img_eq){
const lil_Image* img1 = lil_getImage(L, 1);
const lil_Image* img2 = lil_getImage(L, 2);
if(img1->w != img2->w || img1->h != img2->h){
lua_pushboolean(L, false);
return 1;
}
volatile bool flag = true;
#pragma omp parallel for
for(size_t o = 0; o < img1->w * img1->h; o++){
const lil_Colour c1 = img1->d[o];
const lil_Colour c2 = img2->d[o];
if(c1.rgb * c1.a != c2.rgb * c2.a)
flag = false;
}
lua_pushboolean(L, flag);
return 1;
}
static LUAFUNC(img_isOpaque){
const lil_Image* img = lil_getImage(L, 1);
volatile bool is = true;
#pragma omp parallel for
#if INTPTR_MAX == INT64_MAX
for(size_t o = 0; o < (img->w * img->h) / 2; o++)
if((((uint64_t*)img->d)[o] & 0xff000000ff000000) != 0xff000000ff000000)
is = false;
if(img->w & img->h & 1)
if((img->d[(img->w * img->h) - 1].c & 0xff000000) != 0xff000000)
is = false;
#else
for(size_t o = 0; o < img->w * img->h; o++)
if((img->d[o].c & 0xff000000) != 0xff000000)
is = false;
#endif
lua_pushboolean(L, is);
return 1;
}
LUAREG(imgLib) = {
{ "setPixel", LUAFUNCD(img_setPixel) },
{ "getPixel", LUAFUNCD(img_getPixel) },
{ "resize", LUAFUNCD(img_resize) },
{ "crop", LUAFUNCD(img_crop) },
{ "clone", LUAFUNCD(img_clone) },
{ "comp", LUAFUNCD(img_comp) },
{ "rotate", LUAFUNCD(img_rotate) },
{ "__tostring", LUAFUNCD(img_tostring) },
{ "__eq", LUAFUNCD(img_eq) },
{ "isOpaque", LUAFUNCD(img_isOpaque) },
{ NULL, NULL }
};
static LUAFUNC(img_ud_gc){
lil_freeImage(*((lil_Image**)luaL_checkudata(L, 1, LIL_IMG_UD_MT)));
return 0;
}
LUAREG(imgUdLib) = {
{ "__gc", LUAFUNCD(img_ud_gc) },
{ NULL, NULL }
};