From 48855787a1a5744edf0e2629f12220d66726eb38 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 17 Feb 2024 01:43:09 +0100 Subject: [PATCH 1/5] lua: Fix FrameDestroy function name --- src/auto4_lua.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 370a791e3b..8e4c736412 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -258,7 +258,7 @@ namespace { return 1; } - int FrameDestory(lua_State *L) { + int FrameDestroy(lua_State *L) { std::shared_ptr frame = check_VideoFrame(L); frame.~shared_ptr(); return 0; @@ -282,7 +282,7 @@ namespace { {"height", FrameHeight}, {"getPixel", FramePixel}, {"getPixelFormatted", FramePixelFormatted}, - {"__gc", FrameDestory}, + {"__gc", FrameDestroy}, {NULL, NULL} }; From c11f17b9e666d8ef05a6d7990989e5bf6247f4af Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 17 Feb 2024 02:13:43 +0100 Subject: [PATCH 2/5] lua: Correct refcount handling in get_frame When destroying a frame handle, the previous logic would copy the shared_ptr in the userdata and then free it twice (once explicitly and once at the end of the function), which is actually UB, even if if worked fine so far. This commit now ensures that it's the actual userdata's shared_ptr that's freed in the gc function. --- src/auto4_lua.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 8e4c736412..b5e381fe1a 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -201,25 +201,25 @@ namespace { } } - std::shared_ptr check_VideoFrame(lua_State *L) { + std::shared_ptr *check_VideoFrame(lua_State *L) { auto framePtr = static_cast*>(luaL_checkudata(L, 1, "VideoFrame")); - return *framePtr; + return framePtr; } int FrameWidth(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); + std::shared_ptr frame = *check_VideoFrame(L); push_value(L, frame->width); return 1; } int FrameHeight(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); + std::shared_ptr frame = *check_VideoFrame(L); push_value(L, frame->height); return 1; } int FramePixel(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); + std::shared_ptr frame = *check_VideoFrame(L); size_t x = lua_tointeger(L, -2); size_t y = lua_tointeger(L, -1); lua_pop(L, 2); @@ -239,7 +239,7 @@ namespace { } int FramePixelFormatted(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); + std::shared_ptr frame = *check_VideoFrame(L); size_t x = lua_tointeger(L, -2); size_t y = lua_tointeger(L, -1); lua_pop(L, 2); @@ -259,8 +259,8 @@ namespace { } int FrameDestroy(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); - frame.~shared_ptr(); + std::shared_ptr *frame = check_VideoFrame(L); + frame->~shared_ptr(); return 0; } From 63bbdc32d3da7d5a584b0912c5a434b4c3fa65a4 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 17 Feb 2024 02:22:23 +0100 Subject: [PATCH 3/5] lua: Make frame:getPixel return three values This is what users will want in the majority of cases, and switching to this makes using this function much easier. However, this does break backwards compatibility. Luckily to my knowledge this function is not actually used in any existing published script (all scripts using get_frame just use getPixelFormatted instead) so the damage shouldn't be too large. But this is also why I'd rather rip off the band-aid of breaking backwards compatibility now than later. --- automation/v4-docs/get-frame.txt | 8 +++++--- src/auto4_lua.cpp | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/automation/v4-docs/get-frame.txt b/automation/v4-docs/get-frame.txt index 5d75e5055c..99342ac4a1 100644 --- a/automation/v4-docs/get-frame.txt +++ b/automation/v4-docs/get-frame.txt @@ -40,7 +40,7 @@ Returns: number Get RGB pixel value at a certain position of frame object. -function frame:frame:getPixel(x, y) +function frame:getPixel(x, y) @x (number) Pixel to retrieve on the x-axis @@ -48,8 +48,10 @@ function frame:frame:getPixel(x, y) @y (number) Pixel to retrieve on the y-axis -Returns: number - Integer value representing the RGB pixel value. +Returns: 3 values, all numbers + 1. R value of the pixel + 2. G value of the pixel + 3. B value of the pixel --- diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index b5e381fe1a..449a279899 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -230,12 +230,13 @@ namespace { size_t pos = y * frame->pitch + x * 4; // VideoFrame is stored as BGRA, but we want to return RGB - int pixelValue = frame->data[pos+2] * 65536 + frame->data[pos+1] * 256 + frame->data[pos]; - push_value(L, pixelValue); + push_value(L, frame->data[pos+2]); + push_value(L, frame->data[pos+1]); + push_value(L, frame->data[pos]); } else { lua_pushnil(L); } - return 1; + return 3; } int FramePixelFormatted(lua_State *L) { From 1515c774387da09cf4505ad303a62a272116cc59 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Mon, 19 Feb 2024 14:22:27 +0100 Subject: [PATCH 4/5] Fix widths of time columns for fonts with proportional figures This still breaks when the 0 digit is narrower than some other digit, but that's unlikely and the current behavior now matches the original behavior when all times are < 10h. Fixes arch1t3cht/Aegisub#111 . --- src/grid_column.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/grid_column.cpp b/src/grid_column.cpp index aff1985137..c28b460dc9 100644 --- a/src/grid_column.cpp +++ b/src/grid_column.cpp @@ -159,9 +159,14 @@ struct GridColumnStartTime final : GridColumnTime { int Width(const agi::Context *c, WidthHelper &helper) const override { agi::Time max_time = max_value(&AssDialogue::Start, c->ass->Events); - if (!by_frame) - return helper(max_time.GetAssFormatted()); - return helper(std::to_wstring(c->videoController->FrameAtTime(max_time, agi::vfr::START))); + std::string value = by_frame ? std::to_string(c->videoController->FrameAtTime(max_time, agi::vfr::START)) : max_time.GetAssFormatted(); + + for (char &c : value) { + if (c >= '0' && c <= '9') + c = '0'; + } + + return helper(value); } }; @@ -177,9 +182,14 @@ struct GridColumnEndTime final : GridColumnTime { int Width(const agi::Context *c, WidthHelper &helper) const override { agi::Time max_time = max_value(&AssDialogue::End, c->ass->Events); - if (!by_frame) - return helper(max_time.GetAssFormatted()); - return helper(std::to_wstring(c->videoController->FrameAtTime(max_time, agi::vfr::END))); + std::string value = by_frame ? std::to_string(c->videoController->FrameAtTime(max_time, agi::vfr::END)) : max_time.GetAssFormatted(); + + for (char &c : value) { + if (c >= '0' && c <= '9') + c = '0'; + } + + return helper(value); } }; From f52b69949378941558aafc7fcab6989632d2715d Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 20 Feb 2024 15:02:59 +0100 Subject: [PATCH 5/5] lua: Add function to get raw frame data This can be useful when scripts want to read the entire frame and pass it on somewhere else (say, write it to a file or use some library function on it). --- automation/v4-docs/get-frame.txt | 22 ++++++++++++++++++++++ src/auto4_lua.cpp | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/automation/v4-docs/get-frame.txt b/automation/v4-docs/get-frame.txt index 99342ac4a1..ad60b70d05 100644 --- a/automation/v4-docs/get-frame.txt +++ b/automation/v4-docs/get-frame.txt @@ -69,3 +69,25 @@ Returns: string String in ASS format representing the pixel value. e.g. "&H0073FF&" --- + +Get raw BGRA (alpha being irrelevant) data of frame object, whose pixel values +can then be accessed via LuaJIT's FFI. + +The frame data is valid until the frame object is garbage-collected. + +Example usage (which does not account for flipped frames for simplicity) + +data, pitch = frame:data() +buf = require("ffi").cast("unsigned char *", data) +-- Get the R value of the pixel at coordinates (42, 34) +pix_val = buf[34 * pitch + 4 * 42 + 2] + +function frame:data() + +Returns: 3 values - a lightuserdata, a number, and a boolean + 1. Lightuserdata object which can be cast to "unsigned char *" via ffi.cast, a pointer + to the raw frame data. + 2. The pitch of the frame data. + 3. Whether the frame is flipped upside-down. + +--- diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 449a279899..f6632971df 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -259,6 +259,16 @@ namespace { return 1; } + int FrameData(lua_State *L) { + std::shared_ptr frame = *check_VideoFrame(L); + + push_value(L, frame->data.data()); + push_value(L, frame->pitch); + push_value(L, frame->flipped); + + return 3; + } + int FrameDestroy(lua_State *L) { std::shared_ptr *frame = check_VideoFrame(L); frame->~shared_ptr(); @@ -283,6 +293,7 @@ namespace { {"height", FrameHeight}, {"getPixel", FramePixel}, {"getPixelFormatted", FramePixelFormatted}, + {"data", FrameData}, {"__gc", FrameDestroy}, {NULL, NULL} };