+const Block *Scene::find_root_input_block(lua_State *L, const Block *block)
+{
+ if (block->is_input) {
+ assert(block->inputs.size() == 0);
+ return block;
+ }
+
+ const Block *ret = nullptr;
+ for (size_t input_idx : block->inputs) {
+ const Block *parent = find_root_input_block(L, blocks[input_idx]);
+ if (parent != nullptr) {
+ if (ret != nullptr) {
+ luaL_error(L, "add_white_balance() was connected to more than one input");
+ }
+ ret = parent;
+ }
+ }
+ return ret;
+}
+
+int Scene::add_white_balance(lua_State* L)
+{
+ assert(lua_gettop(L) >= 1);
+ Scene *scene = (Scene *)luaL_checkudata(L, 1, "Scene");
+
+ Block *block = new Block;
+ block->declaration_point = get_declaration_point(L);
+ block->idx = scene->blocks.size();
+
+ block->alternatives.push_back(new EffectBlueprint(WHITE_BALANCE_EFFECT));
+ block->alternatives.push_back(new EffectBlueprint(IDENTITY_EFFECT));
+
+ block->canonical_alternative = 1;
+
+ if (lua_gettop(L) == 1) {
+ // The last added effect is implicitly both the input and gives the white balance controller.
+ assert(!scene->blocks.empty());
+ block->inputs.push_back(scene->blocks.size() - 1);
+ block->white_balance_controller_block = scene->find_root_input_block(L, block);
+ } else if (lua_gettop(L) == 2) {
+ // The given effect is both the input and the white balance controller.
+ block->inputs.push_back(find_block_from_arg(L, scene, 2)->idx);
+ block->white_balance_controller_block = scene->find_root_input_block(L, block);
+ } else if (lua_gettop(L) == 3) {
+ // We have explicit input and white balance controller.
+ block->inputs.push_back(find_block_from_arg(L, scene, 2)->idx);
+ block->white_balance_controller_block = find_block_from_arg(L, scene, 3);
+ } else {
+ luaL_error(L, "add_white_balance([input], [white_balance_controller]) takes zero, one or two arguments");
+ }
+ if (block->white_balance_controller_block == nullptr || !block->white_balance_controller_block->is_input) {
+ luaL_error(L, "add_white_balance() does not get its white balance from an input");
+ }
+
+ scene->blocks.push_back(block);
+
+ return wrap_lua_existing_object_nonowned<Block>(L, "Block", block);
+}
+