mirror of
https://github.com/love2d/love.git
synced 2026-08-13 17:10:54 +02:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user