From 5bbba7f39d1da5a76c4ace96a7102d6f78d9fa8e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 13:36:43 +0300 Subject: [PATCH] add ImageData.drawLine --- src/graphics/core/ImageData.cpp | 94 +++++++++++++++ src/graphics/core/ImageData.hpp | 7 ++ .../lua/usertypes/lua_type_canvas.cpp | 113 ++++-------------- 3 files changed, 121 insertions(+), 93 deletions(-) diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index d6617c57..b5e05d1c 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include ImageData::ImageData(ImageFormat format, uint width, uint height) @@ -93,6 +94,99 @@ void ImageData::blit(const ImageData* image, int x, int y) { throw std::runtime_error("mismatching format"); } +static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { + const int left = 0; + const int right = width; + 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(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(std::round(t1 * dx)); + y2 = y1 + static_cast(std::round(t1 * dy)); + } + if (t0 > 0.0f) { + x1 = x1 + static_cast(std::round(t0 * dx)); + y1 = y1 + static_cast(std::round(t0 * dy)); + } + return true; +} + +template +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(); diff --git a/src/graphics/core/ImageData.hpp b/src/graphics/core/ImageData.hpp index 328ff896..00e59f6a 100644 --- a/src/graphics/core/ImageData.hpp +++ b/src/graphics/core/ImageData.hpp @@ -2,6 +2,7 @@ #include "typedefs.hpp" +#include #include enum class ImageFormat { @@ -23,6 +24,7 @@ public: void flipX(); 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); @@ -44,6 +46,11 @@ public: uint getHeight() const { return height; } + + size_t getDataSize() const { + size_t channels = 3 + (format == ImageFormat::rgba8888); + return width * height * channels; + } }; std::unique_ptr add_atlas_margins(ImageData* image, int grid_size); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index ad79f0ca..f3950c23 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -80,65 +80,26 @@ static int l_set(State* L) { } static int l_clear(State* L) { - RGBA rgba {}; - if (gettop(L) > 1) { - rgba = get_rgba(L, 2); + auto canvas = touserdata(L, 1); + if (canvas == nullptr) { + throw std::runtime_error("used canvas.clear instead of canvas:clear"); } - if (auto canvas = touserdata(L, 1)) { - auto& image = canvas->data(); - ubyte* data = image.getData(); - 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]; - } + 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; } -bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { - const int left = 0; - const int right = width; - 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(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(std::round(t1 * dx)); - y2 = y1 + static_cast(std::round(t1 * dy)); - } - if (t0 > 0.0f) { - x1 = x1 + static_cast(std::round(t0 * dx)); - y1 = y1 + static_cast(std::round(t0 * dy)); - } - return true; -} - static int l_line(State* L) { int x1 = tointeger(L, 2); int y1 = tointeger(L, 3); @@ -149,40 +110,9 @@ static int l_line(State* L) { RGBA rgba = get_rgba(L, 6); if (auto canvas = touserdata(L, 1)) { auto& image = canvas->data(); - ubyte* data = image.getData(); - uint width = image.getWidth(); - uint height = image.getHeight(); - const uint channels = 4; - - 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 0; - } - - int dx = glm::abs(x2 - x1); - int dy = -glm::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] = rgba.arr[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; - } - } + image.drawLine( + x1, y1, x2, y2, glm::ivec4 {rgba.r, rgba.g, rgba.b, rgba.a} + ); } return 0; } @@ -197,10 +127,9 @@ static int l_update(State* L) { static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, - {"clear", lua::wrap}, {"line", lua::wrap}, - {"update", lua::wrap} -}; + {"clear", lua::wrap}, + {"update", lua::wrap}}; static int l_meta_index(State* L) { auto texture = touserdata(L, 1); @@ -237,9 +166,7 @@ static int l_meta_newindex(State* L) { if (isnumber(L, 2) && isnumber(L, 3)) { if (auto pixel = get_at(data, static_cast(tointeger(L, 2)))) { pixel->rgba = static_cast(tointeger(L, 3)); - return 1; } - return 1; } return 0; }