Merge pull request #482 from MihailRis/new-canvas-methods

New canvas methods
This commit is contained in:
MihailRis
2025-03-08 20:01:01 +03:00
committed by GitHub
14 changed files with 334 additions and 66 deletions
+11 -3
View File
@@ -166,13 +166,21 @@ Properties:
Methods: Methods:
Here, *color* can be specified in the following ways:
- rgba: int
- r: int, g: int, b: int
- r: int, g: int, b: int, a: int
| Method | Description | | Method | Description |
|----------------------------------------------------------|---------------------------------------------------------| |----------------------------------------------------------|---------------------------------------------------------|
| data:at(x: int, y: int) | returns an RGBA pixel at the given coordinates | | data:at(x: int, y: int) | returns an RGBA pixel at the given coordinates |
| data:set(x: int, y: int, rgba: int) | updates an RGBA pixel at the given coordinates | | data:set(x: int, y: int, *color*) | updates an RGBA pixel at the given coordinates |
| data:set(x: int, y: int, r: int, g: int, b: int) | updates an RGBA pixel at the given coordinates | | data:line(x1: int, y1: int, x2: int, y2: int, *color*) | draws a line with the specified RGBA color |
| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | updates an RGBA pixel at the given coordinates | | data:blit(src: Canvas, dst_x: int, dst_y: int) | draws the src canvas at the specified coordinates |
| data:clear() | clears the canvas |
| data:clear(*color*) | fills the canvas with the specified RGBA color |
| data:update() | applies changes to the canvas and uploads it to the GPU | | data:update() | applies changes to the canvas and uploads it to the GPU |
| data:set_data(data: table<int>) | replaces pixel data (width * height * 4 numbers) |
## Inventory ## Inventory
+15 -7
View File
@@ -167,13 +167,21 @@ document["worlds-panel"]:clear()
Методы: Методы:
| Метод | Описание | Здесь *цвет* может быть указан следующими способами:
|----------------------------------------------------------|-----------------------------------------------------| - rgba: int
| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам | - r: int, g: int, b: int
| data:set(x: int, y: int, rgba: int) | изменяет RGBA пиксель по указанным координатам | - r: int, g: int, b: int, a: int
| data:set(x: int, y: int, r: int, g: int, b: int) | изменяет RGBA пиксель по указанным координатам |
| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | изменяет RGBA пиксель по указанным координатам | | Метод | Описание |
| data:update() | применяет изменения и загружает холст в видеопамять | |----------------------------------------------------------|------------------------------------------------------|
| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам |
| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам |
| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом |
| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах |
| data:clear() | очищает холст |
| data:clear(*цвет*) | заполняет холст указанным RGBA цветом |
| data:update() | применяет изменения и загружает холст в видеопамять |
| data:set_data(data: table<int>) | заменяет данные пикселей (ширина * высота * 4 чисел) |
## Inventory (inventory) ## Inventory (inventory)
+28
View File
@@ -1,3 +1,31 @@
local _ffi = ffi
ffi = nil
-- Lua has no parallelizm, also _set_data does not call any lua functions so
-- may be reused one global ffi buffer per lua_State
local canvas_ffi_buffer
local canvas_ffi_buffer_size = 0
function __vc_Canvas_set_data(self, data)
if type(data) == "cdata" then
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
end
local width = self.width
local height = self.height
local size = width * height * 4
if size > canvas_ffi_buffer_size then
canvas_ffi_buffer = _ffi.new(
string.format("unsigned char[%s]", size)
)
canvas_ffi_buffer_size = size
end
for i=0, size - 1 do
canvas_ffi_buffer[i] = data[i + 1]
end
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
end
-- Check if given table is an array -- Check if given table is an array
function is_array(x) function is_array(x)
if #x > 0 then if #x > 0 then
+1 -1
View File
@@ -97,7 +97,7 @@ std::unique_ptr<Atlas> AtlasBuilder::build(uint extrusion, bool prepare, uint ma
uint y = rect.y; uint y = rect.y;
uint w = rect.width; uint w = rect.width;
uint h = rect.height; uint h = rect.height;
canvas->blit(entry.image.get(), rect.x, rect.y); canvas->blit(*entry.image, rect.x, rect.y);
for (uint j = 0; j < extrusion; j++) { for (uint j = 0; j < extrusion; j++) {
canvas->extrude(x - j, y - j, w + j*2, h + j*2); canvas->extrude(x - j, y - j, w + j*2, h + j*2);
} }
+112 -14
View File
@@ -3,6 +3,7 @@
#include <assert.h> #include <assert.h>
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
#include <cmath>
#include <algorithm> #include <algorithm>
ImageData::ImageData(ImageFormat format, uint width, uint height) ImageData::ImageData(ImageFormat format, uint width, uint height)
@@ -80,23 +81,116 @@ void ImageData::flipY() {
} }
} }
void ImageData::blit(const ImageData* image, int x, int y) { void ImageData::blit(const ImageData& image, int x, int y) {
if (format == image->format) { if (format == image.format) {
blitMatchingFormat(image, x, y); blitMatchingFormat(image, x, y);
return; return;
} }
if (format == ImageFormat::rgba8888 && if (format == ImageFormat::rgba8888 &&
image->format == ImageFormat::rgb888) { image.format == ImageFormat::rgb888) {
blitRGB_on_RGBA(image, x, y); blitRGB_on_RGBA(image, x, y);
return; return;
} }
throw std::runtime_error("mismatching format"); throw std::runtime_error("mismatching format");
} }
void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) { static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) {
ubyte* source = image->getData(); const int left = 0;
uint srcwidth = image->getWidth(); const int right = width;
uint srcheight = image->getHeight(); const int bottom = 0;
const int top = height;
int dx = x2 - x1;
int dy = y2 - y1;
float t0 = 0.0f;
float t1 = 1.0f;
auto clip = [](int p, int q, float& t0, float& t1) {
if (p == 0) {
return q >= 0;
}
float t = static_cast<float>(q) / p;
if (p < 0) {
if (t > t1) return false;
if (t > t0) t0 = t;
} else {
if (t < t0) return false;
if (t < t1) t1 = t;
}
return true;
};
if (!clip(-dx, x1 - left, t0, t1)) return false;
if (!clip( dx, right - x1, t0, t1)) return false;
if (!clip(-dy, y1 - bottom, t0, t1)) return false;
if (!clip( dy, top - y1, t0, t1)) return false;
if (t1 < 1.0f) {
x2 = x1 + static_cast<int>(std::round(t1 * dx));
y2 = y1 + static_cast<int>(std::round(t1 * dy));
}
if (t0 > 0.0f) {
x1 = x1 + static_cast<int>(std::round(t0 * dx));
y1 = y1 + static_cast<int>(std::round(t0 * dy));
}
return true;
}
template<uint channels>
static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const glm::ivec4& color) {
ubyte* data = image.getData();
uint width = image.getWidth();
uint height = image.getHeight();
if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width ||
y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) &&
!clip_line(x1, y1, x2, y2, width, height)) {
return;
}
int dx = std::abs(x2 - x1);
int dy = -std::abs(y2 - y1);
int sx = x1 < x2 ? 1 : -1;
int sy = y1 < y2 ? 1 : -1;
int err = dx + dy;
while (true) {
size_t pos = (y1 * width + x1) * channels;
for (int i = 0; i < channels; i++) {
data[pos + i] = color[i];
}
if (x1 == x2 && y1 == y2) break;
int e2 = 2 * err;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 <= dx) {
err += dx;
y1 += sy;
}
}
}
void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color) {
switch (format) {
case ImageFormat::rgb888:
draw_line<3>(*this, x1, y1, x2, y2, color);
break;
case ImageFormat::rgba8888:
draw_line<4>(*this, x1, y1, x2, y2, color);
break;
default:
break;
}
}
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
ubyte* source = image.getData();
uint srcwidth = image.getWidth();
uint srcheight = image.getHeight();
for (uint srcy = std::max(0, -y); for (uint srcy = std::max(0, -y);
srcy < std::min(srcheight, height - y); srcy < std::min(srcheight, height - y);
@@ -116,7 +210,7 @@ void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) {
} }
} }
void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) { void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) {
uint comps; uint comps;
switch (format) { switch (format) {
case ImageFormat::rgb888: comps = 3; break; case ImageFormat::rgb888: comps = 3; break;
@@ -124,20 +218,24 @@ void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) {
default: default:
throw std::runtime_error("only unsigned byte formats supported"); throw std::runtime_error("only unsigned byte formats supported");
} }
ubyte* source = image->getData(); ubyte* source = image.getData();
uint srcwidth = image->getWidth();
uint srcheight = image->getHeight(); const uint width = this->width;
const uint height = this->height;
const uint src_width = image.getWidth();
const uint src_height = image.getHeight();
ubyte* data = this->data.get();
for (uint srcy = std::max(0, -y); for (uint srcy = std::max(0, -y);
srcy < std::min(srcheight, height - y); srcy < std::min(src_height, height - y);
srcy++) { srcy++) {
for (uint srcx = std::max(0, -x); for (uint srcx = std::max(0, -x);
srcx < std::min(srcwidth, width - x); srcx < std::min(src_width, width - x);
srcx++) { srcx++) {
uint dstx = srcx + x; uint dstx = srcx + x;
uint dsty = srcy + y; uint dsty = srcy + y;
uint dstidx = (dsty * width + dstx) * comps; uint dstidx = (dsty * width + dstx) * comps;
uint srcidx = (srcy * srcwidth + srcx) * comps; uint srcidx = (srcy * src_width + srcx) * comps;
for (uint c = 0; c < comps; c++) { for (uint c = 0; c < comps; c++) {
data[dstidx + c] = source[srcidx + c]; data[dstidx + c] = source[srcidx + c];
} }
+11 -3
View File
@@ -2,6 +2,7 @@
#include "typedefs.hpp" #include "typedefs.hpp"
#include <glm/vec4.hpp>
#include <memory> #include <memory>
enum class ImageFormat { enum class ImageFormat {
@@ -14,6 +15,9 @@ class ImageData {
uint width; uint width;
uint height; uint height;
std::unique_ptr<ubyte[]> data; std::unique_ptr<ubyte[]> data;
void blitRGB_on_RGBA(const ImageData& image, int x, int y);
void blitMatchingFormat(const ImageData& image, int x, int y);
public: public:
ImageData(ImageFormat format, uint width, uint height); ImageData(ImageFormat format, uint width, uint height);
ImageData(ImageFormat format, uint width, uint height, std::unique_ptr<ubyte[]> data); ImageData(ImageFormat format, uint width, uint height, std::unique_ptr<ubyte[]> data);
@@ -23,9 +27,8 @@ public:
void flipX(); void flipX();
void flipY(); void flipY();
void blitRGB_on_RGBA(const ImageData* image, int x, int y); void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
void blitMatchingFormat(const ImageData* image, int x, int y); void blit(const ImageData& image, int x, int y);
void blit(const ImageData* image, int x, int y);
void extrude(int x, int y, int w, int h); void extrude(int x, int y, int w, int h);
void fixAlphaColor(); void fixAlphaColor();
@@ -44,6 +47,11 @@ public:
uint getHeight() const { uint getHeight() const {
return height; return height;
} }
size_t getDataSize() const {
size_t channels = 3 + (format == ImageFormat::rgba8888);
return width * height * channels;
}
}; };
std::unique_ptr<ImageData> add_atlas_margins(ImageData* image, int grid_size); std::unique_ptr<ImageData> add_atlas_margins(ImageData* image, int grid_size);
+1 -1
View File
@@ -16,5 +16,5 @@ void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) {
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(mTexture.get()); batch->texture(mTexture.get());
batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, true, col); batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, false, col);
} }
+1 -1
View File
@@ -26,4 +26,4 @@ namespace gui {
std::shared_ptr<::Texture> mTexture; std::shared_ptr<::Texture> mTexture;
std::shared_ptr<ImageData> mData; std::shared_ptr<ImageData> mData;
}; };
} }
+5 -1
View File
@@ -114,10 +114,14 @@ namespace lua {
return *mData; return *mData;
} }
[[nodiscard]] bool hasTexture() const {
return mTexture != nullptr;
}
static int createMetatable(lua::State*); static int createMetatable(lua::State*);
inline static std::string TYPENAME = "Canvas"; inline static std::string TYPENAME = "Canvas";
private: private:
std::shared_ptr<Texture> mTexture; std::shared_ptr<Texture> mTexture; // nullable
std::shared_ptr<ImageData> mData; std::shared_ptr<ImageData> mData;
}; };
static_assert(!std::is_abstract<LuaCanvas>()); static_assert(!std::is_abstract<LuaCanvas>());
+11 -8
View File
@@ -88,14 +88,17 @@ static void create_libs(State* L, StateType stateType) {
void lua::init_state(State* L, StateType stateType) { void lua::init_state(State* L, StateType stateType) {
// Allowed standard libraries // Allowed standard libraries
pop(L, luaopen_base(L)); luaL_openlibs(L);
pop(L, luaopen_math(L));
pop(L, luaopen_string(L)); if (getglobal(L, "require")) {
pop(L, luaopen_table(L)); pushstring(L, "ffi");
pop(L, luaopen_debug(L)); if (call_nothrow(L, 1, 1)) {
pop(L, luaopen_jit(L)); setglobal(L, "ffi");
pop(L, luaopen_bit(L)); }
pop(L, luaopen_os(L)); }
pushnil(L);
setglobal(L, "io");
const char* removed_os[] { const char* removed_os[] {
"execute", "exit", "remove", "rename", "setlocale", "tmpname", nullptr}; "execute", "exit", "remove", "rename", "setlocale", "tmpname", nullptr};
remove_lib_funcs(L, "os", removed_os); remove_lib_funcs(L, "os", removed_os);
+6 -2
View File
@@ -163,20 +163,23 @@ int lua::call(State* L, int argc, int nresults) {
int handler_pos = gettop(L) - argc; int handler_pos = gettop(L) - argc;
pushcfunction(L, l_error_handler); pushcfunction(L, l_error_handler);
insert(L, handler_pos); insert(L, handler_pos);
int top = gettop(L);
if (lua_pcall(L, argc, nresults, handler_pos)) { if (lua_pcall(L, argc, nresults, handler_pos)) {
std::string log = tostring(L, -1); std::string log = tostring(L, -1);
pop(L); pop(L);
remove(L, handler_pos); remove(L, handler_pos);
throw luaerror(log); throw luaerror(log);
} }
int added = gettop(L) - (top - argc - 1);
remove(L, handler_pos); remove(L, handler_pos);
return nresults == -1 ? 1 : nresults; return added;
} }
int lua::call_nothrow(State* L, int argc, int nresults) { int lua::call_nothrow(State* L, int argc, int nresults) {
int handler_pos = gettop(L) - argc; int handler_pos = gettop(L) - argc;
pushcfunction(L, l_error_handler); pushcfunction(L, l_error_handler);
insert(L, handler_pos); insert(L, handler_pos);
int top = gettop(L);
if (lua_pcall(L, argc, -1, handler_pos)) { if (lua_pcall(L, argc, -1, handler_pos)) {
auto errorstr = tostring(L, -1); auto errorstr = tostring(L, -1);
if (errorstr) { if (errorstr) {
@@ -188,8 +191,9 @@ int lua::call_nothrow(State* L, int argc, int nresults) {
remove(L, handler_pos); remove(L, handler_pos);
return 0; return 0;
} }
int added = gettop(L) - (top - argc - 1);
remove(L, handler_pos); remove(L, handler_pos);
return 1; return added;
} }
void lua::dump_stack(State* L) { void lua::dump_stack(State* L) {
+5 -1
View File
@@ -25,7 +25,8 @@ namespace lua {
if (n < 0) { if (n < 0) {
abort(); abort();
} }
if (lua_gettop(L) < n) { int top = lua_gettop(L);
if (top < n) {
abort(); abort();
} }
#endif #endif
@@ -63,6 +64,9 @@ namespace lua {
inline void rawseti(lua::State* L, int n, int idx = -2) { inline void rawseti(lua::State* L, int n, int idx = -2) {
lua_rawseti(L, idx, n); lua_rawseti(L, idx, n);
} }
inline void rawset(lua::State* L, int idx = -3) {
lua_rawset(L, idx);
}
inline int createtable(lua::State* L, int narr, int nrec) { inline int createtable(lua::State* L, int narr, int nrec) {
lua_createtable(L, narr, nrec); lua_createtable(L, narr, nrec);
@@ -17,6 +17,7 @@ union RGBA {
struct { struct {
uint8_t r, g, b, a; uint8_t r, g, b, a;
}; };
uint8_t arr[4];
uint32_t rgba; uint32_t rgba;
}; };
@@ -49,38 +50,118 @@ static int l_at(State* L) {
return 0; return 0;
} }
static RGBA get_rgba(State* L, int first) {
RGBA rgba {};
rgba.a = 255;
switch (gettop(L) - first) {
case 0:
rgba.rgba = static_cast<uint>(tointeger(L, first));
break;
case 3:
rgba.a = static_cast<ubyte>(tointeger(L, first + 3));
[[fallthrough]];
case 2:
rgba.r = static_cast<ubyte>(tointeger(L, first));
rgba.g = static_cast<ubyte>(tointeger(L, first + 1));
rgba.b = static_cast<ubyte>(tointeger(L, first + 2));
break;
}
return rgba;
}
static int l_set(State* L) { static int l_set(State* L) {
auto x = static_cast<uint>(tointeger(L, 2)); auto x = static_cast<uint>(tointeger(L, 2));
auto y = static_cast<uint>(tointeger(L, 3)); auto y = static_cast<uint>(tointeger(L, 3));
if (auto pixel = get_at(L, x, y)) { if (auto pixel = get_at(L, x, y)) {
switch (gettop(L)) { *pixel = get_rgba(L, 4);
case 4:
pixel->rgba = static_cast<uint>(tointeger(L, 4));
return 1;
case 6:
pixel->r = static_cast<ubyte>(tointeger(L, 4));
pixel->g = static_cast<ubyte>(tointeger(L, 5));
pixel->b = static_cast<ubyte>(tointeger(L, 6));
pixel->a = 255;
return 1;
case 7:
pixel->r = static_cast<ubyte>(tointeger(L, 4));
pixel->g = static_cast<ubyte>(tointeger(L, 5));
pixel->b = static_cast<ubyte>(tointeger(L, 6));
pixel->a = static_cast<ubyte>(tointeger(L, 7));
return 1;
default:
return 0;
}
} }
return 0;
}
static LuaCanvas& require_canvas(State* L, int idx) {
if (const auto canvas = touserdata<LuaCanvas>(L, idx)) {
return *canvas;
}
throw std::runtime_error(
"canvas expected as argument #" + std::to_string(idx)
);
}
static int l_clear(State* L) {
auto& canvas = require_canvas(L, 1);
auto& image = canvas.data();
ubyte* data = image.getData();
RGBA rgba {};
if (gettop(L) == 1) {
std::fill(data, data + image.getDataSize(), 0);
return 0;
}
rgba = get_rgba(L, 2);
size_t pixels = image.getWidth() * image.getHeight();
const size_t channels = 4;
for (size_t i = 0; i < pixels * channels; i++) {
data[i] = rgba.arr[i % channels];
}
return 0;
}
static int l_line(State* L) {
int x1 = tointeger(L, 2);
int y1 = tointeger(L, 3);
int x2 = tointeger(L, 4);
int y2 = tointeger(L, 5);
RGBA rgba = get_rgba(L, 6);
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
auto& image = canvas->data();
image.drawLine(
x1, y1, x2, y2, glm::ivec4 {rgba.r, rgba.g, rgba.b, rgba.a}
);
}
return 0;
}
static int l_blit(State* L) {
auto& dst = require_canvas(L, 1);
auto& src = require_canvas(L, 2);
int dst_x = tointeger(L, 3);
int dst_y = tointeger(L, 4);
dst.data().blit(src.data(), dst_x, dst_y);
return 0;
}
static int l_set_data(State* L) {
auto& canvas = require_canvas(L, 1);
auto& image = canvas.data();
auto data = image.getData();
if (lua::isstring(L, 2)) {
auto ptr = reinterpret_cast<ubyte*>(std::stoull(lua::tostring(L, 2)));
std::memcpy(data, ptr, image.getDataSize());
return 0;
}
int len = objlen(L, 2);
if (len < image.getDataSize()) {
throw std::runtime_error(
"data size mismatch expected " +
std::to_string(image.getDataSize()) + ", got " + std::to_string(len)
);
}
for (size_t i = 0; i < len; i++) {
rawgeti(L, i + 1, 2);
data[i] = tointeger(L, -1);
pop(L);
}
return 0; return 0;
} }
static int l_update(State* L) { static int l_update(State* L) {
if (auto canvas = touserdata<LuaCanvas>(L, 1)) { if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
canvas->texture().reload(canvas->data()); if (canvas->hasTexture()) {
canvas->texture().reload(canvas->data());
}
} }
return 0; return 0;
} }
@@ -88,7 +169,11 @@ static int l_update(State* L) {
static std::unordered_map<std::string, lua_CFunction> methods { static std::unordered_map<std::string, lua_CFunction> methods {
{"at", lua::wrap<l_at>}, {"at", lua::wrap<l_at>},
{"set", lua::wrap<l_set>}, {"set", lua::wrap<l_set>},
{"update", lua::wrap<l_update>} {"line", lua::wrap<l_line>},
{"blit", lua::wrap<l_blit>},
{"clear", lua::wrap<l_clear>},
{"update", lua::wrap<l_update>},
{"_set_data", lua::wrap<l_set_data>},
}; };
static int l_meta_index(State* L) { static int l_meta_index(State* L) {
@@ -110,6 +195,9 @@ static int l_meta_index(State* L) {
if (!strcmp(name, "height")) { if (!strcmp(name, "height")) {
return pushinteger(L, data.getHeight()); return pushinteger(L, data.getHeight());
} }
if (!strcmp(name, "set_data")) {
return getglobal(L, "__vc_Canvas_set_data");
}
if (auto func = methods.find(tostring(L, 2)); func != methods.end()) { if (auto func = methods.find(tostring(L, 2)); func != methods.end()) {
return pushcfunction(L, func->second); return pushcfunction(L, func->second);
} }
@@ -126,18 +214,33 @@ static int l_meta_newindex(State* L) {
if (isnumber(L, 2) && isnumber(L, 3)) { if (isnumber(L, 2) && isnumber(L, 3)) {
if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) { if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) {
pixel->rgba = static_cast<uint>(tointeger(L, 3)); pixel->rgba = static_cast<uint>(tointeger(L, 3));
return 1;
} }
return 1;
} }
return 0; return 0;
} }
static int l_meta_meta_call(lua::State* L) {
auto size = glm::ivec2(tovec2(L, 2));
if (size.x <= 0 || size.y <= 0) {
throw std::runtime_error("size must be positive");
}
return newuserdata<LuaCanvas>(
L,
nullptr,
std::make_shared<ImageData>(ImageFormat::rgba8888, size.x, size.y)
);
}
int LuaCanvas::createMetatable(State* L) { int LuaCanvas::createMetatable(State* L) {
createtable(L, 0, 3); createtable(L, 0, 3);
pushcfunction(L, lua::wrap<l_meta_index>); pushcfunction(L, lua::wrap<l_meta_index>);
setfield(L, "__index"); setfield(L, "__index");
pushcfunction(L, lua::wrap<l_meta_newindex>); pushcfunction(L, lua::wrap<l_meta_newindex>);
setfield(L, "__newindex"); setfield(L, "__newindex");
createtable(L, 0, 1);
pushcfunction(L, lua::wrap<l_meta_meta_call>);
setfield(L, "__call");
setmetatable(L);
return 1; return 1;
} }
+1 -1
View File
@@ -120,7 +120,7 @@ std::unique_ptr<Process> scripting::start_coroutine(
lua::loadbuffer(L, 0, source, script.name()); lua::loadbuffer(L, 0, source, script.name());
if (lua::call(L, 1)) { if (lua::call(L, 1)) {
int id = lua::tointeger(L, -1); int id = lua::tointeger(L, -1);
lua::pop(L, 2); lua::pop(L, 1);
return std::make_unique<LuaCoroutine>(L, id); return std::make_unique<LuaCoroutine>(L, id);
} }
lua::pop(L); lua::pop(L);