Implemented efficient FFI versions of ImageData methods.

Note that ImageData:mapPixel (with its optional sub-rectangle parameters) is often many times faster than a loop of ImageData:setPixel and/or getPixel, for anything more than a few pixels.
This commit is contained in:
Alex Szpakowski
2015-06-15 00:50:01 -03:00
parent 409d313bff
commit df1bd42dec
5 changed files with 289 additions and 43 deletions
+80 -36
View File
@@ -23,11 +23,21 @@
#include "common/wrap_Data.h"
#include "filesystem/File.h"
// Shove the wrap_ImageData.lua code directly into a raw string literal.
static const char imagedata_lua[] =
#include "wrap_ImageData.lua"
;
namespace love
{
namespace image
{
/**
* NOTE: Additional wrapper code is in wrap_ImageData.lua. Be sure to keep it
* in sync with any changes made to this file!
**/
ImageData *luax_checkimagedata(lua_State *L, int idx)
{
return luax_checktype<ImageData>(L, idx, IMAGE_IMAGE_DATA_ID);
@@ -130,8 +140,9 @@ static int luax_retnumbererror(lua_State *L, int level, int retnum, int ttype)
where, retnum, ttypename);
}
// ImageData:mapPixel. Not thread-safe! See the wrapper function below.
static int w_ImageData_mapPixelUnsafe(lua_State *L)
// ImageData:mapPixel. Not thread-safe! See wrap_ImageData.lua for the thread-
// safe wrapper function.
int w_ImageData__mapPixelUnsafe(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
@@ -186,38 +197,6 @@ static int w_ImageData_mapPixelUnsafe(lua_State *L)
t->setPixelUnsafe(x, y, c);
}
}
return 0;
}
// Thread-safe wrapper for the above function.
int w_ImageData_mapPixel(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
int sx = luaL_optint(L, 3, 0);
int sy = luaL_optint(L, 4, 0);
int w = luaL_optint(L, 5, t->getWidth());
int h = luaL_optint(L, 6, t->getHeight());
lua_pushcfunction(L, w_ImageData_mapPixelUnsafe);
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushinteger(L, sx);
lua_pushinteger(L, sy);
lua_pushinteger(L, w);
lua_pushinteger(L, h);
int ret = 0;
// Lock this ImageData's mutex during the entire mapPixel. We pcall instead
// of call because lua_error longjmp's without calling object destructors.
{
love::thread::Lock lock(t->getMutex());
ret = lua_pcall(L, 6, 0, 0);
}
if (ret != 0)
return lua_error(L);
return 0;
}
@@ -265,6 +244,50 @@ int w_ImageData_encode(lua_State *L)
return 0;
}
int w_ImageData__performAtomic(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
int err = 0;
{
love::thread::Lock lock(t->getMutex());
// call the function, passing any user-specified arguments.
err = lua_pcall(L, lua_gettop(L) - 2, LUA_MULTRET, 0);
}
// Unfortunately, this eats the stack trace, too bad.
if (err != 0)
return lua_error(L);
// The function and everything after it in the stack are eaten by the pcall,
// leaving only the ImageData object. Everything else is a return value.
return lua_gettop(L) - 1;
}
// C functions in a struct, necessary for the FFI versions of ImageData methods.
struct FFI_ImageData
{
void (*lockMutex)(Proxy *p);
void (*unlockMutex)(Proxy *p);
};
static FFI_ImageData ffifuncs =
{
[](Proxy *p) -> void // lockMutex
{
// We don't do any type-checking for the Proxy here since these functions
// are always called from code which has already done type checking.
ImageData *i = (ImageData *) p->object;
i->getMutex()->lock();
},
[](Proxy *p) -> void // unlockMutex
{
ImageData *i = (ImageData *) p->object;
i->getMutex()->unlock();
}
};
static const luaL_Reg functions[] =
{
// Data
@@ -277,15 +300,36 @@ static const luaL_Reg functions[] =
{ "getDimensions", w_ImageData_getDimensions },
{ "getPixel", w_ImageData_getPixel },
{ "setPixel", w_ImageData_setPixel },
{ "mapPixel", w_ImageData_mapPixel },
{ "paste", w_ImageData_paste },
{ "encode", w_ImageData_encode },
// Used in the Lua wrapper code.
{ "_mapPixelUnsafe", w_ImageData__mapPixelUnsafe },
{ "_performAtomic", w_ImageData__performAtomic },
{ 0, 0 }
};
extern "C" int luaopen_imagedata(lua_State *L)
{
return luax_register_type(L, IMAGE_IMAGE_DATA_ID, functions);
// The last argument pushes the type's metatable onto the stack.
int ret = luax_register_type(L, IMAGE_IMAGE_DATA_ID, functions, true);
// Load and execute ImageData.lua, sending the metatable and the ffi
// functions struct pointer as arguments.
if (ret > 0)
{
luaL_loadbuffer(L, imagedata_lua, sizeof(imagedata_lua), "ImageData.lua");
lua_pushvalue(L, -2);
lua_pushlightuserdata(L, &ffifuncs);
lua_call(L, 2, 0);
// Pop the metatable.
lua_pop(L, 1);
ret--;
}
return ret;
}
} // image