X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Ftheme.cpp;h=780c7cec3be04467d68c53061d3836deb0fac5e0;hb=ace7c97abd704ad38a3287c22ae42f05c1f9a5b4;hp=7226e82d55e50b6a7cd4dde12ed1a71ed84a5321;hpb=84eaa9862694f12f95d378abd6dd6b07a64c7b1d;p=nageru diff --git a/nageru/theme.cpp b/nageru/theme.cpp index 7226e82..780c7ce 100644 --- a/nageru/theme.cpp +++ b/nageru/theme.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,20 @@ Theme *get_theme_updata(lua_State* L) return (Theme *)lua_touserdata(L, lua_upvalueindex(1)); } +void print_warning(lua_State* L, const char *format, ...) +{ + char buf[4096]; + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + + lua_Debug ar; + lua_getstack(L, 1, &ar); + lua_getinfo(L, "nSl", &ar); + fprintf(stderr, "WARNING: %s:%d: %s", ar.source, ar.currentline, buf); +} + int ThemeMenu_set(lua_State *L) { Theme *theme = get_theme_updata(L); @@ -153,22 +168,96 @@ int wrap_lua_object_nonowned(lua_State* L, const char *class_name, Args&&... arg return 1; } -Effect *get_effect(lua_State *L, int idx) +enum EffectType { + WHITE_BALANCE_EFFECT, + RESAMPLE_EFFECT, + PADDING_EFFECT, + INTEGRAL_PADDING_EFFECT, + OVERLAY_EFFECT, + RESIZE_EFFECT, + MULTIPLY_EFFECT, + MIX_EFFECT, + LIFT_GAMMA_GAIN_EFFECT +}; + +Effect *instantiate_effect(EffectChain *chain, EffectType effect_type) +{ + switch (effect_type) { + case WHITE_BALANCE_EFFECT: + return new WhiteBalanceEffect; + case RESAMPLE_EFFECT: + return new ResampleEffect; + case PADDING_EFFECT: + return new PaddingEffect; + case INTEGRAL_PADDING_EFFECT: + return new IntegralPaddingEffect; + case OVERLAY_EFFECT: + return new OverlayEffect; + case RESIZE_EFFECT: + return new ResizeEffect; + case MULTIPLY_EFFECT: + return new MultiplyEffect; + case MIX_EFFECT: + return new MixEffect; + case LIFT_GAMMA_GAIN_EFFECT: + return new LiftGammaGainEffect; + default: + fprintf(stderr, "Unhandled effect type %d\n", effect_type); + abort(); + } +} + +// An EffectBlueprint refers to an Effect before it's being added to the graph. +// It contains enough information to instantiate the effect, including any +// parameters that were set before it was added to the graph. Once it is +// instantiated, it forwards its calls on to the real Effect instead. +struct EffectBlueprint { + EffectBlueprint(EffectType effect_type) : effect_type(effect_type) {} + + EffectType effect_type; + map int_parameters; + map float_parameters; + map> vec3_parameters; + map> vec4_parameters; + + Effect *effect = nullptr; // Gets filled out when it's instantiated. +}; + +Effect *get_effect_from_blueprint(EffectChain *chain, lua_State *L, int idx) { - if (luaL_testudata(L, idx, "WhiteBalanceEffect") || - luaL_testudata(L, idx, "ResampleEffect") || - luaL_testudata(L, idx, "PaddingEffect") || - luaL_testudata(L, idx, "IntegralPaddingEffect") || - luaL_testudata(L, idx, "OverlayEffect") || - luaL_testudata(L, idx, "ResizeEffect") || - luaL_testudata(L, idx, "MultiplyEffect") || - luaL_testudata(L, idx, "MixEffect") || - luaL_testudata(L, idx, "LiftGammaGainEffect") || - luaL_testudata(L, idx, "ImageInput")) { - return *(Effect **)lua_touserdata(L, idx); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint"); + if (blueprint->effect != nullptr) { + // NOTE: This will change in the future. + luaL_error(L, "An effect can currently only be added to one chain.\n"); } - luaL_error(L, "Error: Index #%d was not an Effect type\n", idx); - return nullptr; + + Effect *effect = instantiate_effect(chain, blueprint->effect_type); + + // Set the parameters that were deferred earlier. + for (const auto &kv : blueprint->int_parameters) { + if (!effect->set_int(kv.first, kv.second)) { + luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", kv.first.c_str(), kv.second); + } + } + for (const auto &kv : blueprint->float_parameters) { + if (!effect->set_float(kv.first, kv.second)) { + luaL_error(L, "Effect refused set_float(\"%s\", %f) (invalid key?)", kv.first.c_str(), kv.second); + } + } + for (const auto &kv : blueprint->vec3_parameters) { + if (!effect->set_vec3(kv.first, kv.second.data())) { + luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", kv.first.c_str(), + kv.second[0], kv.second[1], kv.second[2]); + } + } + for (const auto &kv : blueprint->vec4_parameters) { + if (!effect->set_vec4(kv.first, kv.second.data())) { + luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", kv.first.c_str(), + kv.second[0], kv.second[1], kv.second[2], kv.second[3]); + } + } + blueprint->effect = effect; + return effect; } InputStateInfo *get_input_state_info(lua_State *L, int idx) @@ -328,7 +417,12 @@ int EffectChain_add_effect(lua_State* L) EffectChain *chain = (EffectChain *)luaL_checkudata(L, 1, "EffectChain"); // TODO: Better error reporting. - Effect *effect = get_effect(L, 2); + Effect *effect; + if (luaL_testudata(L, 2, "ImageInput")) { + effect = *(ImageInput **)luaL_checkudata(L, 2, "ImageInput"); + } else { + effect = get_effect_from_blueprint(chain, L, 2); + } if (lua_gettop(L) == 2) { if (effect->num_inputs() == 0) { chain->add_input((Input *)effect); @@ -341,8 +435,13 @@ int EffectChain_add_effect(lua_State* L) if (luaL_testudata(L, idx, "LiveInputWrapper")) { LiveInputWrapper **input = (LiveInputWrapper **)lua_touserdata(L, idx); inputs.push_back((*input)->get_effect()); + } else if (luaL_testudata(L, idx, "ImageInput")) { + ImageInput *image = *(ImageInput **)luaL_checkudata(L, idx, "ImageInput"); + inputs.push_back(image); } else { - inputs.push_back(get_effect(L, idx)); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, idx, "EffectBlueprint"); + assert(blueprint->effect != nullptr); // Parent must be added to the graph. + inputs.push_back(blueprint->effect); } } chain->add_effect(effect, inputs); @@ -373,11 +472,7 @@ int LiveInputWrapper_connect_signal(lua_State* L) int signal_num = luaL_checknumber(L, 2); bool success = (*input)->connect_signal(signal_num); if (!success) { - lua_Debug ar; - lua_getstack(L, 1, &ar); - lua_getinfo(L, "nSl", &ar); - fprintf(stderr, "ERROR: %s:%d: Calling connect_signal() on a video or HTML input. Ignoring.\n", - ar.source, ar.currentline); + print_warning(L, "Calling connect_signal() on a video or HTML input. Ignoring.\n"); } return 0; } @@ -396,8 +491,7 @@ int VideoInput_new(lua_State* L) int pixel_format = luaL_checknumber(L, 2); if (pixel_format != bmusb::PixelFormat_8BitYCbCrPlanar && pixel_format != bmusb::PixelFormat_8BitBGRA) { - fprintf(stderr, "WARNING: Invalid enum %d used for video format, choosing Y'CbCr.\n", - pixel_format); + print_warning(L, "Invalid enum %d used for video format, choosing Y'CbCr.\n", pixel_format); pixel_format = bmusb::PixelFormat_8BitYCbCrPlanar; } int ret = wrap_lua_object_nonowned(L, "VideoInput", filename, global_flags.width, global_flags.height); @@ -521,55 +615,55 @@ int HTMLInput_get_signal_num(lua_State* L) int WhiteBalanceEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "WhiteBalanceEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", WHITE_BALANCE_EFFECT); } int ResampleEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "ResampleEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", RESAMPLE_EFFECT); } int PaddingEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "PaddingEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", PADDING_EFFECT); } int IntegralPaddingEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "IntegralPaddingEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", INTEGRAL_PADDING_EFFECT); } int OverlayEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "OverlayEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", OVERLAY_EFFECT); } int ResizeEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "ResizeEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", RESIZE_EFFECT); } int MultiplyEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "MultiplyEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", MULTIPLY_EFFECT); } int MixEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "MixEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", MIX_EFFECT); } int LiftGammaGainEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); - return wrap_lua_object_nonowned(L, "LiftGammaGainEffect"); + return wrap_lua_object_nonowned(L, "EffectBlueprint", LIFT_GAMMA_GAIN_EFFECT); } int InputStateInfo_get_width(lua_State* L) @@ -656,63 +750,94 @@ int InputStateInfo_get_last_subtitle(lua_State* L) return 1; } -int Effect_set_float(lua_State *L) +int EffectBlueprint_set_int(lua_State *L) { assert(lua_gettop(L) == 3); - Effect *effect = (Effect *)get_effect(L, 1); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint"); string key = checkstdstring(L, 2); float value = luaL_checknumber(L, 3); - if (!effect->set_float(key, value)) { - luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", key.c_str(), int(value)); + if (blueprint->effect != nullptr) { + if (!blueprint->effect->set_int(key, value)) { + luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), int(value)); + } + } else { + // TODO: check validity already here, if possible? + blueprint->int_parameters[key] = value; } return 0; } -int Effect_set_int(lua_State *L) +int EffectBlueprint_set_float(lua_State *L) { assert(lua_gettop(L) == 3); - Effect *effect = (Effect *)get_effect(L, 1); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint"); string key = checkstdstring(L, 2); float value = luaL_checknumber(L, 3); - if (!effect->set_int(key, value)) { - luaL_error(L, "Effect refused set_int(\"%s\", %d) (invalid key?)", key.c_str(), int(value)); + if (blueprint->effect != nullptr) { + if (!blueprint->effect->set_float(key, value)) { + luaL_error(L, "Effect refused set_float(\"%s\", %d) (invalid key?)", key.c_str(), int(value)); + } + } else { + // TODO: check validity already here, if possible? + blueprint->float_parameters[key] = value; } return 0; } -int Effect_set_vec3(lua_State *L) +int EffectBlueprint_set_vec3(lua_State *L) { assert(lua_gettop(L) == 5); - Effect *effect = (Effect *)get_effect(L, 1); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint"); string key = checkstdstring(L, 2); - float v[3]; + array v; v[0] = luaL_checknumber(L, 3); v[1] = luaL_checknumber(L, 4); v[2] = luaL_checknumber(L, 5); - if (!effect->set_vec3(key, v)) { - luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(), - v[0], v[1], v[2]); + + if (blueprint->effect != nullptr) { + if (!blueprint->effect->set_vec3(key, v.data())) { + luaL_error(L, "Effect refused set_vec3(\"%s\", %f, %f, %f) (invalid key?)", key.c_str(), + v[0], v[1], v[2]); + } + } else { + // TODO: check validity already here, if possible? + blueprint->vec3_parameters[key] = v; } + return 0; } -int Effect_set_vec4(lua_State *L) +int EffectBlueprint_set_vec4(lua_State *L) { assert(lua_gettop(L) == 6); - Effect *effect = (Effect *)get_effect(L, 1); + EffectBlueprint *blueprint = *(EffectBlueprint **)luaL_checkudata(L, 1, "EffectBlueprint"); string key = checkstdstring(L, 2); - float v[4]; + array v; v[0] = luaL_checknumber(L, 3); v[1] = luaL_checknumber(L, 4); v[2] = luaL_checknumber(L, 5); v[3] = luaL_checknumber(L, 6); - if (!effect->set_vec4(key, v)) { - luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(), - v[0], v[1], v[2], v[3]); + if (blueprint->effect != nullptr) { + if (!blueprint->effect->set_vec4(key, v.data())) { + luaL_error(L, "Effect refused set_vec4(\"%s\", %f, %f, %f, %f) (invalid key?)", key.c_str(), + v[0], v[1], v[2], v[3]); + } + } else { + // TODO: check validity already here, if possible? + blueprint->vec4_parameters[key] = v; } return 0; } +const luaL_Reg EffectBlueprint_funcs[] = { + // NOTE: No new() function; that's for the individual effects. + { "set_int", EffectBlueprint_set_int }, + { "set_float", EffectBlueprint_set_float }, + { "set_vec3", EffectBlueprint_set_vec3 }, + { "set_vec4", EffectBlueprint_set_vec4 }, + { NULL, NULL } +}; + const luaL_Reg EffectChain_funcs[] = { { "new", EffectChain_new }, { "__gc", EffectChain_gc }, @@ -733,10 +858,6 @@ const luaL_Reg LiveInputWrapper_funcs[] = { const luaL_Reg ImageInput_funcs[] = { { "new", ImageInput_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; @@ -762,87 +883,57 @@ const luaL_Reg HTMLInput_funcs[] = { { NULL, NULL } }; +// Effects. +// All of these are solely for new(); the returned metatable will be that of +// EffectBlueprint, and Effect (returned from add_effect()) is its own type. + const luaL_Reg WhiteBalanceEffect_funcs[] = { { "new", WhiteBalanceEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg ResampleEffect_funcs[] = { { "new", ResampleEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg PaddingEffect_funcs[] = { { "new", PaddingEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg IntegralPaddingEffect_funcs[] = { { "new", IntegralPaddingEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg OverlayEffect_funcs[] = { { "new", OverlayEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg ResizeEffect_funcs[] = { { "new", ResizeEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg MultiplyEffect_funcs[] = { { "new", MultiplyEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg MixEffect_funcs[] = { { "new", MixEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; const luaL_Reg LiftGammaGainEffect_funcs[] = { { "new", LiftGammaGainEffect_new }, - { "set_float", Effect_set_float }, - { "set_int", Effect_set_int }, - { "set_vec3", Effect_set_vec3 }, - { "set_vec4", Effect_set_vec4 }, { NULL, NULL } }; +// End of effects. + const luaL_Reg InputStateInfo_funcs[] = { { "get_width", InputStateInfo_get_width }, { "get_height", InputStateInfo_get_height }, @@ -905,11 +996,7 @@ LiveInputWrapper::LiveInputWrapper( for (unsigned i = 0; i < num_inputs; ++i) { // We upload our textures ourselves, and Movit swaps // R and B in the shader if we specify BGRA, so lie and say RGBA. - if (global_flags.can_disable_srgb_decoder) { - rgba_inputs.push_back(new sRGBSwitchingFlatInput(inout_format, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, global_flags.width, global_flags.height)); - } else { - rgba_inputs.push_back(new NonsRGBCapableFlatInput(inout_format, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, global_flags.width, global_flags.height)); - } + rgba_inputs.push_back(new sRGBSwitchingFlatInput(inout_format, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, global_flags.width, global_flags.height)); chain->add_input(rgba_inputs.back()); } @@ -1165,6 +1252,7 @@ Theme::Theme(const string &filename, const vector &search_dirs, Resource // Set up the API we provide. register_constants(); + register_class("EffectBlueprint", EffectBlueprint_funcs); register_class("EffectChain", EffectChain_funcs); register_class("LiveInputWrapper", LiveInputWrapper_funcs); register_class("ImageInput", ImageInput_funcs); @@ -1450,14 +1538,14 @@ int Theme::map_signal(int signal_num) ++card_index; } if (signal_num >= int(num_cards - 1)) { - fprintf(stderr, "WARNING: Theme asked for input %d, but we only have %u input card(s) (card %d is busy with output).\n", + print_warning(L, "Theme asked for input %d, but we only have %u input card(s) (card %d is busy with output).\n", signal_num, num_cards - 1, global_flags.output_card); fprintf(stderr, "Mapping to card %d instead.\n", card_index); } } else { card_index = signal_num % num_cards; if (signal_num >= int(num_cards)) { - fprintf(stderr, "WARNING: Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards); + print_warning(L, "Theme asked for input %d, but we only have %u card(s).\n", signal_num, num_cards); fprintf(stderr, "Mapping to card %d instead.\n", card_index); } }