From dca4f283ccb08fa4c4e0b3fb47c3a5b703b1d319 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 7 Mar 2025 20:15:59 +0300 Subject: [PATCH] add canvas:clear(...), canvas:line(...) --- .../lua/usertypes/lua_type_canvas.cpp | 147 +++++++++++++++--- 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index acba361d..ad79f0ca 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -17,6 +17,7 @@ union RGBA { struct { uint8_t r, g, b, a; }; + uint8_t arr[4]; uint32_t rgba; }; @@ -49,32 +50,140 @@ static int l_at(State* L) { 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(tointeger(L, first)); + break; + case 3: + rgba.a = static_cast(tointeger(L, first + 3)); + [[fallthrough]]; + case 2: + rgba.r = static_cast(tointeger(L, first)); + rgba.g = static_cast(tointeger(L, first + 1)); + rgba.b = static_cast(tointeger(L, first + 2)); + break; + } + return rgba; +} + static int l_set(State* L) { auto x = static_cast(tointeger(L, 2)); auto y = static_cast(tointeger(L, 3)); if (auto pixel = get_at(L, x, y)) { - switch (gettop(L)) { - case 4: - pixel->rgba = static_cast(tointeger(L, 4)); - return 1; - case 6: - pixel->r = static_cast(tointeger(L, 4)); - pixel->g = static_cast(tointeger(L, 5)); - pixel->b = static_cast(tointeger(L, 6)); - pixel->a = 255; - return 1; - case 7: - pixel->r = static_cast(tointeger(L, 4)); - pixel->g = static_cast(tointeger(L, 5)); - pixel->b = static_cast(tointeger(L, 6)); - pixel->a = static_cast(tointeger(L, 7)); - return 1; - default: - return 0; + *pixel = get_rgba(L, 4); + } + return 0; +} + +static int l_clear(State* L) { + RGBA rgba {}; + if (gettop(L) > 1) { + rgba = get_rgba(L, 2); + } + 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]; } } + 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); + + int x2 = tointeger(L, 4); + int y2 = tointeger(L, 5); + + 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; + } + } + } return 0; } @@ -88,6 +197,8 @@ 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} };