| #ifdef WITH_RIVE_SCRIPTING |
| #include "lua.h" |
| #include "rive/renderer.hpp" |
| #include "rive/rive_types.hpp" |
| #include "rive/shapes/paint/blend_mode.hpp" |
| #include "lualib.h" |
| #include "rive/lua/rive_lua_libs.hpp" |
| #include "rive/factory.hpp" |
| |
| using namespace rive; |
| |
| static void readStyle(lua_State* L, ScriptedPaint* paint, int index) |
| { |
| int atom; |
| const char* styleName = lua_tostringatom(L, index, &atom); |
| if (!styleName) |
| { |
| luaL_typeerrorL(L, index, lua_typename(L, LUA_TSTRING)); |
| } |
| switch (atom) |
| { |
| case (int)LuaAtoms::stroke: |
| paint->style(RenderPaintStyle::stroke); |
| break; |
| case (int)LuaAtoms::fill: |
| paint->style(RenderPaintStyle::fill); |
| break; |
| default: |
| luaL_error(L, "'%s' is not a valid PaintStyle", styleName); |
| break; |
| } |
| } |
| |
| static void readJoin(lua_State* L, ScriptedPaint* paint, int index) |
| { |
| int atom; |
| const char* joinName = lua_tostringatom(L, index, &atom); |
| if (!joinName) |
| { |
| luaL_typeerrorL(L, index, lua_typename(L, LUA_TSTRING)); |
| } |
| switch (atom) |
| { |
| case (int)LuaAtoms::miter: |
| paint->join(StrokeJoin::miter); |
| break; |
| case (int)LuaAtoms::round: |
| paint->join(StrokeJoin::round); |
| break; |
| case (int)LuaAtoms::bevel: |
| paint->join(StrokeJoin::bevel); |
| break; |
| default: |
| luaL_error(L, "'%s' is not a valid StrokeJoin", joinName); |
| break; |
| } |
| } |
| |
| static void readCap(lua_State* L, ScriptedPaint* paint, int index) |
| { |
| int atom; |
| const char* capName = lua_tostringatom(L, index, &atom); |
| if (!capName) |
| { |
| luaL_typeerrorL(L, index, lua_typename(L, LUA_TSTRING)); |
| } |
| switch (atom) |
| { |
| case (int)LuaAtoms::butt: |
| paint->cap(StrokeCap::butt); |
| break; |
| case (int)LuaAtoms::round: |
| paint->cap(StrokeCap::round); |
| break; |
| case (int)LuaAtoms::square: |
| paint->cap(StrokeCap::square); |
| break; |
| default: |
| luaL_error(L, "'%s' is not a valid StrokeCap", capName); |
| break; |
| } |
| } |
| |
| BlendMode rive::lua_toblendmode(lua_State* L, int idx) |
| { |
| int atom; |
| const char* blendName = lua_tostringatom(L, idx, &atom); |
| if (!blendName) |
| { |
| luaL_typeerrorL(L, idx, lua_typename(L, LUA_TSTRING)); |
| } |
| switch (atom) |
| { |
| case (int)LuaAtoms::srcOver: |
| return BlendMode::srcOver; |
| |
| case (int)LuaAtoms::screen: |
| return BlendMode::screen; |
| |
| case (int)LuaAtoms::overlay: |
| return BlendMode::overlay; |
| |
| case (int)LuaAtoms::darken: |
| return BlendMode::darken; |
| |
| case (int)LuaAtoms::lighten: |
| return BlendMode::lighten; |
| |
| case (int)LuaAtoms::colorDodge: |
| return BlendMode::colorDodge; |
| |
| case (int)LuaAtoms::colorBurn: |
| return BlendMode::colorBurn; |
| |
| case (int)LuaAtoms::hardLight: |
| return BlendMode::hardLight; |
| |
| case (int)LuaAtoms::softLight: |
| return BlendMode::softLight; |
| |
| case (int)LuaAtoms::difference: |
| return BlendMode::difference; |
| |
| case (int)LuaAtoms::exclusion: |
| return BlendMode::exclusion; |
| |
| case (int)LuaAtoms::multiply: |
| return BlendMode::multiply; |
| |
| case (int)LuaAtoms::hue: |
| return BlendMode::hue; |
| |
| case (int)LuaAtoms::saturation: |
| return BlendMode::saturation; |
| |
| case (int)LuaAtoms::color: |
| return BlendMode::color; |
| |
| case (int)LuaAtoms::luminosity: |
| return BlendMode::luminosity; |
| |
| default: |
| luaL_error(L, "'%s' is not a valid BlendMode", blendName); |
| return BlendMode::srcOver; |
| } |
| } |
| |
| static void readBlendMode(lua_State* L, ScriptedPaint* paint, int index) |
| { |
| auto blendMode = lua_toblendmode(L, index); |
| paint->blendMode(blendMode); |
| } |
| |
| ScriptedPaint::ScriptedPaint(Factory* factory) : |
| renderPaint(factory->makeRenderPaint()) |
| {} |
| |
| ScriptedPaint::ScriptedPaint(Factory* factory, const ScriptedPaint& source) : |
| ScriptedPaint(factory) |
| { |
| style(source.m_style); |
| color(source.m_color); |
| thickness(source.m_thickness); |
| join(source.m_join); |
| cap(source.m_cap); |
| feather(source.m_feather); |
| blendMode(source.m_blendMode); |
| gradient(source.m_gradient); |
| } |
| |
| static bool paint_set_value(lua_State* L, |
| ScriptedPaint* renderPaint, |
| int keyAtom, |
| int valueIndex) |
| { |
| switch (keyAtom) |
| { |
| case (int)LuaAtoms::style: |
| readStyle(L, renderPaint, valueIndex); |
| return true; |
| case (int)LuaAtoms::join: |
| readJoin(L, renderPaint, valueIndex); |
| return true; |
| case (int)LuaAtoms::cap: |
| readCap(L, renderPaint, valueIndex); |
| return true; |
| case (int)LuaAtoms::thickness: |
| renderPaint->thickness(float(luaL_checknumber(L, valueIndex))); |
| return true; |
| case (int)LuaAtoms::blendMode: |
| readBlendMode(L, renderPaint, valueIndex); |
| return true; |
| case (int)LuaAtoms::feather: |
| renderPaint->feather(float(luaL_checknumber(L, valueIndex))); |
| return true; |
| case (int)LuaAtoms::gradient: |
| { |
| ScriptedGradient* gradient = |
| lua_torive<ScriptedGradient>(L, valueIndex, true); |
| if (gradient != nullptr) |
| { |
| renderPaint->gradient(gradient->shader); |
| } |
| else |
| { |
| renderPaint->gradient(nullptr); |
| } |
| return true; |
| } |
| case (int)LuaAtoms::color: |
| renderPaint->color(luaL_checkunsigned(L, valueIndex)); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static void setPropertiesFromDefinitionTable(lua_State* L, |
| ScriptedPaint* scriptedPaint, |
| int tableIndex) |
| { |
| luaL_checktype(L, tableIndex, LUA_TTABLE); |
| |
| // iterate table |
| // Push another reference to the table on top of the stack (so we know |
| // where it is, and this function can work for negative, positive and |
| // pseudo indices |
| lua_pushvalue(L, tableIndex); |
| // stack now contains: -1 => table |
| lua_pushnil(L); |
| // stack now contains: -1 => nil; -2 => table |
| while (lua_next(L, -2)) |
| { |
| // stack now contains: -1 => value; -2 => key; -3 => table |
| // copy the key so that lua_tostring does not modify the original |
| // lua_pushvalue(L, -2); |
| // stack now contains: -1 => key; -2 => value; -3 => key; -4 => |
| // table |
| int atom; |
| const char* key = lua_tostringatom(L, -2, &atom); |
| if (key) |
| { |
| paint_set_value(L, scriptedPaint, atom, -1); |
| } |
| lua_pop(L, 1); |
| // stack now contains: -1 => key; -2 => table |
| } |
| // stack now contains: -1 => table (when lua_next returns 0 it pops the |
| // key but does not push anything.) Pop table |
| lua_pop(L, 1); |
| } |
| |
| static int paint_new(lua_State* L) |
| { |
| ScriptingContext* context = |
| static_cast<ScriptingContext*>(lua_getthreaddata(L)); |
| lua_newrive<ScriptedPaint>(L, context->factory()); |
| |
| return 1; |
| } |
| |
| static int paint_with(lua_State* L) |
| { |
| ScriptingContext* context = |
| static_cast<ScriptingContext*>(lua_getthreaddata(L)); |
| auto scriptedPaint = lua_newrive<ScriptedPaint>(L, context->factory()); |
| setPropertiesFromDefinitionTable(L, scriptedPaint, 1); |
| return 1; |
| } |
| |
| static int paint_newindex(lua_State* L) |
| { |
| int atom; |
| const char* key = lua_tostringatom(L, 2, &atom); |
| if (!key) |
| { |
| luaL_typeerrorL(L, 2, lua_typename(L, LUA_TSTRING)); |
| return 0; |
| } |
| |
| auto scriptedPaint = lua_torive<ScriptedPaint>(L, 1); |
| paint_set_value(L, scriptedPaint, atom, 3); |
| |
| return 0; |
| } |
| |
| void ScriptedPaint::pushStyle(lua_State* L) |
| { |
| switch (m_style) |
| { |
| case RenderPaintStyle::fill: |
| lua_pushstring(L, "fill"); |
| break; |
| case RenderPaintStyle::stroke: |
| lua_pushstring(L, "stroke"); |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| |
| void ScriptedPaint::pushJoin(lua_State* L) |
| { |
| switch (m_join) |
| { |
| case StrokeJoin::miter: |
| lua_pushstring(L, "miter"); |
| break; |
| case StrokeJoin::bevel: |
| lua_pushstring(L, "bevel"); |
| break; |
| case StrokeJoin::round: |
| lua_pushstring(L, "round"); |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| |
| void ScriptedPaint::pushCap(lua_State* L) |
| { |
| switch (m_cap) |
| { |
| case StrokeCap::butt: |
| lua_pushstring(L, "butt"); |
| break; |
| case StrokeCap::square: |
| lua_pushstring(L, "square"); |
| break; |
| case StrokeCap::round: |
| lua_pushstring(L, "round"); |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| |
| void ScriptedPaint::pushThickness(lua_State* L) |
| { |
| lua_pushnumber(L, m_thickness); |
| } |
| |
| void ScriptedPaint::pushBlendMode(lua_State* L) |
| { |
| switch (m_blendMode) |
| { |
| case BlendMode::srcOver: |
| lua_pushstring(L, "srcOver"); |
| break; |
| case BlendMode::screen: |
| lua_pushstring(L, "screen"); |
| break; |
| case BlendMode::overlay: |
| lua_pushstring(L, "overlay"); |
| break; |
| case BlendMode::darken: |
| lua_pushstring(L, "darken"); |
| break; |
| case BlendMode::lighten: |
| lua_pushstring(L, "lighten"); |
| break; |
| case BlendMode::colorDodge: |
| lua_pushstring(L, "colorDodge"); |
| break; |
| case BlendMode::colorBurn: |
| lua_pushstring(L, "colorBurn"); |
| break; |
| case BlendMode::hardLight: |
| lua_pushstring(L, "hardLight"); |
| break; |
| case BlendMode::softLight: |
| lua_pushstring(L, "softLight"); |
| break; |
| case BlendMode::difference: |
| lua_pushstring(L, "difference"); |
| break; |
| case BlendMode::exclusion: |
| lua_pushstring(L, "exclusion"); |
| break; |
| case BlendMode::multiply: |
| lua_pushstring(L, "multiply"); |
| break; |
| case BlendMode::hue: |
| lua_pushstring(L, "hue"); |
| break; |
| case BlendMode::saturation: |
| lua_pushstring(L, "saturation"); |
| break; |
| case BlendMode::color: |
| lua_pushstring(L, "color"); |
| break; |
| case BlendMode::luminosity: |
| lua_pushstring(L, "luminosity"); |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| |
| void ScriptedPaint::pushFeather(lua_State* L) { lua_pushnumber(L, m_feather); } |
| void ScriptedPaint::pushGradient(lua_State* L) |
| { |
| if (m_gradient) |
| { |
| ScriptedGradient* gradient = lua_newrive<ScriptedGradient>(L); |
| gradient->shader = m_gradient; |
| } |
| else |
| { |
| lua_pushnil(L); |
| } |
| } |
| void ScriptedPaint::pushColor(lua_State* L) { lua_pushunsigned(L, m_color); } |
| |
| static int paint_index(lua_State* L) |
| { |
| int atom; |
| const char* key = lua_tostringatom(L, 2, &atom); |
| if (!key) |
| { |
| luaL_typeerrorL(L, 2, lua_typename(L, LUA_TSTRING)); |
| return 0; |
| } |
| |
| auto scriptedPaint = lua_torive<ScriptedPaint>(L, 1); |
| switch (atom) |
| { |
| case (int)LuaAtoms::style: |
| scriptedPaint->pushStyle(L); |
| return 1; |
| case (int)LuaAtoms::join: |
| scriptedPaint->pushJoin(L); |
| return 1; |
| case (int)LuaAtoms::cap: |
| scriptedPaint->pushCap(L); |
| return 1; |
| case (int)LuaAtoms::thickness: |
| scriptedPaint->pushThickness(L); |
| return 1; |
| case (int)LuaAtoms::blendMode: |
| scriptedPaint->pushBlendMode(L); |
| return 1; |
| case (int)LuaAtoms::feather: |
| scriptedPaint->pushFeather(L); |
| return 1; |
| case (int)LuaAtoms::gradient: |
| scriptedPaint->pushGradient(L); |
| return 1; |
| case (int)LuaAtoms::color: |
| scriptedPaint->pushColor(L); |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| static int paint_copy(lua_State* L) |
| { |
| int argCount = lua_gettop(L); |
| |
| auto scriptedPaint = lua_torive<ScriptedPaint>(L, 1); |
| |
| ScriptingContext* context = |
| static_cast<ScriptingContext*>(lua_getthreaddata(L)); |
| auto scriptedPaintCopy = |
| lua_newrive<ScriptedPaint>(L, context->factory(), *scriptedPaint); |
| |
| if (argCount == 2) |
| { |
| setPropertiesFromDefinitionTable(L, scriptedPaintCopy, 2); |
| } |
| return 1; |
| } |
| |
| static int paint_namecall(lua_State* L) |
| { |
| int atom; |
| const char* str = lua_namecallatom(L, &atom); |
| if (str != nullptr) |
| { |
| switch (atom) |
| { |
| case (int)LuaAtoms::copy: |
| return paint_copy(L); |
| } |
| } |
| |
| luaL_error(L, |
| "%s is not a valid method of %s", |
| str, |
| ScriptedMat2D::luaName); |
| return 0; |
| } |
| |
| static const luaL_Reg paintStaticMethods[] = { |
| {"new", paint_new}, |
| {"with", paint_with}, |
| {NULL, NULL}, |
| }; |
| |
| int luaopen_rive_paint(lua_State* L) |
| { |
| luaL_register(L, ScriptedPaint::luaName, paintStaticMethods); |
| lua_register_rive<ScriptedPaint>(L); |
| |
| lua_pushcfunction(L, paint_index, nullptr); |
| lua_setfield(L, -2, "__index"); |
| |
| lua_pushcfunction(L, paint_newindex, nullptr); |
| lua_setfield(L, -2, "__newindex"); |
| |
| lua_pushcfunction(L, paint_namecall, nullptr); |
| lua_setfield(L, -2, "__namecall"); |
| |
| lua_setreadonly(L, -1, true); |
| lua_pop(L, 1); // pop the metatable |
| return 1; |
| } |
| |
| #endif |