From d714e6943ad32ecce55ffa136fea36f56b0c0870 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 19 Nov 2025 00:34:16 +0300 Subject: [PATCH] add canvas:encode method --- src/coders/imageio.cpp | 11 ++++ src/coders/imageio.hpp | 11 ++++ src/coders/png.cpp | 64 +++++++++++++++++++ src/coders/png.hpp | 2 + .../lua/usertypes/lua_type_canvas.cpp | 20 ++++++ 5 files changed, 108 insertions(+) diff --git a/src/coders/imageio.cpp b/src/coders/imageio.cpp index 36e1f76d..dc9a78b8 100644 --- a/src/coders/imageio.cpp +++ b/src/coders/imageio.cpp @@ -53,3 +53,14 @@ void imageio::write(const io::path& file, const ImageData* image) { } return found->second(io::resolve(file).u8string(), image); } + +util::Buffer imageio::encode( + ImageFileFormat format, const ImageData& image +) { + switch (format) { + case ImageFileFormat::PNG: + return png::encode_image(image); + default: + throw std::runtime_error("file format is not supported for encoding"); + } +} diff --git a/src/coders/imageio.hpp b/src/coders/imageio.hpp index 0e59ae37..e844cf67 100644 --- a/src/coders/imageio.hpp +++ b/src/coders/imageio.hpp @@ -4,10 +4,20 @@ #include #include "io/fwd.hpp" +#include "util/Buffer.hpp" +#include "util/EnumMetadata.hpp" class ImageData; namespace imageio { + enum class ImageFileFormat { + PNG + }; + + VC_ENUM_METADATA(ImageFileFormat) + {"png", ImageFileFormat::PNG}, + VC_ENUM_END + inline const std::string PNG = ".png"; bool is_read_supported(const std::string& extension); @@ -15,4 +25,5 @@ namespace imageio { std::unique_ptr read(const io::path& file); void write(const io::path& file, const ImageData* image); + util::Buffer encode(ImageFileFormat format, const ImageData& image); } diff --git a/src/coders/png.cpp b/src/coders/png.cpp index 4121b052..a6c3f9e6 100644 --- a/src/coders/png.cpp +++ b/src/coders/png.cpp @@ -11,6 +11,60 @@ static debug::Logger logger("png-coder"); +static util::Buffer write_to_memory(uint width, uint height, const ubyte* data, bool alpha) { + uint pixsize = alpha ? 4 : 3; + + std::vector buffer; + png_structp png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr + ); + png_infop info_ptr = png_create_info_struct(png_ptr); + + png_set_write_fn( + png_ptr, + &buffer, + [](png_structp pngPtr, png_bytep data, png_size_t length) { + auto& buf = *reinterpret_cast*>(png_get_io_ptr(pngPtr)); + buf.insert( + buf.end(), + reinterpret_cast(data), + reinterpret_cast(data) + length + ); + }, + nullptr + ); + + png_set_IHDR( + png_ptr, + info_ptr, + width, + height, + 8, + alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE + ); + + png_write_info(png_ptr, info_ptr); + + auto row = std::make_unique(pixsize * width); + for (uint y = 0; y < height; y++) { + for (uint x = 0; x < width; x++) { + for (uint i = 0; i < pixsize; i++) { + row[x * pixsize + i] = + (png_byte)data[(y * width + x) * pixsize + i]; + } + } + png_write_row(png_ptr, row.get()); + } + + png_write_end(png_ptr, nullptr); + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + png_destroy_write_struct(&png_ptr, &info_ptr); + return util::Buffer(buffer.data(), buffer.size()); +} + // returns 0 if all-right, 1 otherwise static int png_write( const char* filename, uint width, uint height, const ubyte* data, bool alpha @@ -230,3 +284,13 @@ void png::write_image(const std::string& filename, const ImageData* image) { image->getFormat() == ImageFormat::rgba8888 ); } + +util::Buffer png::encode_image(const ImageData& image) { + auto format = image.getFormat(); + return write_to_memory( + image.getWidth(), + image.getHeight(), + image.getData(), + format == ImageFormat::rgba8888 + ); +} diff --git a/src/coders/png.hpp b/src/coders/png.hpp index 9ba2eda9..468efc4b 100644 --- a/src/coders/png.hpp +++ b/src/coders/png.hpp @@ -4,6 +4,7 @@ #include #include "typedefs.hpp" +#include "util/Buffer.hpp" class Texture; class ImageData; @@ -11,6 +12,7 @@ class ImageData; namespace png { std::unique_ptr load_image(const ubyte* bytes, size_t size); void write_image(const std::string& filename, const ImageData* image); + util::Buffer encode_image(const ImageData& image); std::unique_ptr load_texture(const ubyte* bytes, size_t size); std::unique_ptr load_texture(const std::string& filename); } diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index de8021de..25839659 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -1,8 +1,10 @@ +#define VC_ENABLE_REFLECTION #include "lua_type_canvas.hpp" #include "graphics/core/ImageData.hpp" #include "graphics/core/Texture.hpp" #include "logic/scripting/lua/lua_util.hpp" +#include "coders/imageio.hpp" #include "engine/Engine.hpp" #include "assets/Assets.hpp" @@ -284,6 +286,23 @@ static int l_sub(State* L) { return 0; } +static int l_encode(State* L) { + auto canvas = touserdata(L, 1); + if (canvas == nullptr) { + return 0; + } + auto format = imageio::ImageFileFormat::PNG; + if (lua::isstring(L, 2)) { + auto name = lua::require_string(L, 2); + if (!imageio::ImageFileFormatMeta.getItem(name, format)) { + throw std::runtime_error("unsupported image file format"); + } + } + + auto buffer = imageio::encode(format, canvas->getData()); + return lua::create_bytearray(L, buffer.data(), buffer.size()); +} + static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, @@ -296,6 +315,7 @@ static std::unordered_map methods { {"mul", lua::wrap}, {"add", lua::wrap}, {"sub", lua::wrap}, + {"encode", lua::wrap}, {"_set_data", lua::wrap}, };