From ac36455e464695714b5b78f264c34d0deabbcd13 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Wed, 3 Apr 2024 19:38:02 +0300 Subject: [PATCH] keira: fix Lua image transforms --- firmware/keira/sdcard/test_draw_image.lua | 14 +++--- .../keira/src/apps/lua/lualilka_display.cpp | 48 +----------------- .../src/apps/lua/lualilka_imageTransform.cpp | 49 ++++++++----------- .../keira/src/apps/lua/lualilka_resources.cpp | 13 ++++- sdk/addons/lualilka/library/display.lua | 13 +++++ sdk/addons/lualilka/library/resources.lua | 4 +- sdk/lib/lilka/src/lilka/menu.cpp | 3 +- 7 files changed, 59 insertions(+), 85 deletions(-) diff --git a/firmware/keira/sdcard/test_draw_image.lua b/firmware/keira/sdcard/test_draw_image.lua index db9f3e51..4ca7e4fe 100644 --- a/firmware/keira/sdcard/test_draw_image.lua +++ b/firmware/keira/sdcard/test_draw_image.lua @@ -1,6 +1,6 @@ print("Printing stuff to console, yay!") -local face = resources.load_image("face.bmp", display.color565(0, 0, 0)) +local face = resources.load_image("face.bmp", display.color565(0, 0, 0), 32, 32) -- Image size is 64x64, pivot is at 32, 32 print('Face size: ' .. face.width .. 'x' .. face.height) @@ -9,8 +9,8 @@ for i = 10, 1, -1 do display.set_cursor(64, 64) display.print("Start in " .. i .. "...") - local x = math.random(240 - 64) - local y = math.random(280 - 64) + local x = math.random(32, display.width - 32) + local y = math.random(32, display.height - 32) display.draw_image(face, x, y) display.queue_draw() @@ -23,8 +23,8 @@ for i = 10, 1, -1 do display.set_cursor(64, 64) display.print("Start in " .. i .. "...") - local x = math.random(240) - local y = math.random(280 - 64) + local x = math.random(32, display.width - 32) + local y = math.random(32, display.height - 32) local rot = math.random(360) local scaleX = math.min({ math.random(), 0.1 }) @@ -52,8 +52,8 @@ while not key.a.just_pressed do local color = math.random(0xFFFF) display.draw_line(x1, y1, x2, y2, color) - local x = math.random(240 - 64) - local y = math.random(280 - 64) + local x = math.random(32, display.width - 32) + local y = math.random(32, display.height - 32) display.draw_image(face, x, y) display.queue_draw() diff --git a/firmware/keira/src/apps/lua/lualilka_display.cpp b/firmware/keira/src/apps/lua/lualilka_display.cpp index 31b8a152..c7a36e80 100644 --- a/firmware/keira/src/apps/lua/lualilka_display.cpp +++ b/firmware/keira/src/apps/lua/lualilka_display.cpp @@ -267,60 +267,16 @@ int lualilka_display_drawImageTransformed(lua_State* L) { return luaL_error(L, "Invalid image pointer"); } - lilka::Image* image = reinterpret_cast(lua_touserdata(L, -1)); + lilka::Image* image = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); // Pop the userdata pointer int16_t x = luaL_checknumber(L, 2); int16_t y = luaL_checknumber(L, 3); - // int32_t imageWidth = image->width; - // int32_t imageHeight = image->height; - - lilka::Transform* transform = *reinterpret_cast(luaL_checkudata(L, 4, IMAGE_TRANSFORM)); + lilka::Transform* transform = static_cast(luaL_checkudata(L, 4, IMAGE_TRANSFORM)); getDrawable(L)->drawImageTransformed(image, x, y, *transform); - // // Calculate the coordinates of the four corners of the destination rectangle. - // lilka::int_vector_t v1 = transform.transform(lilka::int_vector_t{-image->pivotX, -image->pivotY}); - // lilka::int_vector_t v2 = transform.transform(lilka::int_vector_t{imageWidth - image->pivotX, -image->pivotY}); - // lilka::int_vector_t v3 = transform.transform(lilka::int_vector_t{-image->pivotX, imageHeight - image->pivotY}); - // lilka::int_vector_t v4 = - // transform.transform(lilka::int_vector_t{imageWidth - image->pivotX, imageHeight - image->pivotY}); - - // // Find the bounding box of the transformed image. - // lilka::int_vector_t topLeft = - // lilka::int_vector_t{min(min(v1.x, v2.x), min(v3.x, v4.x)), min(min(v1.y, v2.y), min(v3.y, v4.y))}; - // lilka::int_vector_t bottomRight = - // lilka::int_vector_t{max(max(v1.x, v2.x), max(v3.x, v4.x)), max(max(v1.y, v2.y), max(v3.y, v4.y))}; - - // // Create a new image to hold the transformed image. - // lilka::Image destImage(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y, image->transparentColor, 0, 0); - - // // Draw the transformed image to the new image. - // lilka::Transform inverse = transform.inverse(); - // for (int y = topLeft.y; y < bottomRight.y; y++) { - // for (int x = topLeft.x; x < bottomRight.x; x++) { - // lilka::int_vector_t v = inverse.transform(lilka::int_vector_t{x, y}); - // // Apply pivot offset - // v.x += image->pivotX; - // v.y += image->pivotY; - // if (v.x >= 0 && v.x < image->width && v.y >= 0 && v.y < image->height) { - // destImage.pixels[x - topLeft.x + (y - topLeft.y) * destImage.width] = - // image->pixels[v.x + v.y * image->width]; - // } else { - // destImage.pixels[x - topLeft.x + (y - topLeft.y) * destImage.width] = image->transparentColor; - // } - // } - // } - - // if (destImage.transparentColor >= 0) { - // getDrawable(L)->draw16bitRGBBitmapWithTranColor( - // d_x, d_y, destImage.pixels, destImage.transparentColor, destImage.width, destImage.height - // ); - // } else { - // getDrawable(L)->draw16bitRGBBitmap(d_x, d_y, destImage.pixels, destImage.width, destImage.height); - // } - return 0; } diff --git a/firmware/keira/src/apps/lua/lualilka_imageTransform.cpp b/firmware/keira/src/apps/lua/lualilka_imageTransform.cpp index a70426ea..8b7c3f24 100644 --- a/firmware/keira/src/apps/lua/lualilka_imageTransform.cpp +++ b/firmware/keira/src/apps/lua/lualilka_imageTransform.cpp @@ -2,60 +2,55 @@ #include "lilka/display.h" static int lualilka_create_object_imageTransform(lua_State* L) { - *reinterpret_cast(lua_newuserdata(L, sizeof(lilka::Transform*))) = new lilka::Transform(); + lilka::Transform* userdata = static_cast(lua_newuserdata(L, sizeof(lilka::Transform))); + new (userdata) lilka::Transform(); luaL_setmetatable(L, IMAGE_TRANSFORM); return 1; } static int lualilka_delete_object_imageTransform(lua_State* L) { - lilka::Transform** transformPtr = reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); - if (*transformPtr) { - delete *transformPtr; - *transformPtr = nullptr; - } else { - lilka::serial_err("double free of transform %p, should never happen", transformPtr); - } + const lilka::Transform* transformPtr = static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); + transformPtr->~Transform(); return 0; } static int lualilka_imageTransform_rotate(lua_State* L) { - lilka::Transform* transformPtr = reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); - lilka::Transform* rotatedTransform = new lilka::Transform(transformPtr->rotate(luaL_checknumber(L, 2))); - *reinterpret_cast(lua_newuserdata(L, sizeof(lilka::Transform))) = rotatedTransform; + lilka::Transform* transformPtr = static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); + lilka::Transform* rotatedTransform = static_cast(lua_newuserdata(L, sizeof(lilka::Transform))); + new (rotatedTransform) lilka::Transform(transformPtr->rotate(luaL_checknumber(L, 2))); luaL_setmetatable(L, IMAGE_TRANSFORM); return 1; } static int lualilka_imageTransform_scale(lua_State* L) { - lilka::Transform* transformPtr = reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); - lilka::Transform* scaledTransform = - new lilka::Transform(transformPtr->scale(luaL_checknumber(L, 2), luaL_checknumber(L, 3))); - *reinterpret_cast(lua_newuserdata(L, sizeof(lilka::Transform))) = scaledTransform; + lilka::Transform* transformPtr = static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); + lilka::Transform* scaledTransform = static_cast(lua_newuserdata(L, sizeof(lilka::Transform))); + new (scaledTransform) lilka::Transform(transformPtr->scale(luaL_checknumber(L, 2), luaL_checknumber(L, 3))); luaL_setmetatable(L, IMAGE_TRANSFORM); return 1; } static int lualilka_imageTransform_multiply(lua_State* L) { - lilka::Transform* transformPtr = reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); - lilka::Transform* multipliedTransfform = new lilka::Transform( - transformPtr->multiply(*reinterpret_cast(luaL_checkudata(L, 2, IMAGE_TRANSFORM))) - ); - *reinterpret_cast(lua_newuserdata(L, sizeof(lilka::Transform))) = multipliedTransfform; + lilka::Transform* transformPtr = static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); + lilka::Transform* multipliedTransfform = + static_cast(lua_newuserdata(L, sizeof(lilka::Transform))); + new (multipliedTransfform + ) lilka::Transform(transformPtr->multiply(*static_cast(luaL_checkudata(L, 2, IMAGE_TRANSFORM)))); luaL_setmetatable(L, IMAGE_TRANSFORM); return 1; } static int lualilka_imageTransform_inverse(lua_State* L) { - lilka::Transform* transformPtr = reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); - lilka::Transform* inversedTransform = new lilka::Transform(transformPtr->inverse()); - *reinterpret_cast(lua_newuserdata(L, sizeof(lilka::Transform))) = inversedTransform; + lilka::Transform* transformPtr = static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)); + lilka::Transform* inversedTransform = static_cast(lua_newuserdata(L, sizeof(lilka::Transform))); + new (inversedTransform) lilka::Transform(transformPtr->inverse()); luaL_setmetatable(L, IMAGE_TRANSFORM); return 1; } static int lualilka_imageTransform_vtransform(lua_State* L) { lilka::int_vector_t result = - (*reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM))) + (static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM))) ->transform(lilka::int_vector_t{.x = (int)luaL_checknumber(L, 2), .y = (int)luaL_checknumber(L, 3)}); lua_pushinteger(L, result.x); lua_pushinteger(L, result.y); @@ -67,9 +62,7 @@ static int lualilka_imageTransform_get_matrix(lua_State* L) { for (int i = 0; i < 2; ++i) { lua_newtable(L); for (int j = 0; j < 2; ++j) { - lua_pushinteger( - L, (*reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)))->matrix[i][j] - ); + lua_pushinteger(L, (static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)))->matrix[i][j]); lua_rawseti(L, -2, j + 1); } lua_rawseti(L, -2, i + 1); @@ -91,7 +84,7 @@ static int lualilka_imageTransform_set_matrix(lua_State* L) { return luaL_error(L, "Element at (%d, %d) is not a number", i, j); } - (*reinterpret_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)))->matrix[i][j] = + (static_cast(luaL_checkudata(L, 1, IMAGE_TRANSFORM)))->matrix[i][j] = lua_tonumber(L, -1); lua_pop(L, 1); } diff --git a/firmware/keira/src/apps/lua/lualilka_resources.cpp b/firmware/keira/src/apps/lua/lualilka_resources.cpp index dd9f1e53..e13b90d1 100644 --- a/firmware/keira/src/apps/lua/lualilka_resources.cpp +++ b/firmware/keira/src/apps/lua/lualilka_resources.cpp @@ -14,14 +14,23 @@ int lualilka_resources_loadImage(lua_State* L) { transparencyColor = lua_tointeger(L, 2); } } + int32_t pivotX = 0; + int32_t pivotY = 0; + if (lua_gettop(L) > 3) { + // Pivot X/Y + pivotX = luaL_checkinteger(L, 3); + pivotY = luaL_checkinteger(L, 4); + } - lilka::Image* image = lilka::resources.loadImage(fullPath, transparencyColor); + lilka::Image* image = lilka::resources.loadImage(fullPath, transparencyColor, pivotX, pivotY); if (!image) { return luaL_error(L, "Не вдалося завантажити зображення %s", fullPath.c_str()); } - lilka::serial_log("lua: loaded image %s, width: %d, height: %d", path, image->width, image->height); + lilka::serial_log( + "lua: loaded image %s, size: %d x %d, pivot: %d,%d", path, image->width, image->height, pivotX, pivotY + ); // Append image to images table in registry lua_getfield(L, LUA_REGISTRYINDEX, "images"); diff --git a/sdk/addons/lualilka/library/display.lua b/sdk/addons/lualilka/library/display.lua index 34f59b03..f0a35391 100644 --- a/sdk/addons/lualilka/library/display.lua +++ b/sdk/addons/lualilka/library/display.lua @@ -182,6 +182,19 @@ function display.fill_arc(x, y, r1, r2, start_angle, end_angle, color) end --- display.draw_image(image, 50, 80) -- малює зображення в точці (50, 80) function display.draw_image(image, x, y) end +---Малює зображення на екрані з перетворенням. +---@param image table ідентифікатор зображення +---@param x integer координата x лівого верхнього кута зображення +---@param y integer координата y лівого верхнього кута зображення +---@param transform userdata перетворення (TODO: додати опис модуля imageTransform) +---@usage +--- local image = resource.load_image("face.bmp", display.color565(0, 0, 0)) +--- local transform = imageTransform() +-- transform = transform:scale(1.5, 0.5) +--- transform = transform:rotate(45) +--- display.draw_image(image, 50, 80, transform) -- малює зображення в точці (50, 80) з перетвореннями +function display.draw_image_transformed(image, x, y, transform) end + ---Примусово оновлює вміст екрану. --- ---.. warning:: Ми не радимо використовувати цю функцію. Вона викликається автоматично після кожного виконання ``lilka.draw()``. Вам слід писати весь код малювання всередині своєї функції ``lilka.draw()``, а цю функцію використовувати лише для тестування простих програм. diff --git a/sdk/addons/lualilka/library/resources.lua b/sdk/addons/lualilka/library/resources.lua index 02f27642..fcc7754f 100644 --- a/sdk/addons/lualilka/library/resources.lua +++ b/sdk/addons/lualilka/library/resources.lua @@ -13,12 +13,14 @@ resources = {} --- ---@param filename string шлях до файлу зображення .BMP (відносно місця знаходження скрипта, що виконується) ---@param transparent_color? integer колір, який буде використаний для прозорості (5-6-5). Якщо цей параметр не вказаний, зображення буде виводитись без прозорості +---@param pivotX? integer X-координата центру зображення (за замовчуванням це середина зображення) +---@param pivotY? integer Y-координата центру зображення (за замовчуванням це середина зображення) ---@return table ---@usage --- local face = resources.load_image("face.bmp", display.color565(0, 0, 0)) --- print(face.width, face.height) -- Виведе розміри зображення --- display.draw_image(face, 50, 80) -- Виведе зображення на екран у позицію (50, 80) -function resources.load_image(filename, transparent_color) end +function resources.load_image(filename, transparent_color, pivotX, pivotY) end ---Повертає зображення на певну кількість градусів за годинниковою стрілкою навколо його центру. --- diff --git a/sdk/lib/lilka/src/lilka/menu.cpp b/sdk/lib/lilka/src/lilka/menu.cpp index 2600f83b..8115439b 100644 --- a/sdk/lib/lilka/src/lilka/menu.cpp +++ b/sdk/lib/lilka/src/lilka/menu.cpp @@ -196,7 +196,8 @@ void Menu::draw(Arduino_GFX* canvas) { canvas->println(items[i].postfix); } - int16_t widthAvailable = canvas->width() - 32 - postfixWidth - 8 - 8; // 8 pixels for scrollbar, 8 more for padding + int16_t widthAvailable = + canvas->width() - 32 - postfixWidth - 8 - 8; // 8 pixels for scrollbar, 8 more for padding if (widthAvailable < 0) { // No space for title continue;