1 
2 
3 #include "tmacrofx.h"
4 
5 // TnzBase includes
6 #include "tparamcontainer.h"
7 #include "tfxattributes.h"
8 
9 // TnzCore includes
10 #include "tstream.h"
11 
12 //--------------------------------------------------
13 
14 namespace {
15 
16 class MatchesFx {
17 public:
MatchesFx(const TFxP & fx)18   MatchesFx(const TFxP &fx) : m_fx(fx) {}
19 
operator ()(const TFxP & fx)20   bool operator()(const TFxP &fx) {
21     return m_fx.getPointer() == fx.getPointer();
22   }
23 
24   TFxP m_fx;
25 };
26 
27 //--------------------------------------------------
28 
pushParents(const TFxP & root,std::vector<TFxP> & fxs,const std::vector<TFxP> & selectedFxs)29 void pushParents(const TFxP &root, std::vector<TFxP> &fxs,
30                  const std::vector<TFxP> &selectedFxs) {
31   int i, count = root->getInputPortCount();
32   if (count == 0) {
33     std::vector<TFxP>::const_iterator found =
34         std::find_if(fxs.begin(), fxs.end(), MatchesFx(root));
35     if (found == fxs.end()) fxs.push_back(root);
36     return;
37   }
38   for (i = 0; i < count; i++) {
39     TFxP inutFx = root->getInputPort(i)->getFx();
40     std::vector<TFxP>::const_iterator found =
41         std::find_if(selectedFxs.begin(), selectedFxs.end(), MatchesFx(inutFx));
42     if (found != selectedFxs.end()) pushParents(inutFx, fxs, selectedFxs);
43   }
44   std::vector<TFxP>::const_iterator found =
45       std::find_if(fxs.begin(), fxs.end(), MatchesFx(root));
46   if (found == fxs.end()) fxs.push_back(root);
47 }
48 
49 //--------------------------------------------------
50 
sortFxs(const std::vector<TFxP> & fxs)51 std::vector<TFxP> sortFxs(const std::vector<TFxP> &fxs) {
52   std::vector<TFxP> app;
53   std::vector<TFxP> roots;
54   // find fxs that could be in back of the vector.
55   int i;
56   for (i = 0; i < (int)fxs.size(); i++) {
57     TFxP fx = fxs[i];
58     int j, count = (int)fx->getOutputConnectionCount();
59     if (count == 0) {
60       roots.push_back(fx);
61       continue;
62     }
63     for (j = 0; j < count; j++) {
64       TFxP connectedFx = fx->getOutputConnection(j)->getOwnerFx();
65       std::vector<TFxP>::const_iterator found =
66           std::find_if(fxs.begin(), fxs.end(), MatchesFx(connectedFx));
67       if (found == fxs.end()) {
68         roots.push_back(fx);
69         break;
70       }
71     }
72   }
73   for (i = 0; i < (int)roots.size(); i++) pushParents(roots[i], app, fxs);
74   assert(fxs.size() == app.size());
75   return app;
76 }
77 
78 //--------------------------------------------------
79 
80 // raccoglie tutti i parametri dai vari TFx e li assegna anche alla macro
collectParams(TMacroFx * macroFx)81 void collectParams(TMacroFx *macroFx) {
82   int k;
83   for (k = 0; k < (int)macroFx->m_fxs.size(); k++) {
84     TFxP fx = macroFx->m_fxs[k];
85     int j;
86     for (j = 0; j < fx->getParams()->getParamCount(); j++)
87       macroFx->getParams()->add(fx->getParams()->getParamVar(j)->clone());
88   }
89 }
90 
91 }  // anonymous namespace
92 
93 //--------------------------------------------------
94 
analyze(const std::vector<TFxP> & fxs,TFxP & root,std::vector<TFxP> & roots,std::vector<TFxP> & leafs)95 bool TMacroFx::analyze(const std::vector<TFxP> &fxs, TFxP &root,
96                        std::vector<TFxP> &roots, std::vector<TFxP> &leafs) {
97   if (fxs.size() == 1)
98     return false;
99   else {
100     leafs.clear();
101     roots.clear();
102     std::vector<TFxP>::const_iterator it = fxs.begin();
103     for (; it != fxs.end(); ++it) {
104       TFxP fx                      = *it;
105       int inputInternalConnection  = 0;
106       int inputExternalConnection  = 0;
107       int outputInternalConnection = 0;
108       int outputExternalConnection = 0;
109 
110       int i;
111 
112       // calcola se ci sono connessioni in input dall'esterno
113       // verso l'interno e/o internamente a orderedFxs
114       int inputPortCount = fx->getInputPortCount();
115       for (i = 0; i < inputPortCount; ++i) {
116         TFxPort *inputPort = fx->getInputPort(i);
117         TFx *inputPortFx   = inputPort->getFx();
118         if (inputPortFx) {
119           if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(inputPortFx)) !=
120               fxs.end())
121             ++inputInternalConnection;
122           else
123             ++inputExternalConnection;
124         }
125       }
126 
127       // calcola se ci sono connessioni in output dall'interno
128       // verso l'esterno e/o internamente a orderedFxs
129       int outputPortCount = fx->getOutputConnectionCount();
130       for (i = 0; i < outputPortCount; ++i) {
131         TFxPort *outputPort = fx->getOutputConnection(i);
132         TFx *outputFx       = outputPort->getOwnerFx();
133         if (outputFx) {
134           if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(outputFx)) !=
135               fxs.end())
136             ++outputInternalConnection;
137           else
138             ++outputExternalConnection;
139         }
140       }
141 
142       // se fx e' una radice
143       if ((outputExternalConnection > 0) ||
144           (outputExternalConnection == 0 && outputInternalConnection == 0)) {
145         root = fx;
146         roots.push_back(fx);
147       }
148 
149       // se fx e' una foglia
150       if (inputExternalConnection > 0 || fx->getInputPortCount() == 0 ||
151           (inputExternalConnection == 0 &&
152            inputInternalConnection < fx->getInputPortCount())) {
153         leafs.push_back(fx);
154       }
155     }
156 
157     if (roots.size() != 1)
158       return false;
159     else {
160       if (leafs.size() == 0) return false;
161     }
162 
163     return true;
164   }
165 }
166 
167 //--------------------------------------------------
168 
analyze(const std::vector<TFxP> & fxs)169 bool TMacroFx::analyze(const std::vector<TFxP> &fxs) {
170   TFxP root = 0;
171   std::vector<TFxP> leafs;
172   std::vector<TFxP> roots;
173   return analyze(fxs, root, roots, leafs);
174 }
175 
176 //--------------------------------------------------
177 
isaLeaf(TFx * fx) const178 bool TMacroFx::isaLeaf(TFx *fx) const {
179   int count = fx->getInputPortCount();
180   if (count == 0) return true;
181 
182   for (int i = 0; i < count; ++i) {
183     TFxPort *port = fx->getInputPort(i);
184     TFx *inputFx  = port->getFx();
185     if (inputFx) {
186       if (std::find_if(m_fxs.begin(), m_fxs.end(), MatchesFx(inputFx)) ==
187           m_fxs.end()) {
188         // il nodo di input non appartiene al macroFx
189         return true;
190       }
191     } else {
192       // la porta di input non e' connessa
193       return true;
194     }
195   }
196 
197   // tutte le porte di input sono connesse verso nodi appartenenti al macroFx
198   return false;
199 }
200 
201 //--------------------------------------------------
202 
TMacroFx()203 TMacroFx::TMacroFx() : m_isEditing(false) {}
204 
205 //--------------------------------------------------
206 
~TMacroFx()207 TMacroFx::~TMacroFx() {}
208 
209 //--------------------------------------------------
210 
clone(bool recursive) const211 TFx *TMacroFx::clone(bool recursive) const {
212   int n = m_fxs.size();
213   std::vector<TFxP> clones(n);
214   std::map<TFx *, int> table;
215   std::map<TFx *, int>::iterator it;
216   int i, rootIndex = -1;
217   // nodi
218   for (i = 0; i < n; ++i) {
219     TFx *fx = m_fxs[i].getPointer();
220     assert(fx);
221     clones[i] = fx->clone(false);
222     assert(table.count(fx) == 0);
223     table[fx] = i;
224     if (fx == m_root.getPointer()) rootIndex = i;
225     TFx *linkedFx = fx->getLinkedFx();
226     if (linkedFx && table.find(linkedFx) != table.end())
227       clones[i]->linkParams(clones[table[linkedFx]].getPointer());
228   }
229   assert(rootIndex >= 0);
230   // connessioni
231   for (i = 0; i < n; i++) {
232     TFx *fx = m_fxs[i].getPointer();
233     for (int j = 0; j < fx->getInputPortCount(); j++) {
234       TFxPort *port = fx->getInputPort(j);
235       TFx *inputFx  = port->getFx();
236       if (!inputFx) continue;
237       it = table.find(inputFx);
238       if (it == table.end()) {
239         // il j-esimo input di fx e' esterno alla macro
240         if (recursive)
241           clones[i]->connect(fx->getInputPortName(j), inputFx->clone(true));
242       } else {
243         // il j-esimo input di fx e' interno alla macro
244         clones[i]->connect(fx->getInputPortName(j),
245                            clones[it->second].getPointer());
246       }
247     }
248   }
249 
250   // TFx *rootClone =
251   //  const_cast<TMacroFx*>(this)->
252   //  clone(m_root.getPointer(), recursive, visited, clones);
253 
254   TMacroFx *clone = TMacroFx::create(clones);
255   clone->setName(getName());
256   clone->setFxId(getFxId());
257 
258   // Copy the index of the passive cache manager.
259   clone->getAttributes()->passiveCacheDataIdx() =
260       getAttributes()->passiveCacheDataIdx();
261 
262   assert(clone->getRoot() == clones[rootIndex].getPointer());
263 
264   return clone;
265 }
266 
267 //--------------------------------------------------
268 
doGetBBox(double frame,TRectD & bBox,const TRenderSettings & info)269 bool TMacroFx::doGetBBox(double frame, TRectD &bBox,
270                          const TRenderSettings &info) {
271   return m_root->doGetBBox(frame, bBox, info);
272 }
273 
274 //--------------------------------------------------
275 
doDryCompute(TRectD & rect,double frame,const TRenderSettings & info)276 void TMacroFx::doDryCompute(TRectD &rect, double frame,
277                             const TRenderSettings &info) {
278   assert(m_root);
279   m_root->dryCompute(rect, frame, info);
280 }
281 
282 //--------------------------------------------------
283 
doCompute(TTile & tile,double frame,const TRenderSettings & ri)284 void TMacroFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) {
285   assert(m_root);
286   m_root->compute(tile, frame, ri);
287 }
288 
289 //--------------------------------------------------
290 
getTimeRegion() const291 TFxTimeRegion TMacroFx::getTimeRegion() const {
292   return m_root->getTimeRegion();
293 }
294 
295 //--------------------------------------------------
296 
getPluginId() const297 std::string TMacroFx::getPluginId() const { return "Base"; }
298 
299 //--------------------------------------------------
300 
setRoot(TFx * root)301 void TMacroFx::setRoot(TFx *root) {
302   m_root = root;
303   // TFx::m_imp->m_outputPort = root->m_imp->m_outputPort;
304 }
305 
306 //--------------------------------------------------
307 
getRoot() const308 TFx *TMacroFx::getRoot() const { return m_root.getPointer(); }
309 
310 //--------------------------------------------------
311 
getFxById(const std::wstring & id) const312 TFx *TMacroFx::getFxById(const std::wstring &id) const {
313   int i;
314   for (i = 0; i < (int)m_fxs.size(); i++) {
315     TFx *fx = m_fxs[i].getPointer();
316     if (fx->getFxId() == id) return fx;
317   }
318   return 0;
319 }
320 
321 //--------------------------------------------------
322 
getFxs() const323 const std::vector<TFxP> &TMacroFx::getFxs() const { return m_fxs; }
324 
325 //--------------------------------------------------
326 
getMacroFxType() const327 std::string TMacroFx::getMacroFxType() const {
328   std::string name = getFxType() + "(";
329   for (int i = 0; i < (int)m_fxs.size(); i++) {
330     if (i > 0) name += ",";
331     if (TMacroFx *childMacro = dynamic_cast<TMacroFx *>(m_fxs[i].getPointer()))
332       name += childMacro->getMacroFxType();
333     else
334       name += m_fxs[i]->getFxType();
335   }
336   return name + ")";
337 }
338 
339 //--------------------------------------------------
340 
create(const std::vector<TFxP> & fxs)341 TMacroFx *TMacroFx::create(const std::vector<TFxP> &fxs) {
342   std::vector<TFxP> leafs;
343   std::vector<TFxP> roots;
344   TFxP root = 0;
345 
346   std::vector<TFxP> orederedFxs = sortFxs(fxs);
347 
348   // verifica che gli effetti selezionati siano idonei ad essere raccolti
349   // in una macro. Ci deve essere un solo nodo terminale
350   // (roots.size()==1, roots[0] == root) e uno o piu' nodi di ingresso
351   // (assert leafs.size()>0)
352   if (!analyze(orederedFxs, root, roots, leafs)) return 0;
353 
354   // -----------------------------
355 
356   TMacroFx *macroFx = new TMacroFx;
357 
358   // tutti i nodi vengono spostati (e non copiati) nella macro stessa
359   std::vector<TFxP>::const_iterator it = orederedFxs.begin();
360   for (; it != orederedFxs.end(); ++it) macroFx->m_fxs.push_back(*it);
361 
362   // i nodi di ingresso vengono messi in collegamento con le
363   // porte di ingresso della macro
364   for (int i = 0; i < (int)leafs.size(); i++) {
365     TFxP fx   = leafs[i];
366     int k     = 0;
367     int count = fx->getInputPortCount();
368     for (; k < count; k++) {
369       TFxPort *port        = fx->getInputPort(k);
370       std::string portName = fx->getInputPortName(k);
371       std::string fxId     = ::to_string(fx->getFxId());
372       portName +=
373           "_" + std::to_string(macroFx->getInputPortCount()) + "_" + fxId;
374       TFx *portFx = port->getFx();
375       if (portFx) {
376         // se la porta k-esima del nodo di ingresso i-esimo e' collegata
377         // ad un effetto, la porta viene inserita solo se l'effetto non fa
378         // gia' parte della macro
379         if (std::find_if(orederedFxs.begin(), orederedFxs.end(),
380                          MatchesFx(portFx)) == orederedFxs.end())
381           macroFx->addInputPort(portName, *port);
382       } else
383         macroFx->addInputPort(portName, *port);
384     }
385   }
386 
387   // le porte di uscita di root diventano le porte di uscita della macro
388   int count = root->getOutputConnectionCount();
389   int k     = count - 1;
390   for (; k >= 0; --k) {
391     TFxPort *port = root->getOutputConnection(k);
392     port->setFx(macroFx);
393   }
394 
395   macroFx->setRoot(root.getPointer());
396 
397   // tutti i parametri delle funzioni figlie diventano parametri della macro
398   collectParams(macroFx);
399   return macroFx;
400 }
401 
402 //--------------------------------------------------
403 
canHandle(const TRenderSettings & info,double frame)404 bool TMacroFx::canHandle(const TRenderSettings &info, double frame) {
405   return m_root->canHandle(info, frame);
406 }
407 
408 //--------------------------------------------------
409 
getAlias(double frame,const TRenderSettings & info) const410 std::string TMacroFx::getAlias(double frame,
411                                const TRenderSettings &info) const {
412   std::string alias = getFxType();
413   alias += "[";
414 
415   // alias degli effetti connessi alle porte di input separati da virgole
416   // una porta non connessa da luogo a un alias vuoto (stringa vuota)
417   int i;
418   for (i = 0; i < getInputPortCount(); i++) {
419     TFxPort *port = getInputPort(i);
420     if (port->isConnected()) {
421       TRasterFxP ifx = port->getFx();
422       assert(ifx);
423       alias += ifx->getAlias(frame, info);
424     }
425     alias += ",";
426   }
427 
428   // alias dei valori dei parametri dell'effetto al frame dato
429   for (int j = 0; j < (int)m_fxs.size(); j++) {
430     alias += (j == 0) ? "(" : ",(";
431     for (i = 0; i < m_fxs[j]->getParams()->getParamCount(); i++) {
432       if (i > 0) alias += ",";
433       TParam *param = m_fxs[j]->getParams()->getParam(i);
434       alias += param->getName() + "=" + param->getValueAlias(frame, 2);
435     }
436     alias += ")";
437   }
438 
439   alias += "]";
440   return alias;
441 }
442 
443 //--------------------------------------------------
444 
compatibilityTranslatePort(int major,int minor,std::string & portName)445 void TMacroFx::compatibilityTranslatePort(int major, int minor,
446                                           std::string &portName) {
447   // Reroute translation to the actual fx associated to the port
448   const std::string &fxId =
449       portName.substr(portName.find_last_of('_') + 1, std::string::npos);
450 
451   if (TFx *fx = getFxById(::to_wstring(fxId))) {
452     size_t opnEnd = portName.find_first_of('_');
453 
454     std::string originalPortName = portName.substr(0, opnEnd);
455     fx->compatibilityTranslatePort(major, minor, originalPortName);
456 
457     portName.replace(0, opnEnd, originalPortName);
458   }
459 
460   // Seems that at a certain point, the port name got extended...
461   if (VersionNumber(major, minor) == VersionNumber(1, 16)) {
462     for (int i = 0; i < getInputPortCount(); ++i) {
463       const std::string &name = getInputPortName(i);
464       if (name.find(portName) != std::string::npos) {
465         portName = name;
466         break;
467       }
468     }
469   }
470 }
471 
472 //--------------------------------------------------
473 
loadData(TIStream & is)474 void TMacroFx::loadData(TIStream &is) {
475   VersionNumber tnzVersion = is.getVersion();
476   std::string tagName;
477   while (is.openChild(tagName)) {
478     if (tagName == "root") {
479       // set the flag here in order to prevent the leaf macro fx in the tree
480       // to try to link this fx before finish loading
481       m_isLoading = true;
482       TPersist *p = 0;
483       is >> p;
484       m_root = dynamic_cast<TFx *>(p);
485       // release the flag
486       m_isLoading = false;
487     } else if (tagName == "nodes") {
488       while (!is.eos()) {
489         TPersist *p = 0;
490         is >> p;
491 
492         // NOTE: In current implementation p is sharedly owned by is - it's
493         // automatically
494         //       released upon stream destruction if the below assignment fails
495 
496         if (TFx *fx = dynamic_cast<TFx *>(p)) {
497           m_fxs.push_back(fx);
498         }
499       }
500       // collecting params just after loading nodes since they may need on
501       // loading "super" tag in case it is linked with another macro fx
502       collectParams(this);
503       // link parameters if there is a waiting fx for linking with this
504       if (m_waitingLinkFx) {
505         m_waitingLinkFx->linkParams(this);
506         m_waitingLinkFx = nullptr;
507       }
508     } else if (tagName == "ports") {
509       int i = 0;
510       while (is.matchTag(tagName)) {
511         if (tagName == "port") {
512           std::string name = is.getTagAttribute("name");
513           if (tnzVersion < VersionNumber(1, 16) && name != "") {
514             TRasterFxPort *port = new TRasterFxPort();
515             addInputPort(name, *port);
516           } else {
517             name = is.getTagAttribute("name_inFx");
518             if (tnzVersion < VersionNumber(1, 17) &&
519                 tnzVersion != VersionNumber(0, 0))
520               name.insert(name.find("_"), "_" + std::to_string(i));
521 
522             compatibilityTranslatePort(tnzVersion.first, tnzVersion.second,
523                                        name);
524 
525             std::string inPortName = name;
526             inPortName.erase(inPortName.find("_"), inPortName.size() - 1);
527 
528             std::string inFxId = name;
529             inFxId.erase(0, inFxId.find_last_of("_") + 1);
530 
531             for (int i = 0; i < (int)m_fxs.size(); i++) {
532               std::wstring fxId = m_fxs[i]->getFxId();
533               if (fxId == ::to_wstring(inFxId)) {
534                 if (TFxPort *port = m_fxs[i]->getInputPort(inPortName))
535                   addInputPort(name, *port);
536               }
537             }
538           }
539           i++;
540         } else
541           throw TException("unexpected tag " + tagName);
542       }
543     } else if (tagName == "super") {
544       TRasterFx::loadData(is);
545     } else
546       throw TException("unexpected tag " + tagName);
547     is.closeChild();
548   }
549 }
550 
551 //--------------------------------------------------
552 
saveData(TOStream & os)553 void TMacroFx::saveData(TOStream &os) {
554   int i;
555   os.openChild("root");
556   TPersist *p = m_root.getPointer();
557   os << p;
558   os.closeChild();
559   os.openChild("nodes");
560   for (i = 0; i < (int)m_fxs.size(); i++) {
561     TFxP fx     = m_fxs[i];
562     TPersist *p = fx.getPointer();
563     os << p;
564   }
565   os.closeChild();
566   os.openChild("ports");
567   for (i = 0; i < getInputPortCount(); i++) {
568     std::string portName = getInputPortName(i);
569     std::map<std::string, std::string> attr;
570     attr["name_inFx"] = portName;
571     os.openCloseChild("port", attr);
572   }
573   os.closeChild();
574   os.openChild("super");
575   TRasterFx::saveData(os);
576   os.closeChild();
577 }
578 
579 //--------------------------------------------------
580 
linkParams(TFx * src)581 void TMacroFx::linkParams(TFx *src) {
582   // in case the src fx is not yet loaded
583   // (i.e. we are in loading the src fx tree),
584   // wait linking the parameters until loading src is completed
585   TMacroFx *srcMacroFx = dynamic_cast<TMacroFx *>(src);
586   if (srcMacroFx && srcMacroFx->isLoading()) {
587     srcMacroFx->setWaitingLinkFx(this);
588     return;
589   }
590 
591   TFx::linkParams(src);
592 }
593 
594 //--------------------------------------------------
595 FX_IDENTIFIER(TMacroFx, "macroFx")
596 // FX_IDENTIFIER_IS_HIDDEN(TMacroFx, "macroFx")
597