]> git.sesse.net Git - nageru/commitdiff
Defer creation of effects until they are added to a chain.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 20 May 2019 16:41:12 +0000 (18:41 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 22 May 2019 22:43:56 +0000 (00:43 +0200)
Right now, this is pretty much a no-op, but it is a necessary building
block for making auto-chains (chains that can specialize depending on
the conditions).

nageru/theme.cpp

index 33fa114538d046cf5049c1cbf4b8fdba1d9b909b..71cb2c322c0a0a03c39fc0d1a0f37de8e75fb693 100644 (file)
@@ -153,22 +153,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)
-{
-       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);
+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();
        }
-       luaL_error(L, "Error: Index #%d was not an Effect type\n", idx);
-       return nullptr;
+}
+
+// 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<string, int> int_parameters;
+       map<string, float> float_parameters;
+       map<string, array<float, 3>> vec3_parameters;
+       map<string, array<float, 4>> vec4_parameters;
+
+       Effect *effect = nullptr;  // Gets filled out when it's instantiated.
+};
+
+Effect *get_effect_from_blueprint(EffectChain *chain, lua_State *L, int 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");
+       }
+
+       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 +402,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 +420,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);
@@ -521,55 +605,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<WhiteBalanceEffect>(L, "WhiteBalanceEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", WHITE_BALANCE_EFFECT);
 }
 
 int ResampleEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<ResampleEffect>(L, "ResampleEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", RESAMPLE_EFFECT);
 }
 
 int PaddingEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<PaddingEffect>(L, "PaddingEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", PADDING_EFFECT);
 }
 
 int IntegralPaddingEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<IntegralPaddingEffect>(L, "IntegralPaddingEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", INTEGRAL_PADDING_EFFECT);
 }
 
 int OverlayEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<OverlayEffect>(L, "OverlayEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", OVERLAY_EFFECT);
 }
 
 int ResizeEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<ResizeEffect>(L, "ResizeEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", RESIZE_EFFECT);
 }
 
 int MultiplyEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<MultiplyEffect>(L, "MultiplyEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", MULTIPLY_EFFECT);
 }
 
 int MixEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<MixEffect>(L, "MixEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", MIX_EFFECT);
 }
 
 int LiftGammaGainEffect_new(lua_State* L)
 {
        assert(lua_gettop(L) == 0);
-       return wrap_lua_object_nonowned<LiftGammaGainEffect>(L, "LiftGammaGainEffect");
+       return wrap_lua_object_nonowned<EffectBlueprint>(L, "EffectBlueprint", LIFT_GAMMA_GAIN_EFFECT);
 }
 
 int InputStateInfo_get_width(lua_State* L)
@@ -656,63 +740,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<float, 3> 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<float, 4> 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 +848,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 +873,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 },
@@ -1161,6 +1242,7 @@ Theme::Theme(const string &filename, const vector<string> &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);