Description
If the structures that you want to manipulate from Lua need to be
allocated or created by C/C++ code,
then it's best to just store a pointer to the structure inside the
userdata.
For example, when creating images with
Thomas Boutell's GD Graphics Library[1],
the function gdImageCreate only returns a pointer to an
image object.
Image.new is a constructor that returns a userdata
containing
a pointer the image to be manipulated.
The userdata's metatable has a garbage collection event
to destroy the image, and an index event for calling the image
methods.
Only three methods are implemented:
-
colorallocate takes three numbers for RGB, and returns
a color index.
-
line draw a line between two points.
-
PNG draws the image and saves it to a file.
The metatable for the userdata is put in the registry,
and the __index field points to the table of methods
so that the object:method() syntax will work.
The methods table is stored in the table of globals
so that scripts can add methods written in Lua.
The Lua functions that manipulate the Image will need to either
access a userdata on the stack,
or push a new userdata onto the stack.
checkImage ensures that a userdata on the stack is the
correct type,
and returns the Image pointer inside the userdata.
pushImage leaves a new userdata on top of the stack, sets
its metatable,
and sets the Image pointer inside the userdata.
C code
-
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "gd.h"
/*
==============================================================================
Example Lua bindings for GD
==============================================================================
*/
#define IMAGE "Image"
typedef gdImagePtr Image;
static Image toImage (lua_State *L, int index)
{
Image *pi = (Image *)lua_touserdata(L, index);
if (pi == NULL) luaL_typerror(L, index, IMAGE);
return *pi;
}
static Image checkImage (lua_State *L, int index)
{
Image *pi, im;
luaL_checktype(L, index, LUA_TUSERDATA);
pi = (Image*)luaL_checkudata(L, index, IMAGE);
if (pi == NULL) luaL_typerror(L, index, IMAGE);
im = *pi;
if (!im)
luaL_error(L, "null Image");
return im;
}
static Image *pushImage (lua_State *L, Image im)
{
Image *pi = (Image *)lua_newuserdata(L, sizeof(Image));
*pi = im;
luaL_getmetatable(L, IMAGE);
lua_setmetatable(L, -2);
return pi;
}
static int Image_new (lua_State *L)
{
int x = luaL_checkint(L, 1);
int y = luaL_checkint(L, 2);
pushImage(L, gdImageCreate(x, y));
return 1;
}
static int Image_color_allocate (lua_State *L)
{
Image im = checkImage(L, 1);
int r = luaL_checkint(L, 2);
int g = luaL_checkint(L, 3);
int b = luaL_checkint(L, 4);
lua_pushnumber(L, gdImageColorAllocate(im, r, g, b));
return 1;
}
static int Image_line (lua_State *L)
{
Image im = checkImage(L, 1);
int x1 = luaL_checkint(L, 2);
int y1 = luaL_checkint(L, 3);
int x2 = luaL_checkint(L, 4);
int y2 = luaL_checkint(L, 5);
int colour = luaL_checkint(L, 6);
gdImageLine(im, x1, y1, x2, y2, colour);
return 0;
}
static int Image_png (lua_State *L)
{
/* Output the image to the disk file in PNG format. */
Image im = checkImage(L, 1);
const char *name = luaL_checkstring(L, 2);
FILE *pngout = fopen( name, "wb");
gdImagePng(im, pngout);
fclose(pngout);
return 0;
}
static const luaL_reg Image_methods[] = {
{"new", Image_new},
{"colorallocate", Image_color_allocate},
{"line", Image_line},
{"PNG", Image_png},
{0,0}
};
static int Image_gc (lua_State *L)
{
Image im = toImage(L, 1);
if (im) gdImageDestroy(im);
printf("goodbye Image (%p)\n", lua_touserdata(L, 1));
return 0;
}
static int Image_tostring (lua_State *L)
{
lua_pushfstring(L, "Image: %p", lua_touserdata(L, 1));
return 1;
}
static const luaL_reg Image_meta[] = {
{"__gc", Image_gc},
{"__tostring", Image_tostring},
{0, 0}
};
int Image_register (lua_State *L)
{
luaL_openlib(L, IMAGE, Image_methods, 0); /* create methods table,
add it to the globals */
luaL_newmetatable(L, IMAGE); /* create metatable for Image,
add it to the Lua registry */
luaL_openlib(L, 0, Image_meta, 0); /* fill metatable */
lua_pushliteral(L, "__index");
lua_pushvalue(L, -3); /* dup methods table*/
lua_rawset(L, -3); /* metatable.__index = methods */
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, -3); /* dup methods table*/
lua_rawset(L, -3); /* hide metatable:
metatable.__metatable = methods */
lua_pop(L, 1); /* drop metatable */
return 1; /* return methods on the stack */
}
int main(int argc, char *argv[])
{
lua_State *L = lua_open();
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
luaopen_debug(L);
Image_register(L);
if(argc>1) lua_dofile(L, argv[1]);
lua_close(L);
return 0;
}
Compiling the Code
This code can be compiled for Lua 5.0 as follows:
-
gcc gd.c -L/usr/local/lib/ -llua -llualib -lgd -lpng
Lua Test Code
-
for n,v in pairs(Image) do print(n,v) end
size = 256
im = Image.new(size, size)
print(im)
white = im:colorallocate(255, 255, 255)
for i = 0,size-1,1 do
c = im:colorallocate(0, i, i)
im:line(0, 0, size-1 , i, c)
end
im:PNG'test.png'
Test Code Output
-
$ ./a gd.lua
line function: 0x10054ff0
PNG function: 0x100553b8
colorallocate function: 0x100552f8
new function: 0x10054fb8
Image (0x10055ef0)
goodbye Image (0x10055ef0)
This is the image created by the test code.
Related Pages
|