diff --git a/automation/v4-docs/get-frame.txt b/automation/v4-docs/get-frame.txt index 5d75e5055c..ad60b70d05 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 --- @@ -67,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 8bfe8bf376..1691f552ae 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -198,25 +198,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); @@ -227,16 +227,17 @@ 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) { - 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); @@ -255,9 +256,19 @@ namespace { return 1; } - int FrameDestory(lua_State *L) { - std::shared_ptr frame = check_VideoFrame(L); - frame.~shared_ptr(); + 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(); return 0; } @@ -279,7 +290,8 @@ namespace { {"height", FrameHeight}, {"getPixel", FramePixel}, {"getPixelFormatted", FramePixelFormatted}, - {"__gc", FrameDestory}, + {"data", FrameData}, + {"__gc", FrameDestroy}, {NULL, NULL} }; diff --git a/src/grid_column.cpp b/src/grid_column.cpp index 6519066a06..43234f49d5 100644 --- a/src/grid_column.cpp +++ b/src/grid_column.cpp @@ -205,9 +205,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); } }; @@ -223,9 +228,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); } };