指標範例之使用者資料

lua-users home
wiki

說明

如果使用者想要操作的結構必須由 C/C++ 程式碼來配置或建立,則最好在使用者資料中只儲存指向結構的指標。

例如,當使用 Thomas Boutell 的 GD 圖形函式庫[1]建立影像時,函式 gdImageCreate 只會傳回指向影像物件的指標。

Image.new 是傳回一個含有指向要操作的影像指標的使用者資料的建構函式。使用者資料的元表有一個垃圾回收事件來毀損影像,以及一個用來呼叫影像方法的索引事件。

僅實現三個方法

使用者資料的元表會放入登錄,而 __index 欄位會指向方法表格,因此 object:method() 語法才會運作。方法表格會儲存在全域變數表格中,因此腳本才能夠新增使用 Lua 所寫的方法。

用來操作影像的 Lua 函式需要存取堆疊中的使用者資料,或將新的使用者資料推入堆疊。

checkImage 可確保堆疊中的使用者資料是正確類型,並傳回使用者資料內的影像指標。

pushImage 會在堆疊頂端留下新的使用者資料,並設定其元表,以及設定使用者資料內的影像指標。

C 程式碼

#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;
}

編譯程式碼

這段程式碼可照以下方式編譯為 Lua 5.0

gcc gd.c  -L/usr/local/lib/ -llua -llualib -lgd -lpng

Lua 測試程式碼

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'

-- debug.debug()

測試程式碼輸出

$ ./a gd.lua
line    function: 0x10054ff0
PNG     function: 0x100553b8
colorallocate   function: 0x100552f8
new     function: 0x10054fb8
Image (0x10055ef0)
goodbye Image (0x10055ef0)

這是測試程式碼所建立的影像。

相關頁面


RecentChanges · 喜好設定
編輯 · 歷史記錄
上次編輯日期:2007 年 1 月 6 日下午 7:31 GMT (差異)