1 
2 
3 // System-core includes
4 #include "tsystem.h"  //Processors count
5 #include "timagecache.h"
6 #include "tw/stringtable.h"
7 
8 // Toonz scene-stage structures
9 #include "toonz/toonzscene.h"
10 #include "toonz/tscenehandle.h"
11 #include "toonz/sceneproperties.h"
12 #include "toonz/tframehandle.h"
13 #include "toonz/tfxhandle.h"
14 #include "toonz/tpalettehandle.h"
15 #include "toonz/txshlevel.h"
16 #include "toonz/txshlevelhandle.h"
17 #include "toonz/txsheethandle.h"
18 #include "toonz/tobjecthandle.h"
19 #include "toonz/tstageobjecttree.h"
20 #include "toonz/tcamera.h"
21 #include "toonz/palettecontroller.h"
22 #include "tapp.h"  //Toonz current objects
23 
24 // Images stuff
25 #include "trasterimage.h"
26 #include "trop.h"
27 
28 // Fxs stuff
29 #include "toutputproperties.h"
30 #include "trasterfx.h"
31 #include "toonz/scenefx.h"  //Fxs tree build-up
32 #include "toonz/tcolumnfx.h"
33 
34 // Cache management
35 #include "tpassivecachemanager.h"
36 
37 // Flipbook
38 #include "flipbook.h"
39 #include "toonzqt/flipconsole.h"
40 
41 // Qt stuff
42 #include <QMetaType>
43 #include <QRegion>
44 #include "toonzqt/gutil.h"  //For converions between TRects and QRects
45 
46 // Preferences
47 #include "toonz/preferences.h"
48 
49 #include "previewfxmanager.h"
50 
51 //=======================================================================================================
52 
53 // Resume: The 'Preview Fx' command shows a flipbook associated with given fx,
54 // containing the appearance
55 //         of the fx under current rendering status (including current camera,
56 //         preview settings, schematic tree).
57 //
58 // There are some considerations to be aware of:
59 //   1. A Preview Fx flipbook must hold the render of the whole Preview settings
60 //   range. Plus, many Preview Fx
61 //      could live altogether. It could be that more than one Preview Fx window
62 //      is active for the same fx.
63 //   2. The flipbook associated with a 'Preview Fx' command should react to
64 //   updates of the rendering status.
65 //      This should happen as long as the fx actually has a meaning in the
66 //      rendering context - that is, if
67 //      the user enters sub- or super-xsheets, the flipbook should continue
68 //      rendering with
69 //      the old data. Possibly, if modifying a sub-xsheet, an 'upper' Preview Fx
70 //      should react accordingly.
71 //   3. Fx Subtree aliases retrieved through TRasterFx::getAlias() may be used
72 //   to decide if some frame has to
73 //      be rebuilt.
74 
75 //=======================================================================================================
76 
77 //  Forward declarations
78 class PreviewFxInstance;
79 
80 //=======================================================================================================
81 
82 //==============================
83 //    Preliminary functions
84 //------------------------------
85 
86 namespace {
87 bool suspendedRendering             = false;
88 PreviewFxManager *previewerInstance = 0;
89 
90 // Timer used to deliver scene changes notifications in an 'overridden' fashion.
91 // In practice, only the last (up to a fixed time granularity) of these
92 // notifications
93 // is actually received by the manager's associated slots.
94 QTimer levelChangedTimer, fxChangedTimer, xsheetChangedTimer,
95     objectChangedTimer;
96 const int notificationDelay = 300;
97 
98 //----------------------------------------------------------------------------
99 
getCacheId(const TFxP & fx,int frame)100 inline std::string getCacheId(const TFxP &fx, int frame) {
101   return std::to_string(fx->getIdentifier()) + ".noext" + std::to_string(frame);
102 }
103 
104 //----------------------------------------------------------------------------
105 
106 // NOTE: This method will not currently trespass xsheet level boundaries. It
107 // will not
108 // recognize descendants in sub-xsheets....
areAncestorAndDescendant(const TFxP & ancestor,const TFxP & descendant)109 bool areAncestorAndDescendant(const TFxP &ancestor, const TFxP &descendant) {
110   if (ancestor.getPointer() == descendant.getPointer()) return true;
111 
112   int i;
113   for (i = 0; i < ancestor->getInputPortCount(); ++i)
114     if (areAncestorAndDescendant(ancestor->getInputPort(i)->getFx(),
115                                  descendant))
116       return true;
117 
118   return false;
119 }
120 
121 //----------------------------------------------------------------------------
122 
123 // Qt's contains actually returns QRegion::intersected... I wonder why...
contains(const QRegion & region,const TRect & rect)124 inline bool contains(const QRegion &region, const TRect &rect) {
125   return QRegion(toQRect(rect)).subtracted(region).isEmpty();
126 }
127 
128 //----------------------------------------------------------------------------
129 
adaptView(FlipBook * flipbook,TDimension cameraSize)130 inline void adaptView(FlipBook *flipbook, TDimension cameraSize) {
131   TRect imgRect(cameraSize);
132   flipbook->getImageViewer()->adaptView(imgRect, imgRect);
133 }
134 };  // namespace
135 
136 //=======================================================================================================
137 
138 //==================================
139 //    PreviewFxRenderPort class
140 //----------------------------------
141 
142 //! This class receives and handles notifications from a TRenderer executing the
143 //! preview fx
144 //! rendering job.
145 class PreviewFxRenderPort final : public QObject, public TRenderPort {
146   PreviewFxInstance *m_owner;
147 
148 public:
149   PreviewFxRenderPort(PreviewFxInstance *owner);
150   ~PreviewFxRenderPort();
151 
152   void onRenderRasterStarted(
153       const TRenderPort::RenderData &renderData) override;
154   void onRenderRasterCompleted(const RenderData &renderData) override;
155   void onRenderFailure(const RenderData &renderData, TException &e) override;
156   void onRenderFinished(bool inCanceled = false) override;
157 };
158 
159 //----------------------------------------------------------------------------------------
160 
PreviewFxRenderPort(PreviewFxInstance * owner)161 PreviewFxRenderPort::PreviewFxRenderPort(PreviewFxInstance *owner)
162     : m_owner(owner) {}
163 
164 //----------------------------------------------------------------------------------------
165 
~PreviewFxRenderPort()166 PreviewFxRenderPort::~PreviewFxRenderPort() {}
167 
168 //=======================================================================================================
169 
170 //==========================
171 //    PreviewFxInstance
172 //--------------------------
173 
174 class PreviewFxInstance {
175 public:
176   struct FrameInfo {
177     std::string m_alias;
178     QRegion m_renderedRegion;
179 
FrameInfoPreviewFxInstance::FrameInfo180     FrameInfo(const std::string &alias) : m_alias(alias) {}
181   };
182 
183 public:
184   TXsheetP m_xsheet;
185   TRasterFxP m_fx;
186   TRenderer m_renderer;
187   PreviewFxRenderPort m_renderPort;
188 
189   std::set<FlipBook *> m_flipbooks;
190   std::set<FlipBook *> m_frozenFlips;  // Used externally by PreviewFxManager
191 
192   int m_start, m_end, m_step, m_initFrame;
193   std::map<int, FrameInfo> m_frameInfos;  // Used to resume fx tree structures
194   std::vector<UCHAR> m_pbStatus;
195 
196   TRenderSettings m_renderSettings;
197   TDimension m_cameraRes;
198   TRectD m_renderArea;
199   TPointD m_cameraPos;
200   TPointD m_subcameraDisplacement;
201   bool m_subcamera;
202 
203   QRegion m_overallRenderedRegion;
204   TRect m_rectUnderRender;
205   bool m_renderFailed;
206 
207   TLevelP m_level;
208   TSoundTrackP m_snd;
209 
210 public:
211   PreviewFxInstance(TFxP fx, TXsheet *xsh);
212   ~PreviewFxInstance();
213 
214   void addFlipbook(FlipBook *&flipbook);
215   void detachFlipbook(FlipBook *flipbook);
216   void updateFlipbooks();
217 
218   void refreshViewRects(bool rebuild = false);
219 
220   void reset();
221   void reset(int frame);
222 
223   // Updater methods. These refresh the manager's status, but do not launch new
224   // renders
225   // on their own. The refreshViewRects method must be invoked to trigger it.
226   // Observe that there may exist dependencies among them - invoking in the
227   // following
228   // declaration order is safe.
229   void updateFrameRange();
230   void updateInitialFrame();
231   void updateRenderSettings();
232   void updateCamera();
233   void updateFlipbookTitles();
234   void updatePreviewRect();
235 
236   void updateAliases();
237   void updateAliasKeyword(const std::string &keyword);
238 
239   void updateProgressBarStatus();
240 
241   void onRenderRasterStarted(const TRenderPort::RenderData &renderData);
242   void onRenderRasterCompleted(const TRenderPort::RenderData &renderData);
243   void onRenderFailure(const TRenderPort::RenderData &renderData,
244                        TException &e);
245   void onRenderFinished(bool isCanceled = false);
246 
247   void doOnRenderRasterCompleted(const TRenderPort::RenderData &renderData);
248   void doOnRenderRasterStarted(const TRenderPort::RenderData &renderData);
249 
isSubCameraActive()250   bool isSubCameraActive() { return m_subcamera; }
251 
252 private:
253   void cropAndStep(int &frame);
254 
255   bool isFullPreview();
256   TFxP buildSceneFx(int frame);
257 
258   void addRenderData(std::vector<TRenderer::RenderData> &datas,
259                      ToonzScene *scene, int frame, bool rebuild);
260   void startRender(bool rebuild = false);
261 };
262 
263 //------------------------------------------------------------------
264 
isFullPreview()265 inline bool PreviewFxInstance::isFullPreview() {
266   return dynamic_cast<TOutputFx *>((TFx *)m_fx.getPointer());
267 }
268 
269 //------------------------------------------------------------------
270 
cropAndStep(int & frame)271 inline void PreviewFxInstance::cropAndStep(int &frame) {
272   frame = frame < m_start ? m_start : frame;
273   frame = (frame > m_end && m_end > -1) ? m_end : frame;
274 
275   // If a step was specified, ensure that frame is on step multiples.
276   int framePos = (frame - m_start) / m_step;
277   frame        = m_start + (framePos * m_step);
278 }
279 
280 //------------------------------------------------------------------
281 
buildSceneFx(int frame)282 inline TFxP PreviewFxInstance::buildSceneFx(int frame) {
283   TApp *app         = TApp::instance();
284   ToonzScene *scene = app->getCurrentScene()->getScene();
285 
286   if (isFullPreview())
287     return ::buildSceneFx(scene, m_xsheet.getPointer(), frame,
288                           m_renderSettings.m_shrinkX, true);
289   else
290     return ::buildPartialSceneFx(scene, m_xsheet.getPointer(), frame, m_fx,
291                                  m_renderSettings.m_shrinkX, true);
292 }
293 
294 //------------------------------------------------------------------
295 
addRenderData(std::vector<TRenderer::RenderData> & datas,ToonzScene * scene,int frame,bool rebuild)296 void PreviewFxInstance::addRenderData(std::vector<TRenderer::RenderData> &datas,
297                                       ToonzScene *scene, int frame,
298                                       bool rebuild) {
299   // Seek the image associated to the render data in the cache.
300   std::map<int, FrameInfo>::iterator it;
301   it = m_frameInfos.find(frame);
302 
303   TRasterFxP builtFx =
304       buildSceneFx(frame);  // when stereoscopic, i use this only for the alias
305   TRasterFxP builtFxA, builtFxB;
306 
307   if (m_renderSettings.m_stereoscopic) {
308     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
309     scene->shiftCameraX(-m_renderSettings.m_stereoscopicShift / 2);
310     builtFxA = buildSceneFx(frame);
311     scene->shiftCameraX(m_renderSettings.m_stereoscopicShift);
312     builtFxB = buildSceneFx(frame);
313     scene->shiftCameraX(-m_renderSettings.m_stereoscopicShift / 2);
314   } else
315     builtFxA = builtFx;
316 
317   if (it == m_frameInfos.end()) {
318     // Should not be - however, in this case build an associated Frame info
319     it = m_frameInfos.insert(std::make_pair(frame, FrameInfo(std::string())))
320              .first;
321     it->second.m_alias =
322         builtFx ? builtFx->getAlias(frame, m_renderSettings) : "";
323   }
324 
325   bool isCalculated =
326       (!builtFx) || ((!rebuild) && ::contains(it->second.m_renderedRegion,
327                                               m_rectUnderRender));
328 
329   m_pbStatus[(frame - m_start) / m_step] = isCalculated
330                                                ? FlipSlider::PBFrameFinished
331                                                : FlipSlider::PBFrameNotStarted;
332 
333   // If it is already present, return
334   if (isCalculated) return;
335 
336   datas.push_back(TRenderer::RenderData(frame, m_renderSettings,
337                                         TFxPair(builtFxA, builtFxB)));
338 }
339 
340 //-----------------------------------------------------------------------------------------
341 
PreviewFxInstance(TFxP fx,TXsheet * xsh)342 PreviewFxInstance::PreviewFxInstance(TFxP fx, TXsheet *xsh)
343     : m_renderer(TSystem::getProcessorCount())
344     , m_renderPort(this)
345     , m_fx(fx)
346     , m_cameraRes(0, 0)
347     , m_start(0)
348     , m_end(-1)
349     , m_initFrame(0)
350     , m_xsheet(xsh) {
351   // Install the render port on the instance renderer
352   m_renderer.addPort(&m_renderPort);
353 
354   updateRenderSettings();
355   updateCamera();
356   updateFrameRange();
357   updateAliases();
358 }
359 
360 //----------------------------------------------------------------------------------------
361 
~PreviewFxInstance()362 PreviewFxInstance::~PreviewFxInstance() {
363   // Stop the render - there is no need to wait for a complete (and blocking)
364   // render stop.
365   m_renderer.removePort(
366       &m_renderPort);  // No more images to be stored in the cache!
367   m_renderer.stopRendering();
368 
369   // Release the user cache about this instance
370   std::string contextName("PFX");
371   contextName += std::to_string(m_fx->getIdentifier());
372   TPassiveCacheManager::instance()->releaseContextNamesWithPrefix(contextName);
373 
374   // Clear the cached images
375   int i;
376   for (i = m_start; i <= m_end; i += m_step)
377     TImageCache::instance()->remove(getCacheId(m_fx, i));
378 }
379 
380 //-----------------------------------------------------------------------------------------
381 
382 //! Clears the preview instance informations about passed frame, including any
383 //! cached image.
384 //! Informations needed to preview again are NOT rebuilt.
reset(int frame)385 void PreviewFxInstance::reset(int frame) {
386   TImageCache::instance()->remove(getCacheId(m_fx, frame));
387   std::map<int, FrameInfo>::iterator it = m_frameInfos.find(frame);
388   if (it != m_frameInfos.end()) {
389     TRasterFxP builtFx = buildSceneFx(frame);
390     it->second.m_alias =
391         builtFx ? builtFx->getAlias(frame, m_renderSettings) : "";
392     it->second.m_renderedRegion = QRegion();
393     m_overallRenderedRegion     = QRegion();
394   }
395 }
396 
397 //-----------------------------------------------------------------------------------------
398 
399 //! Clears the preview instance informations, including cached images.
400 //! Informations needed
401 //! to preview again are rebuilt.
reset()402 void PreviewFxInstance::reset() {
403   int i;
404   for (i = m_start; i <= m_end; i += m_step)
405     TImageCache::instance()->remove(getCacheId(m_fx, i));
406 
407   m_frameInfos.clear();
408 
409   updateFrameRange();
410   updateRenderSettings();
411   updateCamera();
412   updateFlipbookTitles();
413   updateAliases();
414 }
415 
416 //-----------------------------------------------------------------------------------------
417 
addFlipbook(FlipBook * & flipbook)418 void PreviewFxInstance::addFlipbook(FlipBook *&flipbook) {
419   TApp *app         = TApp::instance();
420   ToonzScene *scene = app->getCurrentScene()->getScene();
421   TOutputProperties *properties =
422       scene->getProperties()->getPreviewProperties();
423 
424   int currFrame = flipbook ? flipbook->getCurrentFrame() - 1
425                            : app->getCurrentFrame()->getFrame();
426   cropAndStep(currFrame);
427 
428   if (!flipbook) {
429     /*-- 使用可能なFlipbookを取り出す。Poolに無い場合は新たに作る --*/
430     flipbook = FlipBookPool::instance()->pop();
431 
432     // In case this is a subcamera preview, fit the flipbook's view. This is
433     // done *before* associating
434     // m_fx to the flipbook (using Flipbook::setLevel) - so there is no
435     // duplicate view refresh.
436     /*-- Preview Settingsで"Use Sub Camera"
437      * がONのとき、サブカメラ枠の外は計算しないようになる --*/
438     if (m_subcamera) {
439       // result->adaptGeometry(TRect(previewInstance->m_cameraRes));   //This
440       // one fits the panel, too.
441       adaptView(flipbook, m_cameraRes);  // This one adapts the view. If has
442                                          // associated fx, calls refresh...
443     } else {
444       // Retrieve the eventual sub-camera
445       TCamera *currCamera = TApp::instance()
446                                 ->getCurrentScene()
447                                 ->getScene()
448                                 ->getCurrentPreviewCamera();
449       TRect subcameraRect(currCamera->getInterestRect());
450       /*-- Viewer上でサブカメラが指定されている状態でFxPreviewをした場合 --*/
451       if (subcameraRect.getLx() > 0 && subcameraRect.getLy() > 0) {
452         /*-- サブカメラ枠のShrink --*/
453         if (m_renderSettings.m_shrinkX > 1 || m_renderSettings.m_shrinkY > 1) {
454           subcameraRect.x0 /= m_renderSettings.m_shrinkX;
455           subcameraRect.y0 /= m_renderSettings.m_shrinkY;
456           subcameraRect.x1 =
457               (subcameraRect.x1 + 1) / m_renderSettings.m_shrinkX - 1;
458           subcameraRect.y1 =
459               (subcameraRect.y1 + 1) / m_renderSettings.m_shrinkY - 1;
460         }
461 
462         // Fit & pan the panel to cover the sub-camera
463         flipbook->adaptGeometry(subcameraRect, TRect(TPoint(), m_cameraRes));
464       }
465     }
466 
467     /*-- フリーズボタンの表示 --*/
468     flipbook->addFreezeButtonToTitleBar();
469   }
470 
471   m_flipbooks.insert(flipbook);
472 
473   /*-- タイトルの設定。Previewコマンドから呼ばれた場合はisFullPreviewがON --*/
474 
475   // Build the fx string description - Should really be moved in a better
476   // function...
477   std::wstring fxId;
478   TLevelColumnFx *columnFx = dynamic_cast<TLevelColumnFx *>(m_fx.getPointer());
479   TZeraryColumnFx *sfx     = dynamic_cast<TZeraryColumnFx *>(m_fx.getPointer());
480   if (columnFx)
481     fxId =
482         L"Col" + QString::number(columnFx->getColumnIndex() + 1).toStdWString();
483   else if (sfx)
484     fxId = sfx->getZeraryFx()->getFxId();
485   else {
486     fxId = m_fx->getFxId();
487     if (fxId.empty()) fxId = m_fx->getName();
488   }
489 
490   // Adjust the flipbook appropriately
491 
492   // Decorate the description for the flipbook
493   if (isFullPreview()) {
494     flipbook->setTitle(
495         /*"Rendered Frames  ::  From " + QString::number(m_start+1) +
496             " To " + QString::number(m_end+1) +
497      "  ::  Step " + QString::number(m_step)*/
498         QObject::tr("Rendered Frames  ::  From %1 To %2  ::  Step %3")
499             .arg(QString::number(m_start + 1))
500             .arg(QString::number(m_end + 1))
501             .arg(QString::number(m_step)));
502     TXsheet::SoundProperties *prop = new TXsheet::SoundProperties();
503     prop->m_frameRate              = properties->getFrameRate();
504     m_snd                          = m_xsheet->makeSound(prop);
505     if (Preferences::instance()->fitToFlipbookEnabled())
506       flipbook->getImageViewer()->adaptView(TRect(m_cameraRes),
507                                             TRect(m_cameraRes));
508 
509   } else
510     flipbook->setTitle(
511         QObject::tr("Preview FX :: %1 ").arg(QString::fromStdWString(fxId)));
512 
513   // In case the render is a full preview, add the soundtrack
514 
515   // Associate the rendered level to flipbook
516   flipbook->setLevel(m_fx.getPointer(), m_xsheet.getPointer(),
517                      m_level.getPointer(), 0, m_start + 1, m_end + 1, m_step,
518                      currFrame + 1, m_snd.getPointer());
519 
520   // Add the progress bar status pointer
521   flipbook->setProgressBarStatus(&m_pbStatus);
522 }
523 
524 //----------------------------------------------------------------------------------------
525 
detachFlipbook(FlipBook * flipbook)526 void PreviewFxInstance::detachFlipbook(FlipBook *flipbook) {
527   // Just remove the flipbook from the flipbooks container
528   std::set<FlipBook *>::iterator it = m_flipbooks.find(flipbook);
529   if (it == m_flipbooks.end()) return;
530 
531   m_flipbooks.erase(it);
532 
533   // If the flipbook set associated with the render is now empty, stop the
534   // render
535   if (m_flipbooks.empty()) m_renderer.stopRendering();
536 }
537 
538 //----------------------------------------------------------------------------------------
539 
updateFlipbooks()540 void PreviewFxInstance::updateFlipbooks() {
541   std::set<FlipBook *>::iterator it;
542   for (it = m_flipbooks.begin(); it != m_flipbooks.end(); ++it) (*it)->update();
543 }
544 
545 //----------------------------------------------------------------------------------------
546 
updateFrameRange()547 void PreviewFxInstance::updateFrameRange() {
548   TApp *app         = TApp::instance();
549   ToonzScene *scene = app->getCurrentScene()->getScene();
550   TOutputProperties *properties =
551       scene->getProperties()->getPreviewProperties();
552 
553   int frameCount = m_xsheet->getFrameCount();
554 
555   // Initialize the render starting from current frame. If not in the preview
556   // range,
557   // start from the closest range extreme.
558   properties->getRange(m_start, m_end, m_step);
559   if (m_end < 0) m_end = frameCount - 1;
560 
561   // Intersect with the fx active frame range
562   TRasterFxP rasterFx(m_fx);
563   TFxTimeRegion timeRegion(rasterFx->getTimeRegion());
564   m_start = std::max(timeRegion.getFirstFrame(), m_start);
565   m_end   = std::min(timeRegion.getLastFrame(), m_end);
566 
567   // Release all images not in the new frame range
568   std::map<int, FrameInfo>::iterator it, jt;
569   for (it = m_frameInfos.begin(); it != m_frameInfos.end();) {
570     if (it->first < m_start || it->first > m_end ||
571         ((it->first - m_start) % m_step)) {
572       TImageCache::instance()->remove(getCacheId(m_fx, it->first));
573       jt = it++;
574       m_frameInfos.erase(jt);
575     } else
576       ++it;
577   }
578 
579   // Build a level to associate the flipbook with the rendered output
580   m_level->setName(std::to_string(m_fx->getIdentifier()) + ".noext");
581   int i;
582   for (i = 0; i < frameCount; i++) m_level->setFrame(TFrameId(i), 0);
583 
584   // Resize and update internal containers
585   if (m_start > m_end) {
586     m_frameInfos.clear();
587     m_pbStatus.clear();
588   } else {
589     // Build the new frame-alias range
590     for (i = m_start; i <= m_end; i += m_step)
591       if (m_frameInfos.find(i) == m_frameInfos.end()) {
592         // Clear the overall rendered region and build the frame info
593         m_overallRenderedRegion = QRegion();
594         m_frameInfos.insert(std::make_pair(i, std::string()));
595       }
596 
597     // Resize the progress bar
598     m_pbStatus.resize((m_end - m_start) / m_step + 1);
599   }
600 
601   // Reset the flipbooks' frame range
602   std::set<FlipBook *>::iterator kt;
603   int currFrame;
604   bool fullPreview = isFullPreview();
605   for (kt = m_flipbooks.begin(); kt != m_flipbooks.end(); ++kt) {
606     currFrame = (*kt)->getCurrentFrame() - 1;
607     cropAndStep(currFrame);
608     (*kt)->setLevel(m_fx.getPointer(), m_xsheet.getPointer(),
609                     m_level.getPointer(), 0, m_start + 1, m_end + 1, m_step,
610                     currFrame + 1, m_snd.getPointer());
611   }
612 }
613 
614 //----------------------------------------------------------------------------------------
615 
updateInitialFrame()616 void PreviewFxInstance::updateInitialFrame() {
617   // Search all flipbooks and take the minimum of each's current
618   std::set<FlipBook *>::iterator kt;
619   m_initFrame = (std::numeric_limits<int>::max)();
620   for (kt = m_flipbooks.begin(); kt != m_flipbooks.end(); ++kt)
621     m_initFrame = std::min(m_initFrame, (*kt)->getCurrentFrame() - 1);
622 
623   cropAndStep(m_initFrame);
624 }
625 
626 //----------------------------------------------------------------------------------------
627 
updateFlipbookTitles()628 void PreviewFxInstance::updateFlipbookTitles() {
629   if (isFullPreview() && m_start <= m_end) {
630     int start = m_start + 1;
631     int end   = m_end + 1;
632 
633     std::set<FlipBook *>::iterator kt;
634     for (kt = m_flipbooks.begin(); kt != m_flipbooks.end(); ++kt) {
635       // In the full preview case, the title must display the frame range
636       // informations
637       (*kt)->setTitle(
638           /*"Rendered Frames  ::  From " + QString::number(start) +
639         " To " + QString::number(end) +
640  "  ::  Step " + QString::number(m_step)*/
641           QObject::tr("Rendered Frames  ::  From %1 To %2  ::  Step %3")
642               .arg(QString::number(start))
643               .arg(QString::number(end))
644               .arg(QString::number(m_step)));
645     }
646   }
647 }
648 
649 //----------------------------------------------------------------------------------------
650 
updateAliases()651 void PreviewFxInstance::updateAliases() {
652   if (m_start > m_end) return;
653 
654   std::string newAlias;
655 
656   // Build and compare the new aliases with the stored ones
657   std::map<int, FrameInfo>::iterator it;
658   for (it = m_frameInfos.begin(); it != m_frameInfos.end(); ++it) {
659     TRasterFxP builtFx = buildSceneFx(it->first);
660     newAlias = builtFx ? builtFx->getAlias(it->first, m_renderSettings) : "";
661     if (newAlias != it->second.m_alias) {
662       // Clear the overall and frame-specific rendered regions
663       m_overallRenderedRegion     = QRegion();
664       it->second.m_renderedRegion = QRegion();
665 
666       it->second.m_alias = newAlias;
667     }
668   }
669 }
670 
671 //----------------------------------------------------------------------------------------
672 
updateAliasKeyword(const std::string & keyword)673 void PreviewFxInstance::updateAliasKeyword(const std::string &keyword) {
674   if (m_start > m_end) return;
675 
676   // Remove the rendered image whose alias contains keyword
677   std::map<int, FrameInfo>::iterator it;
678   for (it = m_frameInfos.begin(); it != m_frameInfos.end(); ++it) {
679     if (it->second.m_alias.find(keyword) != std::string::npos) {
680       // Clear the overall and frame-specific rendered regions
681       m_overallRenderedRegion     = QRegion();
682       it->second.m_renderedRegion = QRegion();
683 
684       // Clear the cached image
685       TRasterImageP ri =
686           TImageCache::instance()->get(getCacheId(m_fx, it->first), true);
687       if (ri) ri->getRaster()->clear();
688     }
689   }
690 }
691 
692 //----------------------------------------------------------------------------------------
693 
updateProgressBarStatus()694 void PreviewFxInstance::updateProgressBarStatus() {
695   int i;
696   unsigned int j;
697   std::map<int, FrameInfo>::iterator it;
698   for (i = m_start, j = 0; i <= m_end; i += m_step, ++j) {
699     it            = m_frameInfos.find(i);
700     m_pbStatus[j] = ::contains(it->second.m_renderedRegion, m_rectUnderRender)
701                         ? FlipSlider::PBFrameFinished
702                         : FlipSlider::PBFrameNotStarted;
703   }
704 }
705 
706 //----------------------------------------------------------------------------------------
707 
updateRenderSettings()708 void PreviewFxInstance::updateRenderSettings() {
709   TApp *app         = TApp::instance();
710   ToonzScene *scene = app->getCurrentScene()->getScene();
711   TOutputProperties *properties =
712       scene->getProperties()->getPreviewProperties();
713 
714   m_subcamera = properties->isSubcameraPreview();
715 
716   const TRenderSettings &renderSettings = properties->getRenderSettings();
717 
718   if (m_renderSettings != renderSettings) {
719     m_renderSettings = renderSettings;
720 
721     // Erase all previuosly previewed images
722     int i;
723     for (i = m_start; i <= m_end; i += m_step)
724       TImageCache::instance()->remove(getCacheId(m_fx, i));
725 
726     // Clear all frame-specific rendered regions
727     std::map<int, FrameInfo>::iterator it;
728     for (it = m_frameInfos.begin(); it != m_frameInfos.end(); ++it)
729       it->second.m_renderedRegion = QRegion();
730   }
731 }
732 
733 //----------------------------------------------------------------------------------------
734 
updateCamera()735 void PreviewFxInstance::updateCamera() {
736   // Clear the overall rendered region
737   m_overallRenderedRegion = QRegion();
738 
739   // Retrieve the preview camera
740   TCamera *currCamera = TApp::instance()
741                             ->getCurrentScene()
742                             ->getScene()
743                             ->getCurrentPreviewCamera();
744   TRect subCameraRect = currCamera->getInterestRect();
745   TPointD cameraPos(-0.5 * currCamera->getRes().lx,
746                     -0.5 * currCamera->getRes().ly);
747 
748   // Update the camera region and camera stage area
749   TDimension cameraRes(0, 0);
750   TRectD renderArea;
751   if (m_subcamera && subCameraRect.getLx() > 0 && subCameraRect.getLy() > 0) {
752     cameraRes  = TDimension(subCameraRect.getLx(), subCameraRect.getLy());
753     renderArea = TRectD(subCameraRect.x0, subCameraRect.y0,
754                         subCameraRect.x1 + 1, subCameraRect.y1 + 1) +
755                  cameraPos;
756   } else {
757     cameraRes  = currCamera->getRes();
758     renderArea = TRectD(cameraPos, TDimensionD(cameraRes.lx, cameraRes.ly));
759   }
760 
761   cameraRes.lx /= m_renderSettings.m_shrinkX;
762   cameraRes.ly /= m_renderSettings.m_shrinkY;
763 
764   if (m_cameraRes != cameraRes || m_renderArea != renderArea) {
765     m_cameraRes  = cameraRes;
766     m_renderArea = renderArea;
767     m_cameraPos  = cameraPos;
768 
769     // Build the displacement needed when extracting the flipbooks' views
770     m_subcameraDisplacement =
771         TPointD(0.5 * (m_renderArea.x0 + m_renderArea.x1),
772                 0.5 * (m_renderArea.y0 + m_renderArea.y1));
773 
774     // Erase all previuosly previewed images
775     int i;
776     for (i = m_start; i <= m_end; i += m_step)
777       TImageCache::instance()->remove(getCacheId(m_fx, i));
778 
779     // Clear all frame-specific rendered regions
780     std::map<int, FrameInfo>::iterator it;
781     for (it = m_frameInfos.begin(); it != m_frameInfos.end(); ++it)
782       it->second.m_renderedRegion = QRegion();
783   }
784 }
785 
786 //----------------------------------------------------------------------------------------
787 
updatePreviewRect()788 void PreviewFxInstance::updatePreviewRect() {
789   bool isFullRender = false;
790   if (!m_subcamera) {
791     // Retrieve the eventual sub-camera
792     TCamera *currCamera = TApp::instance()
793                               ->getCurrentScene()
794                               ->getScene()
795                               ->getCurrentPreviewCamera();
796     TRect subcameraRect(currCamera->getInterestRect());
797 
798     /*-- Viewer上でサブカメラが指定されていない状態でFxPreviewをした場合 --*/
799     if (subcameraRect.getLx() == 0 || subcameraRect.getLy() == 0)
800       isFullRender = true;
801   }
802 
803   // Build all the viewRects to be calculated. They will be computed on
804   // consecutive
805   // render operations.
806   // NOTE: For now, we'll perform a simplicistic solution - coalesce all the
807   // flipbooks'
808   // viewrects and launch just one render.
809   TRectD previewRectD;
810   m_rectUnderRender = TRect();
811 
812   int shrinkX = m_renderSettings.m_shrinkX;
813   int shrinkY = m_renderSettings.m_shrinkY;
814 
815   if (!isFullRender) {
816     // For each opened flipbook
817     /*-- 開いているFlipbookの表示範囲を足しこんでいく --*/
818     std::set<FlipBook *>::iterator it;
819     for (it = m_flipbooks.begin(); it != m_flipbooks.end(); ++it) {
820       // Only visible flipbooks are considered
821       if ((*it)->isVisible())
822         // Retrieve the flipbook's viewRect. Observe that this image geometry
823         // is intended in shrinked image reference, and assumes that the camera
824         // center
825         // lies at coords (0.0, 0.0).
826         previewRectD += (*it)->getPreviewedImageGeometry();
827     }
828 
829     // Pass from shrinked to standard image geometry
830     /*-- いったんShrinkを元に戻す --*/
831     previewRectD.x0 *= shrinkX;
832     previewRectD.y0 *= shrinkY;
833     previewRectD.x1 *= shrinkX;
834     previewRectD.y1 *= shrinkY;
835 
836     // Now, the viewer will center the subcamera's raster instead than camera's.
837     // So, we have
838     // to correct the previewRectD by the stored displacement.
839     previewRectD += m_subcameraDisplacement;
840 
841     /*-- 表示範囲と計算範囲の共通部分を得る --*/
842     previewRectD *= m_renderArea;
843   } else
844     previewRectD = m_renderArea;
845 
846   // Ensure that rect has the same pixel geometry as the preview camera
847   /*-- 再度Shrink --*/
848   previewRectD -= m_cameraPos;
849   previewRectD.x0 = previewRectD.x0 / shrinkX;
850   previewRectD.y0 = previewRectD.y0 / shrinkY;
851   previewRectD.x1 = previewRectD.x1 / shrinkX;
852   previewRectD.y1 = previewRectD.y1 / shrinkY;
853 
854   // Now, pass to m_cameraRes-relative coordinates
855   /*-- 計算エリア基準の座標 → カメラ基準の座標 --*/
856   TPointD shrinkedRelPos((m_renderArea.x0 - m_cameraPos.x) / shrinkX,
857                          (m_renderArea.y0 - m_cameraPos.y) / shrinkY);
858   previewRectD -= shrinkedRelPos;
859 
860   previewRectD.x0 = tfloor(previewRectD.x0);
861   previewRectD.y0 = tfloor(previewRectD.y0);
862   previewRectD.x1 = tceil(previewRectD.x1);
863   previewRectD.y1 = tceil(previewRectD.y1);
864 
865   /*-- 表示しなくてはいけないRect --*/
866   QRect qViewRect(previewRectD.x0, previewRectD.y0, previewRectD.getLx(),
867                   previewRectD.getLy());
868   /*-- 表示しなくてはいけないRectから、既に計算済みの範囲を引く =
869    * 新たに計算が必要な領域 --*/
870   QRegion viewRectRegionToRender(
871       QRegion(qViewRect).subtracted(m_overallRenderedRegion));
872 
873   // If the rect to render has already been calculated, continue.
874   /*-- 新たに計算が必要な領域が無ければReturn --*/
875   if (viewRectRegionToRender.isEmpty()) return;
876 
877   // Retrieve the minimal box containing the region yet to be rendered
878   /*-- 新たに計算が必要な領域を含む最小のRectを得る --*/
879   QRect boxRectToRender(viewRectRegionToRender.boundingRect());
880 
881   /*-- 計算中のRectに登録する --*/
882   m_rectUnderRender = toTRect(boxRectToRender);
883   /*-- カメラ基準の座標 → 計算エリア基準の座標 --*/
884   previewRectD = toTRectD(boxRectToRender) + m_cameraPos + shrinkedRelPos;
885   /*-- RenderAreaをセット --*/
886   m_renderPort.setRenderArea(previewRectD);
887 }
888 
889 //----------------------------------------------------------------------------------------
890 
refreshViewRects(bool rebuild)891 void PreviewFxInstance::refreshViewRects(bool rebuild) {
892   if (suspendedRendering) return;
893 
894   if (m_flipbooks.empty()) return;
895 
896   // Stop any currently running render process. It *should not* be necessary to
897   // wait for complete stop.
898   // WARNING: This requires that different rendering instances are
899   // simultaneously
900   // supported in a single TRenderer...!! We're not currently supporting this...
901   {
902     // NOTE: stopRendering(true) LOOPS and may trigger events which delete this
903     // very
904     // render instance. So we have to watch inside the manager to see if this is
905     // still
906     // alive... The following should be removed using stopRendering(false)...
907     // NOTE: The same problem imposes that refreshViewRects() is not invoked
908     // directly
909     // when iterating the previewInstances map - we've used a signal-slot
910     // connection for this.
911     unsigned long fxId = m_fx->getIdentifier();
912 
913     m_renderer.stopRendering(true);  // Wait until we've finished
914 
915     QMap<unsigned long, PreviewFxInstance *> &previewInstances =
916         PreviewFxManager::instance()->m_previewInstances;
917     if (previewInstances.find(fxId) == previewInstances.end()) return;
918   }
919 
920   if (suspendedRendering) return;
921 
922   updatePreviewRect();
923   updateProgressBarStatus();
924   updateFlipbooks();
925 
926   startRender(rebuild);
927 }
928 
929 //----------------------------------------------------------------------------------------
930 
startRender(bool rebuild)931 void PreviewFxInstance::startRender(bool rebuild) {
932   if (m_start > m_end) return;
933 
934   // Build the rendering initial frame
935   /*-- m_initialFrameに最初に計算するフレーム番号を格納 --*/
936   updateInitialFrame();
937 
938   m_renderFailed = false;
939 
940   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
941 
942   // Fill the production-specific infos (threads count and tile size)
943   TOutputProperties *properties =
944       scene->getProperties()->getPreviewProperties();
945 
946   // Update the threads number
947   const int procCount       = TSystem::getProcessorCount();
948   const int threadCounts[3] = {1, procCount / 2, procCount};
949 
950   int index = properties->getThreadIndex();
951   m_renderer.setThreadsCount(threadCounts[index]);
952 
953   // Build raster granularity size
954   index = properties->getMaxTileSizeIndex();
955 
956   const int maxTileSizes[4] = {
957       (std::numeric_limits<int>::max)(), TOutputProperties::LargeVal,
958       TOutputProperties::MediumVal, TOutputProperties::SmallVal};
959 
960   int oldMaxTileSize             = m_renderSettings.m_maxTileSize;
961   m_renderSettings.m_maxTileSize = maxTileSizes[index];
962 
963   // Initialize the vector of TRenderer::RenderData to be rendered. The 'frame'
964   // data
965   // should be inserted first.
966   RenderDataVector *renderDatas = new RenderDataVector;
967   int i;
968   for (i = m_initFrame; i <= m_end; i += m_step)
969     addRenderData(*renderDatas, scene, i, rebuild);
970   for (i = m_start; i < m_initFrame; i += m_step)
971     addRenderData(*renderDatas, scene, i, rebuild);
972 
973   // Restore the original max tile size
974   m_renderSettings.m_maxTileSize = oldMaxTileSize;
975 
976   // Retrieve the renderId
977   unsigned long renderId = m_renderer.nextRenderId();
978   std::string contextName("PFX");
979   contextName += std::to_string(m_fx->getIdentifier());
980   TPassiveCacheManager::instance()->setContextName(renderId, contextName);
981 
982   // Finally, start rendering all frames which were not found in cache
983   m_renderer.startRendering(renderDatas);
984 }
985 
986 //----------------------------------------------------------------------------------------
987 
onRenderRasterStarted(const TRenderPort::RenderData & renderData)988 void PreviewFxRenderPort::onRenderRasterStarted(
989     const TRenderPort::RenderData &renderData) {
990   m_owner->onRenderRasterStarted(renderData);
991 }
992 
993 //----------------------------------------------------------------------------------------
994 
onRenderRasterCompleted(const RenderData & renderData)995 void PreviewFxRenderPort::onRenderRasterCompleted(
996     const RenderData &renderData) {
997   /*-- Do not show the result if canceled while rendering --*/
998   if (renderData.m_info.m_isCanceled && *renderData.m_info.m_isCanceled) {
999     // set m_renderFailed to true in order to prevent updating
1000     // m_overallRenderedRegion at PreviewFxInstance::onRenderFinished().
1001     m_owner->m_renderFailed = true;
1002     return;
1003   }
1004 
1005   m_owner->onRenderRasterCompleted(renderData);
1006 }
1007 
1008 //----------------------------------------------------------------------------------------
1009 
onRenderFailure(const RenderData & renderData,TException & e)1010 void PreviewFxRenderPort::onRenderFailure(const RenderData &renderData,
1011                                           TException &e) {
1012   m_owner->onRenderFailure(renderData, e);
1013 }
1014 
1015 //----------------------------------------------------------------------------------------
1016 
onRenderFinished(bool isCanceled)1017 void PreviewFxRenderPort::onRenderFinished(bool isCanceled) {
1018   m_owner->onRenderFinished(isCanceled);
1019 }
1020 
1021 //----------------------------------------------------------------------------------------
1022 
onRenderRasterStarted(const TRenderPort::RenderData & renderData)1023 void PreviewFxInstance::onRenderRasterStarted(
1024     const TRenderPort::RenderData &renderData) {
1025   PreviewFxManager::instance()->emitStartedFrame(m_fx->getIdentifier(),
1026                                                  renderData);
1027 }
1028 
1029 //----------------------------------------------------------------------------------------
1030 
onRenderRasterCompleted(const TRenderPort::RenderData & renderData)1031 void PreviewFxInstance::onRenderRasterCompleted(
1032     const TRenderPort::RenderData &renderData) {
1033   PreviewFxManager::instance()->emitRenderedFrame(m_fx->getIdentifier(),
1034                                                   renderData);
1035 }
1036 
1037 //----------------------------------------------------------------------------------------
1038 
1039 // Update the progress bar status to show the frame has started
doOnRenderRasterStarted(const TRenderPort::RenderData & renderData)1040 void PreviewFxInstance::doOnRenderRasterStarted(
1041     const TRenderPort::RenderData &renderData) {
1042   unsigned int i, size = renderData.m_frames.size();
1043   for (i = 0; i < size; ++i)
1044     // Update the pb status for each cluster's frame
1045     m_pbStatus[(renderData.m_frames[i] - m_start) / m_step] =
1046         FlipSlider::PBFrameStarted;
1047 
1048   /*-- 計算中の赤枠を表示する --*/
1049   std::set<FlipBook *>::iterator it;
1050   for (it = m_flipbooks.begin(); it != m_flipbooks.end(); ++it)
1051     (*it)->setIsRemakingPreviewFx(true);
1052 
1053   updateFlipbooks();
1054 }
1055 
1056 //----------------------------------------------------------------------------------------
1057 
1058 // Show the rendered frame if it is some flipbook's current
doOnRenderRasterCompleted(const TRenderPort::RenderData & renderData)1059 void PreviewFxInstance::doOnRenderRasterCompleted(
1060     const TRenderPort::RenderData &renderData) {
1061   std::string cacheId(getCacheId(m_fx, renderData.m_frames[0]));
1062 
1063   TRasterImageP ri(TImageCache::instance()->get(cacheId, true));
1064   TRasterP ras;
1065   if (ri)
1066     ras = ri->getRaster();
1067   else
1068     ras = 0;
1069 
1070   /*-- 16bpcで計算された場合、結果をDitheringする --*/
1071   TRasterP rasA = renderData.m_rasA;
1072   TRasterP rasB = renderData.m_rasB;
1073   if (rasA->getPixelSize() == 8)  // render in 64 bits
1074   {
1075     TRaster32P auxA(rasA->getLx(), rasA->getLy());
1076     TRop::convert(auxA, rasA);  // dithering
1077     rasA = auxA;
1078     if (m_renderSettings.m_stereoscopic) {
1079       assert(rasB);
1080       TRaster32P auxB(rasB->getLx(), rasB->getLy());
1081       TRop::convert(auxB, rasB);  // dithering
1082       rasB = auxB;
1083     }
1084   }
1085 
1086   if (!ras || (ras->getSize() != m_cameraRes)) {
1087     TImageCache::instance()->remove(cacheId);
1088 
1089     // Create the raster at camera resolution
1090     ras = rasA->create(m_cameraRes.lx, m_cameraRes.ly);
1091     ras->clear();
1092     ri = TRasterImageP(ras);
1093   }
1094 
1095   // Finally, copy the rendered raster over the cached one
1096   TRect rectUnderRender(
1097       m_rectUnderRender);  // Extract may MODIFY IT! E.g. with shrinks..!
1098   ras = ras->extract(rectUnderRender);
1099   if (ras) {
1100     if (m_renderSettings.m_stereoscopic) {
1101       assert(rasB);
1102       TRop::makeStereoRaster(rasA, rasB);
1103     }
1104     ras->copy(rasA);
1105   }
1106   // Submit the image to the cache, for all cluster's frames
1107   unsigned int i, size = renderData.m_frames.size();
1108   for (i = 0; i < size; ++i) {
1109     int frame = renderData.m_frames[i];
1110     TImageCache::instance()->add(getCacheId(m_fx, frame), ri);
1111 
1112     // Update the pb status
1113     int pbIndex = (frame - m_start) / m_step;
1114     if (pbIndex >= 0 && pbIndex < (int)m_pbStatus.size())
1115       m_pbStatus[pbIndex] = FlipSlider::PBFrameFinished;
1116 
1117     // Update the frame-specific rendered region
1118     std::map<int, FrameInfo>::iterator jt = m_frameInfos.find(frame);
1119     assert(jt != m_frameInfos.end());
1120     jt->second.m_renderedRegion += toQRect(m_rectUnderRender);
1121 
1122     std::set<FlipBook *>::iterator it;
1123     int renderedFrame = frame + 1;
1124     for (it = m_flipbooks.begin(); it != m_flipbooks.end(); ++it)
1125       if ((*it)->getCurrentFrame() == renderedFrame)
1126         (*it)->showFrame(renderedFrame);
1127   }
1128 
1129   updateFlipbooks();
1130 }
1131 
1132 //----------------------------------------------------------------------------------------
1133 
onRenderFailure(const TRenderPort::RenderData & renderData,TException & e)1134 void PreviewFxInstance::onRenderFailure(
1135     const TRenderPort::RenderData &renderData, TException &e) {
1136   m_renderFailed = true;
1137 
1138   // Update each frame status
1139   unsigned int i, size = renderData.m_frames.size();
1140   for (i = 0; i < size; ++i) {
1141     int frame = renderData.m_frames[i];
1142 
1143     // Update the pb status
1144     int pbIndex = (frame - m_start) / m_step;
1145     if (pbIndex >= 0 && pbIndex < (int)m_pbStatus.size())
1146       m_pbStatus[pbIndex] = FlipSlider::PBFrameNotStarted;
1147   }
1148 }
1149 
1150 //----------------------------------------------------------------------------------------
1151 
onRenderFinished(bool isCanceled)1152 void PreviewFxInstance::onRenderFinished(bool isCanceled) {
1153   // Update the rendered region
1154   if (!m_renderFailed && !isCanceled)
1155     m_overallRenderedRegion += toQRect(m_rectUnderRender);
1156 
1157   /*-- 計算中の赤枠の表示を消す --*/
1158   std::set<FlipBook *>::iterator it;
1159   for (it = m_flipbooks.begin(); it != m_flipbooks.end(); ++it)
1160     (*it)->setIsRemakingPreviewFx(false);
1161 }
1162 
1163 //=======================================================================================================
1164 
1165 //=========================
1166 //    PreviewFxManager
1167 //-------------------------
1168 
PreviewFxManager()1169 PreviewFxManager::PreviewFxManager() : QObject() {
1170   TApp *app = TApp::instance();
1171   qRegisterMetaType<unsigned long>("unsigned long");
1172   qRegisterMetaType<TRenderPort::RenderData>("TRenderPort::RenderData");
1173 
1174   /*-- Rendering終了時、各RenderPortからEmit → Flipbookの更新を行う --*/
1175   connect(this, SIGNAL(renderedFrame(unsigned long, TRenderPort::RenderData)),
1176           this, SLOT(onRenderedFrame(unsigned long, TRenderPort::RenderData)));
1177   /*-- Rendering開始時、各RenderPortからEmit →
1178    * Flipbookのプログレスバーのステータスを「計算中」にする --*/
1179   connect(this, SIGNAL(startedFrame(unsigned long, TRenderPort::RenderData)),
1180           this, SLOT(onStartedFrame(unsigned long, TRenderPort::RenderData)));
1181 
1182   // connect(app->getPaletteController()->getCurrentPalette(),
1183   // SIGNAL(colorStyleChangedOnMouseRelease()),SLOT(onLevelChanged()));
1184   // connect(app->getPaletteController()->getCurrentPalette(),
1185   // SIGNAL(paletteChanged()),   SLOT(onLevelChanged()));
1186   connect(app->getPaletteController()->getCurrentLevelPalette(),
1187           SIGNAL(colorStyleChangedOnMouseRelease()), SLOT(onLevelChanged()));
1188   connect(app->getPaletteController()->getCurrentLevelPalette(),
1189           SIGNAL(paletteChanged()), SLOT(onLevelChanged()));
1190 
1191   connect(app->getCurrentLevel(), SIGNAL(xshLevelChanged()), this,
1192           SLOT(onLevelChanged()));
1193   connect(app->getCurrentFx(), SIGNAL(fxChanged()), this, SLOT(onFxChanged()));
1194   connect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()), this,
1195           SLOT(onXsheetChanged()));
1196   connect(app->getCurrentObject(), SIGNAL(objectChanged(bool)), this,
1197           SLOT(onObjectChanged(bool)));
1198 
1199   /*-- 上記の on○○Changed() は、全て refreshViewRects をEmitしている。
1200           → これまでの計算を止め、新たにstartRenderをする。
1201           (Qt::QueuedConnection
1202   は、イベントループの手が空いた時に初めてSLOTを呼ぶ、ということ)
1203   --*/
1204   // Due to current implementation of PreviewFxInstance::refreshViewRects().
1205   connect(this, SIGNAL(refreshViewRects(unsigned long)), this,
1206           SLOT(onRefreshViewRects(unsigned long)), Qt::QueuedConnection);
1207 
1208   previewerInstance = this;
1209 }
1210 
1211 //-----------------------------------------------------------------------------
1212 
~PreviewFxManager()1213 PreviewFxManager::~PreviewFxManager() {}
1214 
1215 //-----------------------------------------------------------------------------
1216 
instance()1217 PreviewFxManager *PreviewFxManager::instance() {
1218   static PreviewFxManager _instance;
1219   return &_instance;
1220 }
1221 
1222 //-----------------------------------------------------------------------------
1223 
showNewPreview(TFxP fx,bool forceFlipbook)1224 FlipBook *PreviewFxManager::showNewPreview(TFxP fx, bool forceFlipbook) {
1225   if (!fx) return 0;
1226 
1227   /*-- fxIdは、Fxの作成ごとに1つずつインクリメントして割り振られる数字 --*/
1228   unsigned long fxId                 = fx->getIdentifier();
1229   PreviewFxInstance *previewInstance = 0;
1230 
1231   /*-- PreviewFxInstanceをFxごとに作成する --*/
1232   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1233       m_previewInstances.find(fxId);
1234   if (it == m_previewInstances.end()) {
1235     TXsheet *xsh    = TApp::instance()->getCurrentXsheet()->getXsheet();
1236     previewInstance = new PreviewFxInstance(fx, xsh);
1237     m_previewInstances.insert(fxId, previewInstance);
1238   } else {
1239     previewInstance = it.value();
1240     /*-- 以前PreviewしたことのあるFxを、再度Previewしたとき --*/
1241     if (!forceFlipbook &&
1242         !Preferences::instance()->previewAlwaysOpenNewFlipEnabled()) {
1243       // Search the first visible flipbook to be raised. If not found, add a new
1244       // one.
1245       /*--
1246        * そのFxに関連付けられたFlipbookがあり、かつVisibleな場合は、reset()で再計算
1247        * --*/
1248       std::set<FlipBook *> &flipbooks = previewInstance->m_flipbooks;
1249       std::set<FlipBook *>::iterator jt;
1250       for (jt = flipbooks.begin(); jt != flipbooks.end(); ++jt)
1251         if ((*jt)->isVisible()) {
1252           reset(fx);  // Also recalculate the preview
1253           (*jt)->parentWidget()->raise();
1254           return 0;
1255         }
1256     }
1257   }
1258 
1259   FlipBook *result = 0;
1260   /*-- resultに必要なFlipbookを格納し、setLevelをする --*/
1261   previewInstance->addFlipbook(result);
1262 
1263   /*-- Flipbookのクローン時以外は forceFlipbookがfalse --*/
1264   if (!forceFlipbook) /*-- startRenderを実行 --*/
1265     previewInstance->refreshViewRects();
1266 
1267   return result;
1268 }
1269 
1270 //-----------------------------------------------------------------------------
1271 /*! return true if the preview fx instance for specified fx is with sub-camera
1272  * activated
1273  */
1274 
isSubCameraActive(TFxP fx)1275 bool PreviewFxManager::isSubCameraActive(TFxP fx) {
1276   if (!fx) return false;
1277 
1278   unsigned long fxId = fx->getIdentifier();
1279   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1280       m_previewInstances.find(fxId);
1281   if (it == m_previewInstances.end()) return false;
1282 
1283   return it.value()->isSubCameraActive();
1284 }
1285 
1286 //-----------------------------------------------------------------------------
1287 
refreshView(TFxP fx)1288 void PreviewFxManager::refreshView(TFxP fx) {
1289   if (!fx) return;
1290 
1291   unsigned long fxId = fx->getIdentifier();
1292   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1293       m_previewInstances.find(fxId);
1294   if (it == m_previewInstances.end()) return;
1295 
1296   it.value()->refreshViewRects();
1297 }
1298 
1299 //-----------------------------------------------------------------------------
1300 
1301 //! This slot is necessary to prevent problems with current implementation of
1302 //! the
1303 //! event-looping PreviewFxInstance::refreshViewRects function.
onRefreshViewRects(unsigned long id)1304 void PreviewFxManager::onRefreshViewRects(unsigned long id) {
1305   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1306       m_previewInstances.find(id);
1307   if (it != m_previewInstances.end()) it.value()->refreshViewRects();
1308 }
1309 
1310 //-----------------------------------------------------------------------------
1311 
unfreeze(FlipBook * flipbook)1312 void PreviewFxManager::unfreeze(FlipBook *flipbook) {
1313   TFxP fx(flipbook->getPreviewedFx());
1314   if (!fx) return;
1315 
1316   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1317       m_previewInstances.find(fx->getIdentifier());
1318   if (it == m_previewInstances.end()) return;
1319 
1320   PreviewFxInstance *previewInstance = it.value();
1321   std::set<FlipBook *> &frozenFlips  = previewInstance->m_frozenFlips;
1322 
1323   std::set<FlipBook *>::iterator jt = frozenFlips.find(flipbook);
1324   if (jt == frozenFlips.end()) return;
1325 
1326   // Re-attach to the preview instance
1327   {
1328     frozenFlips.erase(jt);
1329 
1330     // Before attaching, remove any old flipbook level from the cache
1331     flipbook->clearCache();
1332 
1333     // Also any associated pb status
1334     delete flipbook->getProgressBarStatus();
1335     flipbook->setProgressBarStatus(NULL);
1336 
1337     previewInstance->addFlipbook(flipbook);
1338 
1339     // recompute frames, if necessary (call the same process as
1340     // PreviewFxManager::onXsheetChanged())
1341     previewInstance->updateRenderSettings();
1342     previewInstance->updateCamera();
1343     previewInstance->updateFrameRange();
1344     previewInstance->updateFlipbookTitles();
1345     previewInstance->updateAliases();
1346 
1347     previewInstance->refreshViewRects();
1348   }
1349 }
1350 
1351 //-----------------------------------------------------------------------------
1352 
1353 //! Observe that detached flipbooks which maintain the previewed images also
1354 //! maintain the internal reference
1355 //! to the previewed fx returned by the FlipBook::getPreviewedFx() method, so
1356 //! the flipbook may be re-attached
1357 //! (ie un-freezed) to the same preview fx.
freeze(FlipBook * flipbook)1358 void PreviewFxManager::freeze(FlipBook *flipbook) {
1359   // Retrieve its previewed fx
1360   TFxP fx(flipbook->getPreviewedFx());
1361   if (!fx) return;
1362 
1363   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1364       m_previewInstances.find(fx->getIdentifier());
1365   if (it == m_previewInstances.end()) return;
1366 
1367   PreviewFxInstance *previewInstance = it.value();
1368 
1369   // First off, detach the flipbook from the preview instance and
1370   // insert it among the instance's frozen ones
1371   previewInstance->detachFlipbook(flipbook);
1372   previewInstance->m_frozenFlips.insert(flipbook);
1373 
1374   // Then, perform the level copy
1375   {
1376     std::string levelName("freezed" + std::to_string(flipbook->getPoolIndex()) +
1377                           ".noext");
1378     int i;
1379 
1380     // Clone the preview images
1381     for (i = previewInstance->m_start; i <= previewInstance->m_end;
1382          i += previewInstance->m_step) {
1383       TImageP cachedImage =
1384           TImageCache::instance()->get(getCacheId(fx, i), false);
1385       if (cachedImage)
1386         TImageCache::instance()->add(levelName + std::to_string(i),
1387                                      cachedImage->cloneImage());
1388     }
1389 
1390     // Associate a level with the cached images
1391     TLevelP freezedLevel;
1392     freezedLevel->setName(levelName);
1393     for (i = 0; i < previewInstance->m_level->getFrameCount(); ++i)
1394       freezedLevel->setFrame(TFrameId(i), 0);
1395     flipbook->setLevel(fx.getPointer(), previewInstance->m_xsheet.getPointer(),
1396                        freezedLevel.getPointer(), 0,
1397                        previewInstance->m_start + 1, previewInstance->m_end + 1,
1398                        previewInstance->m_step, flipbook->getCurrentFrame(),
1399                        previewInstance->m_snd.getPointer());
1400 
1401     // Also, the associated PB must be cloned
1402     std::vector<UCHAR> *newPBStatuses = new std::vector<UCHAR>;
1403     *newPBStatuses                    = previewInstance->m_pbStatus;
1404     flipbook->setProgressBarStatus(newPBStatuses);
1405 
1406     // Traverse the PB: frames under rendering must be signed as uncompleted
1407     std::vector<UCHAR>::iterator it;
1408     for (it = newPBStatuses->begin(); it != newPBStatuses->end(); ++it)
1409       if (*it == FlipSlider::PBFrameStarted)
1410         *it = FlipSlider::PBFrameNotStarted;
1411   }
1412 }
1413 
1414 //-----------------------------------------------------------------------------
1415 
detach(FlipBook * flipbook)1416 void PreviewFxManager::detach(FlipBook *flipbook) {
1417   // Retrieve its previewed fx
1418   TFxP fx(flipbook->getPreviewedFx());
1419   if (!fx) return;
1420 
1421   // Search the flip among attached ones
1422   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1423       m_previewInstances.find(fx->getIdentifier());
1424   if (it == m_previewInstances.end()) return;
1425 
1426   PreviewFxInstance *previewInstance = it.value();
1427 
1428   // Detach the flipbook (does nothing if flipbook is frozen)
1429   previewInstance->detachFlipbook(flipbook);
1430 
1431   // Eventually, it could be in frozens; detach it from there too
1432   std::set<FlipBook *> &frozenFlips = previewInstance->m_frozenFlips;
1433 
1434   std::set<FlipBook *>::iterator jt = frozenFlips.find(flipbook);
1435   if (jt != frozenFlips.end()) {
1436     flipbook->clearCache();
1437     delete flipbook->getProgressBarStatus();
1438     frozenFlips.erase(jt);
1439   }
1440 
1441   // Finally, delete the preview instance if no flipbook (active or frozen)
1442   // remain
1443   if (previewInstance->m_flipbooks.empty() &&
1444       previewInstance->m_frozenFlips.empty()) {
1445     delete it.value();
1446     m_previewInstances.erase(it);
1447   }
1448 }
1449 
1450 //-----------------------------------------------------------------------------
1451 
onLevelChanged()1452 void PreviewFxManager::onLevelChanged() {
1453   if (m_previewInstances.size()) {
1454     // Build the level name as an alias keyword. All cache images associated
1455     // with an alias containing the level name will be updated.
1456     TXshLevel *xl = TApp::instance()->getCurrentLevel()->getLevel();
1457     std::string aliasKeyword;
1458     TFilePath fp = xl->getPath();
1459     aliasKeyword = ::to_string(fp.withType(""));
1460 
1461     QMap<unsigned long, PreviewFxInstance *>::iterator it;
1462     for (it = m_previewInstances.begin(); it != m_previewInstances.end();
1463          ++it) {
1464       it.value()->updateAliasKeyword(aliasKeyword);
1465       emit refreshViewRects(it.key());
1466       // it.value()->refreshViewRects();
1467     }
1468   }
1469 }
1470 
1471 //-----------------------------------------------------------------------------
1472 
onFxChanged()1473 void PreviewFxManager::onFxChanged() {
1474   // Examinate all RenderInstances for ancestors of current fx
1475   if (m_previewInstances.size()) {
1476     TFxP fx = TApp::instance()->getCurrentFx()->getFx();
1477 
1478     QMap<unsigned long, PreviewFxInstance *>::iterator it;
1479     for (it = m_previewInstances.begin(); it != m_previewInstances.end(); ++it)
1480     // if(areAncestorAndDescendant(it.value()->m_fx, fx))  //Currently not
1481     // trespassing sub-xsheet boundaries
1482     {
1483       // in case the flipbook is frozen
1484       if (it.value()->m_flipbooks.empty()) continue;
1485       it.value()->updateAliases();
1486       emit refreshViewRects(it.key());
1487       // it.value()->refreshViewRects();
1488     }
1489   }
1490 }
1491 
1492 //-----------------------------------------------------------------------------
1493 
onXsheetChanged()1494 void PreviewFxManager::onXsheetChanged() {
1495   // Update all rendered frames, if necessary
1496   if (m_previewInstances.size()) {
1497     QMap<unsigned long, PreviewFxInstance *>::iterator it;
1498     for (it = m_previewInstances.begin(); it != m_previewInstances.end();
1499          ++it) {
1500       // in case the flipbook is frozen
1501       if (it.value()->m_flipbooks.empty()) continue;
1502       it.value()->updateRenderSettings();
1503       it.value()->updateCamera();
1504       it.value()->updateFrameRange();
1505       it.value()->updateFlipbookTitles();
1506       it.value()->updateAliases();
1507       emit refreshViewRects(it.key());
1508       // it.value()->refreshViewRects();
1509     }
1510   }
1511 }
1512 
1513 //-----------------------------------------------------------------------------
1514 
onObjectChanged(bool isDragging)1515 void PreviewFxManager::onObjectChanged(bool isDragging) {
1516   if (isDragging) return;
1517   // Update all rendered frames, if necessary
1518   if (m_previewInstances.size()) {
1519     QMap<unsigned long, PreviewFxInstance *>::iterator it;
1520     for (it = m_previewInstances.begin(); it != m_previewInstances.end();
1521          ++it) {
1522       // in case the flipbook is frozen
1523       if (it.value()->m_flipbooks.empty()) continue;
1524       it.value()->updateFrameRange();
1525       it.value()->updateFlipbookTitles();
1526       it.value()->updateAliases();
1527       emit refreshViewRects(it.key());
1528       // it.value()->refreshViewRects();
1529     }
1530   }
1531 }
1532 
1533 //-----------------------------------------------------------------------------
1534 /*--
1535  * 既にPreviewしたことがあり、Flipbookが開いているFxを再度Previewしたときに実行される
1536  * --*/
reset(TFxP fx)1537 void PreviewFxManager::reset(TFxP fx) {
1538   if (!fx) return;
1539 
1540   unsigned long fxId = fx->getIdentifier();
1541   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1542       m_previewInstances.find(fxId);
1543   if (it == m_previewInstances.end()) return;
1544 
1545   it.value()->m_renderer.stopRendering(true);
1546 
1547   // stopRendering(true) LOOPS and may destroy the preview instance. Recheck for
1548   // its presence
1549   it = m_previewInstances.find(fxId);
1550   if (it != m_previewInstances.end()) {
1551     it.value()->reset();
1552     it.value()->refreshViewRects();
1553   }
1554 }
1555 
1556 //-----------------------------------------------------------------------------
1557 
reset(TFxP fx,int frame)1558 void PreviewFxManager::reset(TFxP fx, int frame) {
1559   if (!fx) return;
1560 
1561   unsigned long fxId = fx->getIdentifier();
1562   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1563       m_previewInstances.find(fxId);
1564   if (it == m_previewInstances.end()) return;
1565 
1566   if (it.value()->m_start > it.value()->m_end) return;
1567 
1568   it.value()->m_renderer.stopRendering(true);
1569 
1570   // stopRendering(true) LOOPS and may destroy the preview instance. Recheck for
1571   // its presence
1572   it = m_previewInstances.find(fxId);
1573   if (it != m_previewInstances.end()) {
1574     it.value()->reset(frame);
1575     it.value()->refreshViewRects();
1576   }
1577 }
1578 
1579 //-----------------------------------------------------------------------------
1580 
reset(bool detachFlipbooks)1581 void PreviewFxManager::reset(bool detachFlipbooks) {
1582   // Hard copy the instances pointers
1583   QMap<unsigned long, PreviewFxInstance *> previewInstances =
1584       m_previewInstances;
1585 
1586   QMap<unsigned long, PreviewFxInstance *>::iterator it;
1587   for (it = previewInstances.begin(); it != previewInstances.end(); ++it) {
1588     // Just like the above, stopRendering(true) event-LOOPS...
1589     it.value()->m_renderer.stopRendering(true);
1590 
1591     if (m_previewInstances.find(it.key()) == m_previewInstances.end()) continue;
1592 
1593     if (detachFlipbooks) {
1594       // Reset all associated flipbooks
1595       PreviewFxInstance *previewInstance = it.value();
1596 
1597       // Hard copy, since detach manipulates the original
1598       std::set<FlipBook *> flipbooks = previewInstance->m_flipbooks;
1599 
1600       std::set<FlipBook *>::iterator jt;
1601       for (jt = flipbooks.begin(); jt != flipbooks.end(); ++jt) {
1602         // Detach and reset the flipbook
1603         (*jt)->reset();  // The detachment happens in here
1604       }
1605 
1606       /*- Frozen
1607        * Flipがひとつも無い場合には、この時点でpreviewInstanceが除外されている
1608        * -*/
1609       if (m_previewInstances.find(it.key()) == m_previewInstances.end())
1610         continue;
1611 
1612       // Same for frozen ones
1613 
1614       if (!previewInstance->m_frozenFlips.empty() &&
1615           previewInstance->m_frozenFlips.size() < 20) {
1616         std::set<FlipBook *> frozenFlips = previewInstance->m_frozenFlips;
1617         for (jt = frozenFlips.begin(); jt != frozenFlips.end(); ++jt) {
1618           // Detach and reset the flipbook
1619           (*jt)->reset();  // The detachment happens in here
1620         }
1621       }
1622 
1623       // The Preview instance should have been deleted at this point (due to
1624       // flipbook detachments)
1625       assert(m_previewInstances.find(it.key()) == m_previewInstances.end());
1626     } else {
1627       it.value()->reset();
1628       it.value()->refreshViewRects();
1629     }
1630   }
1631 }
1632 
1633 //-----------------------------------------------------------------------------
1634 
emitStartedFrame(unsigned long fxId,const TRenderPort::RenderData & renderData)1635 void PreviewFxManager::emitStartedFrame(
1636     unsigned long fxId, const TRenderPort::RenderData &renderData) {
1637   emit startedFrame(fxId, renderData);
1638 }
1639 
1640 //-----------------------------------------------------------------------------
1641 
emitRenderedFrame(unsigned long fxId,const TRenderPort::RenderData & renderData)1642 void PreviewFxManager::emitRenderedFrame(
1643     unsigned long fxId, const TRenderPort::RenderData &renderData) {
1644   emit renderedFrame(fxId, renderData);
1645 }
1646 
1647 //-----------------------------------------------------------------------------
1648 
onStartedFrame(unsigned long fxId,TRenderPort::RenderData renderData)1649 void PreviewFxManager::onStartedFrame(unsigned long fxId,
1650                                       TRenderPort::RenderData renderData) {
1651   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1652       m_previewInstances.find(fxId);
1653   if (it != m_previewInstances.end()) {
1654     // Invoke the corresponding function. This happens in the MAIN THREAD
1655     it.value()->doOnRenderRasterStarted(renderData);
1656   }
1657 }
1658 
1659 //-----------------------------------------------------------------------------
1660 
onRenderedFrame(unsigned long fxId,TRenderPort::RenderData renderData)1661 void PreviewFxManager::onRenderedFrame(unsigned long fxId,
1662                                        TRenderPort::RenderData renderData) {
1663   QMap<unsigned long, PreviewFxInstance *>::iterator it =
1664       m_previewInstances.find(fxId);
1665   if (it != m_previewInstances.end()) {
1666     // Invoke the corresponding function. This happens in the MAIN THREAD
1667     it.value()->doOnRenderRasterCompleted(renderData);
1668   }
1669 }
1670 
1671 //-----------------------------------------------------------------------------
1672 
1673 //! The suspendRendering method allows suspension of the previewer's rendering
1674 //! activity for safety purposes, typically related to the fact that no
1675 //! rendering
1676 //! process should actually be performed as the underlying scene is about to
1677 //! change
1678 //! or being destroyed. Upon suspension, further rendering requests are silently
1679 //! rejected - and currently active ones are canceled and waited until they are
1680 //! no
1681 //! longer active.
suspendRendering(bool suspend)1682 void PreviewFxManager::suspendRendering(bool suspend) {
1683   suspendedRendering = suspend;
1684   if (suspend && previewerInstance) {
1685     QMap<unsigned long, PreviewFxInstance *>::iterator it;
1686     for (it = previewerInstance->m_previewInstances.begin();
1687          it != previewerInstance->m_previewInstances.end(); ++it) {
1688       it.value()->m_renderer.stopRendering(true);
1689     }
1690   }
1691 }
1692