diff --git a/src/graphics/core/Atlas.cpp b/src/graphics/core/Atlas.cpp index 039db4d2..be205d1e 100644 --- a/src/graphics/core/Atlas.cpp +++ b/src/graphics/core/Atlas.cpp @@ -97,7 +97,7 @@ std::unique_ptr AtlasBuilder::build(uint extrusion, bool prepare, uint ma uint y = rect.y; uint w = rect.width; 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++) { canvas->extrude(x - j, y - j, w + j*2, h + j*2); } diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index b5e05d1c..a2b1d524 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -81,13 +81,13 @@ void ImageData::flipY() { } } -void ImageData::blit(const ImageData* image, int x, int y) { - if (format == image->format) { +void ImageData::blit(const ImageData& image, int x, int y) { + if (format == image.format) { blitMatchingFormat(image, x, y); return; } if (format == ImageFormat::rgba8888 && - image->format == ImageFormat::rgb888) { + image.format == ImageFormat::rgb888) { blitRGB_on_RGBA(image, x, y); return; } @@ -187,10 +187,10 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color } } -void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) { - ubyte* source = image->getData(); - uint srcwidth = image->getWidth(); - uint srcheight = image->getHeight(); +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); srcy < std::min(srcheight, height - y); @@ -210,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; switch (format) { case ImageFormat::rgb888: comps = 3; break; @@ -218,20 +218,24 @@ void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) { default: throw std::runtime_error("only unsigned byte formats supported"); } - ubyte* source = image->getData(); - uint srcwidth = image->getWidth(); - uint srcheight = image->getHeight(); + ubyte* source = image.getData(); + + 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); - srcy < std::min(srcheight, height - y); + srcy < std::min(src_height, height - y); srcy++) { for (uint srcx = std::max(0, -x); - srcx < std::min(srcwidth, width - x); + srcx < std::min(src_width, width - x); srcx++) { uint dstx = srcx + x; uint dsty = srcy + y; 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++) { data[dstidx + c] = source[srcidx + c]; } diff --git a/src/graphics/core/ImageData.hpp b/src/graphics/core/ImageData.hpp index 00e59f6a..213a352c 100644 --- a/src/graphics/core/ImageData.hpp +++ b/src/graphics/core/ImageData.hpp @@ -15,6 +15,9 @@ class ImageData { uint width; uint height; std::unique_ptr data; + + void blitRGB_on_RGBA(const ImageData& image, int x, int y); + void blitMatchingFormat(const ImageData& image, int x, int y); public: ImageData(ImageFormat format, uint width, uint height); ImageData(ImageFormat format, uint width, uint height, std::unique_ptr data); @@ -25,9 +28,7 @@ public: void flipY(); void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color); - void blitRGB_on_RGBA(const ImageData* image, int x, int y); - 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 fixAlphaColor(); diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 145cc4d8..a844b1f1 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -114,10 +114,14 @@ namespace lua { return *mData; } + [[nodiscard]] bool hasTexture() const { + return mTexture != nullptr; + } + static int createMetatable(lua::State*); inline static std::string TYPENAME = "Canvas"; private: - std::shared_ptr mTexture; + std::shared_ptr mTexture; // nullable std::shared_ptr mData; }; static_assert(!std::is_abstract()); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index f3950c23..f041c1ff 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -79,12 +79,18 @@ static int l_set(State* L) { return 0; } -static int l_clear(State* L) { - auto canvas = touserdata(L, 1); - if (canvas == nullptr) { - throw std::runtime_error("used canvas.clear instead of canvas:clear"); +static LuaCanvas& require_canvas(State* L, int idx) { + if (const auto canvas = touserdata(L, idx)) { + return *canvas; } - auto& image = canvas->data(); + 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) { @@ -117,9 +123,20 @@ static int l_line(State* L) { 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_update(State* L) { if (auto canvas = touserdata(L, 1)) { - canvas->texture().reload(canvas->data()); + if (canvas->hasTexture()) { + canvas->texture().reload(canvas->data()); + } } return 0; } @@ -128,8 +145,10 @@ static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, {"line", lua::wrap}, + {"blit", lua::wrap}, {"clear", lua::wrap}, - {"update", lua::wrap}}; + {"update", lua::wrap}, +}; static int l_meta_index(State* L) { auto texture = touserdata(L, 1); @@ -171,11 +190,28 @@ static int l_meta_newindex(State* L) { 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( + L, + nullptr, + std::make_shared(ImageFormat::rgba8888, size.x, size.y) + ); +} + int LuaCanvas::createMetatable(State* L) { createtable(L, 0, 3); pushcfunction(L, lua::wrap); setfield(L, "__index"); pushcfunction(L, lua::wrap); setfield(L, "__newindex"); + + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__call"); + setmetatable(L); return 1; }