#include "tmacrofx.h" // TnzBase includes #include "tparamcontainer.h" #include "tfxattributes.h" // TnzCore includes #include "tstream.h" //-------------------------------------------------- namespace { class MatchesFx { public: MatchesFx(const TFxP &fx) : m_fx(fx) {} bool operator()(const TFxP &fx) { return m_fx.getPointer() == fx.getPointer(); } TFxP m_fx; }; //-------------------------------------------------- void pushParents(const TFxP &root, std::vector &fxs, const std::vector &selectedFxs) { int i, count = root->getInputPortCount(); if (count == 0) { std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root)); if (found == fxs.end()) fxs.push_back(root); return; } for (i = 0; i < count; i++) { TFxP inutFx = root->getInputPort(i)->getFx(); std::vector::const_iterator found = std::find_if(selectedFxs.begin(), selectedFxs.end(), MatchesFx(inutFx)); if (found != selectedFxs.end()) pushParents(inutFx, fxs, selectedFxs); } std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root)); if (found == fxs.end()) fxs.push_back(root); } //-------------------------------------------------- std::vector sortFxs(const std::vector &fxs) { std::vector app; std::vector roots; // find fxs that could be in back of the vector. int i; for (i = 0; i < (int)fxs.size(); i++) { TFxP fx = fxs[i]; int j, count = (int)fx->getOutputConnectionCount(); if (count == 0) { roots.push_back(fx); continue; } for (j = 0; j < count; j++) { TFxP connectedFx = fx->getOutputConnection(j)->getOwnerFx(); std::vector::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(connectedFx)); if (found == fxs.end()) { roots.push_back(fx); break; } } } for (i = 0; i < (int)roots.size(); i++) pushParents(roots[i], app, fxs); assert(fxs.size() == app.size()); return app; } //-------------------------------------------------- // raccoglie tutti i parametri dai vari TFx e li assegna anche alla macro void collectParams(TMacroFx *macroFx) { int k; for (k = 0; k < (int)macroFx->m_fxs.size(); k++) { TFxP fx = macroFx->m_fxs[k]; int j; for (j = 0; j < fx->getParams()->getParamCount(); j++) macroFx->getParams()->add(fx->getParams()->getParamVar(j)->clone()); } } } // anonymous namespace //-------------------------------------------------- bool TMacroFx::analyze(const std::vector &fxs, TFxP &root, std::vector &roots, std::vector &leafs) { if (fxs.size() == 1) return false; else { leafs.clear(); roots.clear(); std::vector::const_iterator it = fxs.begin(); for (; it != fxs.end(); ++it) { TFxP fx = *it; int inputInternalConnection = 0; int inputExternalConnection = 0; int outputInternalConnection = 0; int outputExternalConnection = 0; int i; // calcola se ci sono connessioni in input dall'esterno // verso l'interno e/o internamente a orderedFxs int inputPortCount = fx->getInputPortCount(); for (i = 0; i < inputPortCount; ++i) { TFxPort *inputPort = fx->getInputPort(i); TFx *inputPortFx = inputPort->getFx(); if (inputPortFx) { if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(inputPortFx)) != fxs.end()) ++inputInternalConnection; else ++inputExternalConnection; } } // calcola se ci sono connessioni in output dall'interno // verso l'esterno e/o internamente a orderedFxs int outputPortCount = fx->getOutputConnectionCount(); for (i = 0; i < outputPortCount; ++i) { TFxPort *outputPort = fx->getOutputConnection(i); TFx *outputFx = outputPort->getOwnerFx(); if (outputFx) { if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(outputFx)) != fxs.end()) ++outputInternalConnection; else ++outputExternalConnection; } } // se fx e' una radice if ((outputExternalConnection > 0) || (outputExternalConnection == 0 && outputInternalConnection == 0)) { root = fx; roots.push_back(fx); } // se fx e' una foglia if (inputExternalConnection > 0 || fx->getInputPortCount() == 0 || (inputExternalConnection == 0 && inputInternalConnection < fx->getInputPortCount())) { leafs.push_back(fx); } } if (roots.size() != 1) return false; else { if (leafs.size() == 0) return false; } return true; } } //-------------------------------------------------- bool TMacroFx::analyze(const std::vector &fxs) { TFxP root = 0; std::vector leafs; std::vector roots; return analyze(fxs, root, roots, leafs); } //-------------------------------------------------- bool TMacroFx::isaLeaf(TFx *fx) const { int count = fx->getInputPortCount(); if (count == 0) return true; for (int i = 0; i < count; ++i) { TFxPort *port = fx->getInputPort(i); TFx *inputFx = port->getFx(); if (inputFx) { if (std::find_if(m_fxs.begin(), m_fxs.end(), MatchesFx(inputFx)) == m_fxs.end()) { // il nodo di input non appartiene al macroFx return true; } } else { // la porta di input non e' connessa return true; } } // tutte le porte di input sono connesse verso nodi appartenenti al macroFx return false; } //-------------------------------------------------- TMacroFx::TMacroFx() : m_isEditing(false) {} //-------------------------------------------------- TMacroFx::~TMacroFx() {} //-------------------------------------------------- TFx *TMacroFx::clone(bool recursive) const { int n = m_fxs.size(); std::vector clones(n); std::map table; std::map::iterator it; int i, rootIndex = -1; // nodi for (i = 0; i < n; ++i) { TFx *fx = m_fxs[i].getPointer(); assert(fx); clones[i] = fx->clone(false); assert(table.count(fx) == 0); table[fx] = i; if (fx == m_root.getPointer()) rootIndex = i; TFx *linkedFx = fx->getLinkedFx(); if (linkedFx && table.find(linkedFx) != table.end()) clones[i]->linkParams(clones[table[linkedFx]].getPointer()); } assert(rootIndex >= 0); // connessioni for (i = 0; i < n; i++) { TFx *fx = m_fxs[i].getPointer(); for (int j = 0; j < fx->getInputPortCount(); j++) { TFxPort *port = fx->getInputPort(j); TFx *inputFx = port->getFx(); if (!inputFx) continue; it = table.find(inputFx); if (it == table.end()) { // il j-esimo input di fx e' esterno alla macro if (recursive) clones[i]->connect(fx->getInputPortName(j), inputFx->clone(true)); } else { // il j-esimo input di fx e' interno alla macro clones[i]->connect(fx->getInputPortName(j), clones[it->second].getPointer()); } } } // TFx *rootClone = // const_cast(this)-> // clone(m_root.getPointer(), recursive, visited, clones); TMacroFx *clone = TMacroFx::create(clones); clone->setName(getName()); clone->setFxId(getFxId()); // Copy the index of the passive cache manager. clone->getAttributes()->passiveCacheDataIdx() = getAttributes()->passiveCacheDataIdx(); assert(clone->getRoot() == clones[rootIndex].getPointer()); return clone; } //-------------------------------------------------- bool TMacroFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { return m_root->doGetBBox(frame, bBox, info); } //-------------------------------------------------- void TMacroFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) { assert(m_root); m_root->dryCompute(rect, frame, info); } //-------------------------------------------------- void TMacroFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { assert(m_root); m_root->compute(tile, frame, ri); } //-------------------------------------------------- TFxTimeRegion TMacroFx::getTimeRegion() const { return m_root->getTimeRegion(); } //-------------------------------------------------- std::string TMacroFx::getPluginId() const { return "Base"; } //-------------------------------------------------- void TMacroFx::setRoot(TFx *root) { m_root = root; // TFx::m_imp->m_outputPort = root->m_imp->m_outputPort; } //-------------------------------------------------- TFx *TMacroFx::getRoot() const { return m_root.getPointer(); } //-------------------------------------------------- TFx *TMacroFx::getFxById(const std::wstring &id) const { int i; for (i = 0; i < (int)m_fxs.size(); i++) { TFx *fx = m_fxs[i].getPointer(); if (fx->getFxId() == id) return fx; } return 0; } //-------------------------------------------------- const std::vector &TMacroFx::getFxs() const { return m_fxs; } //-------------------------------------------------- std::string TMacroFx::getMacroFxType() const { std::string name = getFxType() + "("; for (int i = 0; i < (int)m_fxs.size(); i++) { if (i > 0) name += ","; if (TMacroFx *childMacro = dynamic_cast(m_fxs[i].getPointer())) name += childMacro->getMacroFxType(); else name += m_fxs[i]->getFxType(); } return name + ")"; } //-------------------------------------------------- TMacroFx *TMacroFx::create(const std::vector &fxs) { std::vector leafs; std::vector roots; TFxP root = 0; std::vector orederedFxs = sortFxs(fxs); // verifica che gli effetti selezionati siano idonei ad essere raccolti // in una macro. Ci deve essere un solo nodo terminale // (roots.size()==1, roots[0] == root) e uno o piu' nodi di ingresso // (assert leafs.size()>0) if (!analyze(orederedFxs, root, roots, leafs)) return 0; // ----------------------------- TMacroFx *macroFx = new TMacroFx; // tutti i nodi vengono spostati (e non copiati) nella macro stessa std::vector::const_iterator it = orederedFxs.begin(); for (; it != orederedFxs.end(); ++it) macroFx->m_fxs.push_back(*it); // i nodi di ingresso vengono messi in collegamento con le // porte di ingresso della macro for (int i = 0; i < (int)leafs.size(); i++) { TFxP fx = leafs[i]; int k = 0; int count = fx->getInputPortCount(); for (; k < count; k++) { TFxPort *port = fx->getInputPort(k); std::string portName = fx->getInputPortName(k); std::string fxId = ::to_string(fx->getFxId()); portName += "_" + std::to_string(macroFx->getInputPortCount()) + "_" + fxId; TFx *portFx = port->getFx(); if (portFx) { // se la porta k-esima del nodo di ingresso i-esimo e' collegata // ad un effetto, la porta viene inserita solo se l'effetto non fa // gia' parte della macro if (std::find_if(orederedFxs.begin(), orederedFxs.end(), MatchesFx(portFx)) == orederedFxs.end()) macroFx->addInputPort(portName, *port); } else macroFx->addInputPort(portName, *port); } } // le porte di uscita di root diventano le porte di uscita della macro int count = root->getOutputConnectionCount(); int k = count - 1; for (; k >= 0; --k) { TFxPort *port = root->getOutputConnection(k); port->setFx(macroFx); } macroFx->setRoot(root.getPointer()); // tutti i parametri delle funzioni figlie diventano parametri della macro collectParams(macroFx); return macroFx; } //-------------------------------------------------- bool TMacroFx::canHandle(const TRenderSettings &info, double frame) { return m_root->canHandle(info, frame); } //-------------------------------------------------- std::string TMacroFx::getAlias(double frame, const TRenderSettings &info) const { std::string alias = getFxType(); alias += "["; // alias degli effetti connessi alle porte di input separati da virgole // una porta non connessa da luogo a un alias vuoto (stringa vuota) int i; for (i = 0; i < getInputPortCount(); i++) { TFxPort *port = getInputPort(i); if (port->isConnected()) { TRasterFxP ifx = port->getFx(); assert(ifx); alias += ifx->getAlias(frame, info); } alias += ","; } // alias dei valori dei parametri dell'effetto al frame dato for (int j = 0; j < (int)m_fxs.size(); j++) { alias += (j == 0) ? "(" : ",("; for (i = 0; i < m_fxs[j]->getParams()->getParamCount(); i++) { if (i > 0) alias += ","; TParam *param = m_fxs[j]->getParams()->getParam(i); alias += param->getName() + "=" + param->getValueAlias(frame, 2); } alias += ")"; } alias += "]"; return alias; } //-------------------------------------------------- void TMacroFx::compatibilityTranslatePort(int major, int minor, std::string &portName) { // Reroute translation to the actual fx associated to the port const std::string &fxId = portName.substr(portName.find_last_of('_') + 1, std::string::npos); if (TFx *fx = getFxById(::to_wstring(fxId))) { size_t opnEnd = portName.find_first_of('_'); std::string originalPortName = portName.substr(0, opnEnd); fx->compatibilityTranslatePort(major, minor, originalPortName); portName.replace(0, opnEnd, originalPortName); } // Seems that at a certain point, the port name got extended... if (VersionNumber(major, minor) == VersionNumber(1, 16)) { for (int i = 0; i < getInputPortCount(); ++i) { const std::string &name = getInputPortName(i); if (name.find(portName) != std::string::npos) { portName = name; break; } } } } //-------------------------------------------------- void TMacroFx::loadData(TIStream &is) { VersionNumber tnzVersion = is.getVersion(); std::string tagName; while (is.openChild(tagName)) { if (tagName == "root") { // set the flag here in order to prevent the leaf macro fx in the tree // to try to link this fx before finish loading m_isLoading = true; TPersist *p = 0; is >> p; m_root = dynamic_cast(p); // release the flag m_isLoading = false; } else if (tagName == "nodes") { while (!is.eos()) { TPersist *p = 0; is >> p; // NOTE: In current implementation p is sharedly owned by is - it's // automatically // released upon stream destruction if the below assignment fails if (TFx *fx = dynamic_cast(p)) { m_fxs.push_back(fx); } } // collecting params just after loading nodes since they may need on // loading "super" tag in case it is linked with another macro fx collectParams(this); // link parameters if there is a waiting fx for linking with this if (m_waitingLinkFx) { m_waitingLinkFx->linkParams(this); m_waitingLinkFx = nullptr; } } else if (tagName == "ports") { int i = 0; while (is.matchTag(tagName)) { if (tagName == "port") { std::string name = is.getTagAttribute("name"); if (tnzVersion < VersionNumber(1, 16) && name != "") { TRasterFxPort *port = new TRasterFxPort(); addInputPort(name, *port); } else { name = is.getTagAttribute("name_inFx"); if (tnzVersion < VersionNumber(1, 17) && tnzVersion != VersionNumber(0, 0)) name.insert(name.find("_"), "_" + std::to_string(i)); compatibilityTranslatePort(tnzVersion.first, tnzVersion.second, name); std::string inPortName = name; inPortName.erase(inPortName.find("_"), inPortName.size() - 1); std::string inFxId = name; inFxId.erase(0, inFxId.find_last_of("_") + 1); for (int i = 0; i < (int)m_fxs.size(); i++) { std::wstring fxId = m_fxs[i]->getFxId(); if (fxId == ::to_wstring(inFxId)) { if (TFxPort *port = m_fxs[i]->getInputPort(inPortName)) addInputPort(name, *port); } } } i++; } else throw TException("unexpected tag " + tagName); } } else if (tagName == "super") { TRasterFx::loadData(is); } else throw TException("unexpected tag " + tagName); is.closeChild(); } } //-------------------------------------------------- void TMacroFx::saveData(TOStream &os) { int i; os.openChild("root"); TPersist *p = m_root.getPointer(); os << p; os.closeChild(); os.openChild("nodes"); for (i = 0; i < (int)m_fxs.size(); i++) { TFxP fx = m_fxs[i]; TPersist *p = fx.getPointer(); os << p; } os.closeChild(); os.openChild("ports"); for (i = 0; i < getInputPortCount(); i++) { std::string portName = getInputPortName(i); std::map attr; attr["name_inFx"] = portName; os.openCloseChild("port", attr); } os.closeChild(); os.openChild("super"); TRasterFx::saveData(os); os.closeChild(); } //-------------------------------------------------- void TMacroFx::linkParams(TFx *src) { // in case the src fx is not yet loaded // (i.e. we are in loading the src fx tree), // wait linking the parameters until loading src is completed TMacroFx *srcMacroFx = dynamic_cast(src); if (srcMacroFx && srcMacroFx->isLoading()) { srcMacroFx->setWaitingLinkFx(this); return; } TFx::linkParams(src); } //-------------------------------------------------- FX_IDENTIFIER(TMacroFx, "macroFx") // FX_IDENTIFIER_IS_HIDDEN(TMacroFx, "macroFx")