1 
2 
3 // System-core includes
4 #include "tsystem.h"
5 #include "tthreadmessage.h"
6 #include "timagecache.h"
7 #include "tstopwatch.h"
8 #include "tfiletype.h"
9 
10 // Images includes
11 #include "trasterimage.h"
12 #include "trop.h"
13 #include "tiio.h"
14 #include "tpixelutils.h"
15 #include "tlevel_io.h"
16 #include "tcodec.h"
17 
18 // Fx-related includes
19 #include "tfxutil.h"
20 
21 // Cache management includes
22 #include "tpassivecachemanager.h"
23 
24 // Toonz app (currents)
25 #include "tapp.h"
26 #include "toutputproperties.h"
27 
28 // Toonz stage structures
29 #include "toonz/tobjecthandle.h"
30 #include "toonz/tscenehandle.h"
31 #include "toonz/tframehandle.h"
32 #include "toonz/txshlevelhandle.h"
33 #include "toonz/txsheethandle.h"
34 #include "toonz/tfxhandle.h"
35 #include "toonz/tpalettehandle.h"
36 #include "toonz/sceneproperties.h"
37 #include "toonz/scenefx.h"
38 #include "toonz/toonzscene.h"
39 #include "toonz/txshlevel.h"
40 #include "toonz/txsheet.h"
41 #include "toonz/tcamera.h"
42 #include "toonz/palettecontroller.h"
43 
44 // Toonz-qt stuff
45 #include "toonzqt/gutil.h"
46 #include "toonzqt/menubarcommand.h"
47 #include "menubarcommandids.h"
48 #include "filebrowserpopup.h"
49 #include "filebrowsermodel.h"
50 
51 // Toonz 6 FlipConsole's slider
52 #include "toonzqt/flipconsole.h"
53 
54 // Qt stuff
55 #include <QTimer>
56 #include <QRegion>
57 #include <QMetaType>
58 
59 #include "previewer.h"
60 
61 //------------------------------------------------
62 
63 /*! \class Previewer
64 The Previewer class is a singleton which deals with Preview renders of isolated
65 frames.
66 The main purpose of this class, as of Toonz 6.x, is that of managing preview
67 requests coming
68 from Toonz's Scene Viewers; however, it is designed to support requests from a
69 generic
70 \b Previewer::Listener class.
71 The Previewer is a singleton, meaning that it centralizes requests from many
72 possible listeners
73 in a single collection of previewed frames.
74 \n \n
75 The most important method of the class is \b getRaster(), which attempts
76 retrieval of a certain
77 frame and, if the frame was not calculated yet, makes the Previewer build it.
78 \n
79 The update() slot is provided to refresh the cached informations about the
80 entire collection
81 of currently stored frames. Updated frames are recalculated only upon a new
82 getRaster(), in case the previously
83 stored informations no more match the current frame description.
84 \n
85 The clear() methods make the Previewer erase all stored informations about one
86 or all frames,
87 so that the following getRaster() will forcibly recalculate the requested frame.
88 */
89 
90 //------------------------------------------------
91 
92 //======================================================================================
93 //    Preliminary stuff
94 //======================================================================================
95 
96 #define CACHEID "RenderCache"
97 
98 namespace {
99 bool suspendedRendering        = false;
100 Previewer *previewerInstance   = 0;
101 Previewer *previewerInstanceSC = 0;
102 
103 QTimer levelChangedTimer, fxChangedTimer, xsheetChangedTimer,
104     objectChangedTimer;
105 const int notificationDelay = 300;
106 
107 //-------------------------------------------------------------------------
108 
109 void buildNodeTreeDescription(std::string &desc, const TFxP &root);
110 
111 //-------------------------------------------------------------------------
112 
113 // Qt's contains actually returns QRegion::intersected... I wonder why...
contains(const QRegion & region,const TRect & rect)114 inline bool contains(const QRegion &region, const TRect &rect) {
115   return QRegion(toQRect(rect)).subtracted(region).isEmpty();
116 }
117 }  // namespace
118 
119 //======================================================================================
120 //    Internal classes declaration
121 //======================================================================================
122 
123 //=======================
124 //    Previewer::Imp
125 //-----------------------
126 
127 class Previewer::Imp final : public TRenderPort {
128 public:
129   // All useful infos about a frame under Previewer's management
130   struct FrameInfo {
131   public:
132     std::string m_alias;       // The alias of m_fx
133     unsigned long m_renderId;  // The render process Id - passed by TRenderer
134     QRegion m_renderedRegion;  // The plane region already rendered for m_fx
135     TRect m_rectUnderRender;   // Plane region currently under render
136 
FrameInfoPreviewer::Imp::FrameInfo137     FrameInfo() : m_renderId((unsigned long)-1) {}
138   };
139 
140 public:
141   Previewer *m_owner;
142   TThread::Mutex m_mutex;
143 
144   std::set<Previewer::Listener *> m_listeners;
145   std::map<int, FrameInfo> m_frames;
146   std::vector<UCHAR> m_pbStatus;
147   int m_computingFrameCount;
148 
149   TRenderSettings m_renderSettings;
150 
151   std::string m_cachePrefix;
152 
153   TDimension m_cameraRes;
154   TRectD m_renderArea;
155   TPointD m_cameraPos;
156   bool m_subcamera;
157 
158   TRect m_previewRect;
159 
160   TRenderer m_renderer;
161 
162   // Save command stuff
163   TLevelWriterP m_lw;
164   int m_currentFrameToSave;
165 
166 public:
167   Imp(Previewer *owner);
168   ~Imp();
169 
170   void notifyStarted(int frame);
171   void notifyCompleted(int frame);
172   void notifyFailed(int frame);
173   void notifyUpdate();
174 
175   TFxPair buildSceneFx(int frame);
176 
177   // Updater methods. These refresh the manager's status, but do not launch new
178   // renders
179   // on their own. The refreshFrame() method must be manually invoked if
180   // necessary.
181   void updateRenderSettings();
182 
183   void updateAliases();
184   void updateAliasKeyword(const std::string &keyword);
185 
186   // There are dependencies among the following updaters. Invoke them in the
187   // specified order.
188   void updateFrameRange();
189   void updateProgressBarStatus();
190 
191   void updateCamera();
192   void updatePreviewRect();  // This is automatically invoked by refreshFrame()
193 
194   // Use this method to re-render the passed frame. Infos specified with the
195   // update* methods
196   // are assumed correct.
197   void refreshFrame(int frame);
198 
199   // TRenderPort methods
200   void onRenderRasterStarted(const RenderData &renderData) override;
201   void onRenderRasterCompleted(const RenderData &renderData) override;
202   void onRenderFailure(const RenderData &renderData, TException &e) override;
203 
204   // Main-thread executed code related to TRenderPort. Used to update
205   // thread-vulnerable infos.
206   void doOnRenderRasterStarted(const RenderData &renderData);
207   void doOnRenderRasterCompleted(const RenderData &renderData);
208   void doOnRenderRasterFailed(const RenderData &renderData);
209 
210   void remove(int frame);
211   void remove();
212 
213 public:
214   void saveFrame();
215   bool doSaveRenderedFrames(const TFilePath fp);
216 };
217 
218 //======================================================================================
219 //    Code implementation
220 //======================================================================================
221 
222 //============================
223 //    Previewer::Listener
224 //----------------------------
225 
Listener()226 Previewer::Listener::Listener() { m_refreshTimer.setSingleShot(true); }
227 
228 //-----------------------------------------------------------------------------
229 
requestTimedRefresh()230 void Previewer::Listener::requestTimedRefresh() { m_refreshTimer.start(1000); }
231 
232 //=======================
233 //    Previewer::Imp
234 //-----------------------
235 
Imp(Previewer * owner)236 Previewer::Imp::Imp(Previewer *owner)
237     : m_owner(owner)
238     , m_cameraRes(0, 0)
239     , m_renderer(TSystem::getProcessorCount())
240     , m_computingFrameCount(0)
241     , m_currentFrameToSave(0)
242     , m_subcamera(false)
243     , m_lw() {
244   // Precomputing (ie predictive cache) is disabled in this case. This is due to
245   // current TFxCacheManager's
246   // implementation, which can't still handle multiple render processes from the
247   // same TRenderer. This should
248   // change in the near future...
249   m_renderer.enablePrecomputing(false);
250   m_renderer.addPort(this);
251 
252   updateRenderSettings();
253   updateCamera();
254   updateFrameRange();
255 }
256 
257 //-----------------------------------------------------------------------------
258 
~Imp()259 Previewer::Imp::~Imp() {
260   m_renderer.removePort(this);
261 
262   // for(std::map<int, FrameInfo*>::iterator it=m_frames.begin();
263   // it!=m_frames.end(); ++it)
264   //  delete it->second; //crash on exit! not a serious leak, Previewer is a
265   //  singleton
266 }
267 
268 //-----------------------------------------------------------------------------
269 
buildSceneFx(int frame)270 TFxPair Previewer::Imp::buildSceneFx(int frame) {
271   TFxPair fxPair;
272 
273   TApp *app         = TApp::instance();
274   ToonzScene *scene = app->getCurrentScene()->getScene();
275   TXsheet *xsh      = scene->getXsheet();
276   if (m_renderSettings.m_stereoscopic) {
277     scene->shiftCameraX(-m_renderSettings.m_stereoscopicShift / 2.0);
278     fxPair.m_frameA = ::buildSceneFx(
279         scene, xsh, frame, TOutputProperties::AllLevels,
280         m_renderSettings.m_applyShrinkToViewer ? m_renderSettings.m_shrinkX : 1,
281         false);
282 
283     scene->shiftCameraX(m_renderSettings.m_stereoscopicShift);
284     fxPair.m_frameB = ::buildSceneFx(
285         scene, xsh, frame, TOutputProperties::AllLevels,
286         m_renderSettings.m_applyShrinkToViewer ? m_renderSettings.m_shrinkX : 1,
287         false);
288 
289     scene->shiftCameraX(-m_renderSettings.m_stereoscopicShift / 2.0);
290   } else
291     fxPair.m_frameA = ::buildSceneFx(
292         scene, xsh, frame, TOutputProperties::AllLevels,
293         m_renderSettings.m_applyShrinkToViewer ? m_renderSettings.m_shrinkX : 1,
294         false);
295 
296   return fxPair;
297 }
298 
299 //-----------------------------------------------------------------------------
300 
updateCamera()301 void Previewer::Imp::updateCamera() {
302   // Retrieve current camera
303   TCamera *currCamera =
304       TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera();
305   TRect subCameraRect = currCamera->getInterestRect();
306   TPointD cameraPos(-0.5 * currCamera->getRes().lx,
307                     -0.5 * currCamera->getRes().ly);
308 
309   // Update the camera region and camera stage area
310   TDimension cameraRes(0, 0);
311   TRectD renderArea;
312 
313   if (m_subcamera && subCameraRect.getLx() > 0 && subCameraRect.getLy() > 0) {
314     cameraRes  = TDimension(subCameraRect.getLx(), subCameraRect.getLy());
315     renderArea = TRectD(subCameraRect.x0, subCameraRect.y0,
316                         subCameraRect.x1 + 1, subCameraRect.y1 + 1) +
317                  cameraPos;
318   } else {
319     cameraRes  = currCamera->getRes();
320     renderArea = TRectD(cameraPos, TDimensionD(cameraRes.lx, cameraRes.ly));
321   }
322 
323   // Add the shrink to camera res
324   cameraRes.lx /= m_renderSettings.m_shrinkX;
325   cameraRes.ly /= m_renderSettings.m_shrinkY;
326 
327   // Invalidate the old camera size
328   if (m_cameraRes != cameraRes || m_renderArea != renderArea) {
329     m_cameraRes  = cameraRes;
330     m_renderArea = renderArea;
331     m_cameraPos  = cameraPos;
332 
333     // All previously rendered frames must be erased
334     std::map<int, FrameInfo>::iterator it;
335     for (it = m_frames.begin(); it != m_frames.end(); ++it)
336       TImageCache::instance()->remove(m_cachePrefix +
337                                       std::to_string(it->first));
338 
339     m_frames.clear();
340   }
341 }
342 
343 //-----------------------------------------------------------------------------
344 
updateRenderSettings()345 void Previewer::Imp::updateRenderSettings() {
346   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
347   TRenderSettings renderSettings =
348       scene->getProperties()->getPreviewProperties()->getRenderSettings();
349 
350   if (renderSettings.m_applyShrinkToViewer)
351     renderSettings.m_shrinkY = renderSettings.m_shrinkX;
352   else
353     renderSettings.m_shrinkY = renderSettings.m_shrinkX = 1;
354 
355   // In case the settings changee, erase all previously cached images
356   if (renderSettings != m_renderSettings) {
357     m_renderSettings = renderSettings;
358 
359     std::map<int, FrameInfo>::iterator it;
360     for (it = m_frames.begin(); it != m_frames.end(); ++it)
361       TImageCache::instance()->remove(m_cachePrefix +
362                                       std::to_string(it->first));
363 
364     m_frames.clear();
365   }
366 }
367 
368 //-----------------------------------------------------------------------------
369 
updateFrameRange()370 void Previewer::Imp::updateFrameRange() {
371   // Erase all rendered frames outside the new frame range
372   int newFrameCount =
373       TApp::instance()->getCurrentScene()->getScene()->getFrameCount();
374 
375   std::map<int, FrameInfo>::iterator it,
376       jt = m_frames.lower_bound(newFrameCount);
377   for (it = jt; it != m_frames.end(); ++it)
378     // Release all associated cached images
379     TImageCache::instance()->remove(m_cachePrefix + std::to_string(it->first));
380 
381   m_frames.erase(jt, m_frames.end());
382 
383   // Resize the progress bar status
384   int i, oldSize = m_pbStatus.size();
385   m_pbStatus.resize(newFrameCount);
386   for (i = oldSize; i < newFrameCount; ++i)
387     m_pbStatus[i] = FlipSlider::PBFrameNotStarted;
388 }
389 
390 //-----------------------------------------------------------------------------
391 
updateProgressBarStatus()392 void Previewer::Imp::updateProgressBarStatus() {
393   unsigned int i, pbSize = m_pbStatus.size();
394   std::map<int, FrameInfo>::iterator it;
395   for (i = 0; i < pbSize; ++i) {
396     it = m_frames.find(i);
397     m_pbStatus[i] =
398         (it == m_frames.end())
399             ? FlipSlider::PBFrameNotStarted
400             : ::contains(it->second.m_renderedRegion, m_previewRect)
401                   ? FlipSlider::PBFrameFinished
402                   : it->second.m_rectUnderRender.contains(m_previewRect)
403                         ? FlipSlider::PBFrameStarted
404                         : FlipSlider::PBFrameNotStarted;
405   }
406 }
407 
408 //-----------------------------------------------------------------------------
409 
updatePreviewRect()410 void Previewer::Imp::updatePreviewRect() {
411   TRectD previewRectD;
412 
413   /*--
414    * SubCameraPreviewではない場合、Viewerの表示エリアに関係なく全画面で計算を行う
415    * --*/
416   if (!m_subcamera)
417     previewRectD = m_renderArea;
418   else {
419     // Retrieve the view rects from each listener. Their union will form the
420     // rect to be rendered.
421     std::set<Previewer::Listener *>::iterator it;
422     for (it = m_listeners.begin(); it != m_listeners.end(); ++it) {
423       // Retrieve the listener's viewRect and add it to the preview rect
424       previewRectD += (*it)->getPreviewRect();
425     }
426   }
427 
428   previewRectD *= m_renderArea;
429 
430   int shrinkX = m_renderSettings.m_shrinkX;
431   int shrinkY = m_renderSettings.m_shrinkY;
432 
433   // Ensure that rect has the same pixel geometry as the preview camera
434   previewRectD -= m_cameraPos;
435   previewRectD.x0 = previewRectD.x0 / shrinkX;
436   previewRectD.y0 = previewRectD.y0 / shrinkY;
437   previewRectD.x1 = previewRectD.x1 / shrinkX;
438   previewRectD.y1 = previewRectD.y1 / shrinkY;
439 
440   // Now, pass to m_cameraRes-relative coordinates
441   TPointD shrinkedRelPos((m_renderArea.x0 - m_cameraPos.x) / shrinkX,
442                          (m_renderArea.y0 - m_cameraPos.y) / shrinkY);
443   previewRectD -= shrinkedRelPos;
444 
445   previewRectD.x0 = tfloor(previewRectD.x0);
446   previewRectD.y0 = tfloor(previewRectD.y0);
447   previewRectD.x1 = tceil(previewRectD.x1);
448   previewRectD.y1 = tceil(previewRectD.y1);
449 
450   m_previewRect = TRect(previewRectD.x0, previewRectD.y0, previewRectD.x1 - 1,
451                         previewRectD.y1 - 1);
452 
453   previewRectD += m_cameraPos + shrinkedRelPos;
454 
455   setRenderArea(previewRectD);
456 }
457 
458 //-----------------------------------------------------------------------------
459 
updateAliases()460 void Previewer::Imp::updateAliases() {
461   std::map<int, FrameInfo>::iterator it;
462   for (it = m_frames.begin(); it != m_frames.end(); ++it) {
463     TFxPair fxPair = buildSceneFx(it->first);
464 
465     std::string newAlias =
466         fxPair.m_frameA ? fxPair.m_frameA->getAlias(it->first, m_renderSettings)
467                         : "";
468     newAlias = newAlias + (fxPair.m_frameB ? fxPair.m_frameB->getAlias(
469                                                  it->first, m_renderSettings)
470                                            : "");
471 
472     if (newAlias != it->second.m_alias) {
473       // Clear the remaining frame infos
474       it->second.m_renderedRegion = QRegion();
475     }
476   }
477 }
478 
479 //-----------------------------------------------------------------------------
480 
updateAliasKeyword(const std::string & keyword)481 void Previewer::Imp::updateAliasKeyword(const std::string &keyword) {
482   std::map<int, FrameInfo>::iterator it;
483   for (it = m_frames.begin(); it != m_frames.end(); ++it) {
484     if (it->second.m_alias.find(keyword) != std::string::npos) {
485       TFxPair fxPair     = buildSceneFx(it->first);
486       it->second.m_alias = fxPair.m_frameA ? fxPair.m_frameA->getAlias(
487                                                  it->first, m_renderSettings)
488                                            : "";
489       it->second.m_alias = it->second.m_alias +
490                            (fxPair.m_frameB ? fxPair.m_frameB->getAlias(
491                                                   it->first, m_renderSettings)
492                                             : "");
493 
494       // Clear the remaining frame infos
495       it->second.m_renderedRegion = QRegion();
496 
497       // No need to release the cached image... eventually, clear it
498       TRasterImageP ri = TImageCache::instance()->get(
499           m_cachePrefix + std::to_string(it->first), true);
500       if (ri) ri->getRaster()->clear();
501     }
502   }
503 }
504 
505 //-----------------------------------------------------------------------------
506 
507 //! Starts rendering the passed frame.
refreshFrame(int frame)508 void Previewer::Imp::refreshFrame(int frame) {
509   if (suspendedRendering) return;
510 
511   // Build the region to render
512   updatePreviewRect();
513 
514   if (m_previewRect.getLx() <= 0 || m_previewRect.getLy() <= 0) return;
515 
516   // Retrieve the FrameInfo for passed frame
517   std::map<int, FrameInfo>::iterator it = m_frames.find(frame);
518   if (it != m_frames.end()) {
519     // In case the rect we would render is contained in the frame's rendered
520     // region, quit
521     if (::contains(it->second.m_renderedRegion, m_previewRect)) return;
522 
523     // Then, check the m_previewRect against the frame's m_rectUnderRendering.
524     // Ensure that we're not re-launching the very same render.
525     if (it->second.m_rectUnderRender == m_previewRect) return;
526 
527     // Stop any frame's previously running render process
528     m_renderer.abortRendering(it->second.m_renderId);
529   } else {
530     it = m_frames.insert(std::make_pair(frame, FrameInfo())).first;
531 
532     // In case the frame is not in the frame range, we add a temporary
533     // supplement
534     // to the progress bar.
535     if (frame >= (int)m_pbStatus.size()) m_pbStatus.resize(frame + 1);
536   }
537 
538   // Build the TFxPair to be passed to TRenderer
539   TFxPair fxPair = buildSceneFx(frame);
540 
541   // Update the RenderInfos associated with frame
542   it->second.m_rectUnderRender = m_previewRect;
543   it->second.m_alias = fxPair.m_frameA->getAlias(frame, m_renderSettings);
544   if (fxPair.m_frameB)
545     it->second.m_alias =
546         it->second.m_alias + fxPair.m_frameB->getAlias(frame, m_renderSettings);
547 
548   // Retrieve the renderId of the rendering instance
549   it->second.m_renderId = m_renderer.nextRenderId();
550   std::string contextName("P");
551   contextName += m_subcamera ? "SC" : "FU";
552   contextName += std::to_string(frame);
553   TPassiveCacheManager::instance()->setContextName(it->second.m_renderId,
554                                                    contextName);
555 
556   // Start the render
557   m_renderer.startRendering(frame, m_renderSettings, fxPair);
558 }
559 
560 //-----------------------------------------------------------------------------
561 
remove(int frame)562 void Previewer::Imp::remove(int frame) {
563   // Search the frame among rendered ones
564   std::map<int, FrameInfo>::iterator it = m_frames.find(frame);
565   if (it != m_frames.end()) {
566     m_renderer.abortRendering(it->second.m_renderId);
567     m_frames.erase(frame);
568   }
569 
570   // Remove the associated image from cache
571   TImageCache::instance()->remove(m_cachePrefix + std::to_string(frame));
572 }
573 
574 //-----------------------------------------------------------------------------
575 
remove()576 void Previewer::Imp::remove() {
577   m_renderer.stopRendering(false);
578 
579   // Remove all cached images
580   std::map<int, FrameInfo>::iterator it;
581   for (it = m_frames.begin(); it != m_frames.end(); ++it)
582     TImageCache::instance()->remove(m_cachePrefix + std::to_string(it->first));
583 
584   m_frames.clear();
585 }
586 
587 //-----------------------------------------------------------------------------
588 
notifyStarted(int frame)589 inline void Previewer::Imp::notifyStarted(int frame) {
590   std::set<Previewer::Listener *>::iterator it;
591   for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
592     (*it)->onRenderStarted(frame);
593 }
594 
595 //-----------------------------------------------------------------------------
596 
notifyCompleted(int frame)597 inline void Previewer::Imp::notifyCompleted(int frame) {
598   std::set<Previewer::Listener *>::iterator it;
599   for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
600     (*it)->onRenderCompleted(frame);
601 }
602 
603 //-----------------------------------------------------------------------------
604 
notifyFailed(int frame)605 inline void Previewer::Imp::notifyFailed(int frame) {
606   std::set<Previewer::Listener *>::iterator it;
607   for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
608     (*it)->onRenderFailed(frame);
609 }
610 
611 //-----------------------------------------------------------------------------
612 
notifyUpdate()613 inline void Previewer::Imp::notifyUpdate() {
614   std::set<Previewer::Listener *>::iterator it;
615   for (it = m_listeners.begin(); it != m_listeners.end(); ++it)
616     (*it)->onPreviewUpdate();
617 }
618 
619 //-----------------------------------------------------------------------------
620 
621 //! Adds the renderized image to TImageCache; listeneres are advised too.
onRenderRasterStarted(const RenderData & renderData)622 void Previewer::Imp::onRenderRasterStarted(const RenderData &renderData) {
623   // Emit the started signal to execute code in the main thread
624   m_owner->emitStartedFrame(renderData);
625 }
626 
627 //-----------------------------------------------------------------------------
628 
doOnRenderRasterStarted(const RenderData & renderData)629 void Previewer::Imp::doOnRenderRasterStarted(const RenderData &renderData) {
630   int frame = renderData.m_frames[0];
631 
632   m_computingFrameCount++;
633 
634   // Update the progress bar status
635   if (frame < m_pbStatus.size()) m_pbStatus[frame] = FlipSlider::PBFrameStarted;
636 
637   // Notify listeners
638   notifyStarted(frame);
639 }
640 
641 //-----------------------------------------------------------------------------
642 
643 //! Adds the renderized image to TImageCache; listeneres are advised too.
onRenderRasterCompleted(const RenderData & renderData)644 void Previewer::Imp::onRenderRasterCompleted(const RenderData &renderData) {
645   if (renderData.m_rasB) {
646     RenderData _renderData = renderData;
647     assert(m_renderSettings.m_stereoscopic);
648     TRop::makeStereoRaster(_renderData.m_rasA, _renderData.m_rasB);
649     if (_renderData.m_info.m_gamma != 1.0)
650       TRop::gammaCorrect(_renderData.m_rasA, _renderData.m_info.m_gamma);
651 
652     _renderData.m_rasB = TRasterP();
653     m_owner->emitRenderedFrame(_renderData);
654     return;
655   }
656 
657   // If required, correct gamma
658   if (renderData.m_info.m_gamma != 1.0)
659     TRop::gammaCorrect(renderData.m_rasA, renderData.m_info.m_gamma);
660 
661   // Emit the started signal to execute code in the main thread
662   m_owner->emitRenderedFrame(renderData);
663 }
664 
665 //-----------------------------------------------------------------------------
666 
doOnRenderRasterCompleted(const RenderData & renderData)667 void Previewer::Imp::doOnRenderRasterCompleted(const RenderData &renderData) {
668   int renderId = renderData.m_renderId;
669   int frame    = renderData.m_frames[0];
670 
671   if (renderData.m_rasB) {
672     assert(m_renderSettings.m_stereoscopic);
673     TRop::makeStereoRaster(renderData.m_rasA, renderData.m_rasB);
674   }
675 
676   TRasterP ras(renderData.m_rasA);
677 
678   m_computingFrameCount--;
679 
680   // Find the render infos in the Previewer
681   std::map<int, FrameInfo>::iterator it = m_frames.find(frame);
682   if (it == m_frames.end()) return;
683 
684   // Ensure that the render process id is the same
685   if (renderId != it->second.m_renderId) return;
686 
687   // Store the rendered image in the cache - this is done in the MAIN thread due
688   // to the necessity of accessing it->second.m_rectUnderRender for raster
689   // extraction.
690   std::string str = m_cachePrefix + std::to_string(frame);
691 
692   TRasterImageP ri(TImageCache::instance()->get(str, true));
693   TRasterP cachedRas(ri ? ri->getRaster() : TRasterP());
694 
695   if (!cachedRas || (cachedRas->getSize() != m_cameraRes)) {
696     TImageCache::instance()->remove(str);
697 
698     // Create the raster at camera resolution
699     cachedRas = ras->create(m_cameraRes.lx, m_cameraRes.ly);
700     cachedRas->clear();
701     ri = TRasterImageP(cachedRas);
702   }
703 
704   // Finally, copy the rendered raster over the cached one
705   TRect rectUnderRender(
706       it->second
707           .m_rectUnderRender);  // Extract may MODIFY IT! E.g. with shrinks..!
708   cachedRas = cachedRas->extract(rectUnderRender);
709   if (cachedRas) {
710     cachedRas->copy(ras);
711     TImageCache::instance()->add(str, ri);
712   }
713 
714   // Update the FrameInfo
715   it->second.m_renderedRegion += toQRect(it->second.m_rectUnderRender);
716   it->second.m_rectUnderRender = TRect();
717 
718   // Update the progress bar status
719   if (frame < m_pbStatus.size())
720     m_pbStatus[frame] = FlipSlider::PBFrameFinished;
721 
722   // Notify listeners
723   notifyCompleted(frame);
724 }
725 
726 //-----------------------------------------------------------------------------
727 
728 //! Removes the associated raster from TImageCache, and listeners are made
729 //! aware.
onRenderFailure(const RenderData & renderData,TException & e)730 void Previewer::Imp::onRenderFailure(const RenderData &renderData,
731                                      TException &e) {
732   m_owner->emitFailedFrame(renderData);
733 }
734 
735 //-----------------------------------------------------------------------------
736 
737 //! Adds the renderized image to TImageCache; listeneres are advised too.
doOnRenderRasterFailed(const RenderData & renderData)738 void Previewer::Imp::doOnRenderRasterFailed(const RenderData &renderData) {
739   m_computingFrameCount--;
740 
741   int frame = (int)renderData.m_frames[0];
742 
743   std::map<int, FrameInfo>::iterator it = m_frames.find(frame);
744   if (it == m_frames.end()) return;
745 
746   if (renderData.m_renderId != it->second.m_renderId) return;
747 
748   it->second.m_renderedRegion  = QRegion();
749   it->second.m_rectUnderRender = TRect();
750 
751   // Update the progress bar status
752   if (frame < m_pbStatus.size())
753     m_pbStatus[frame] = FlipSlider::PBFrameNotStarted;
754 
755   notifyCompleted(frame);  // Completed!?
756 }
757 
758 //-----------------------------------------------------------------------------
759 
760 namespace {
761 enum { eBegin, eIncrement, eEnd };
762 
763 static DVGui::ProgressDialog *Pd = 0;
764 
765 //-----------------------------------------------------------------------------
766 
767 class ProgressBarMessager final : public TThread::Message {
768 public:
769   int m_choice;
770   int m_val;
771   QString m_str;
ProgressBarMessager(int choice,int val,const QString & str="")772   ProgressBarMessager(int choice, int val, const QString &str = "")
773       : m_choice(choice), m_val(val), m_str(str) {}
onDeliver()774   void onDeliver() override {
775     switch (m_choice) {
776     case eBegin:
777       if (!Pd)
778         Pd = new DVGui::ProgressDialog(
779             QObject::tr("Saving previewed frames...."), QObject::tr("Cancel"),
780             0, m_val);
781       else
782         Pd->setMaximum(m_val);
783       Pd->show();
784       break;
785     case eIncrement:
786       if (Pd->wasCanceled()) {
787         delete Pd;
788         Pd = 0;
789       } else {
790         // if (m_val==Pd->maximum()) Pd->hide();
791         Pd->setValue(m_val);
792       }
793       break;
794     case eEnd: {
795       DVGui::info(m_str);
796       delete Pd;
797       Pd = 0;
798     } break;
799     default:
800       assert(false);
801     }
802   }
803 
clone() const804   TThread::Message *clone() const override {
805     return new ProgressBarMessager(*this);
806   }
807 };
808 
809 }  // namespace
810 
811 //-----------------------------------------------------------------------------
812 
813 class SavePreviewedPopup final : public FileBrowserPopup {
814   Previewer *m_p;
815 
816 public:
SavePreviewedPopup()817   SavePreviewedPopup()
818       : FileBrowserPopup(QObject::tr("Save Previewed Images")) {
819     setOkText(QObject::tr("Save"));
820   }
821 
setPreview(Previewer * p)822   void setPreview(Previewer *p) { m_p = p; }
823 
execute()824   bool execute() override {
825     if (m_selectedPaths.empty()) return false;
826 
827     return m_p->doSaveRenderedFrames(*m_selectedPaths.begin());
828   }
829 };
830 
831 //=============================================================================
832 
doSaveRenderedFrames(TFilePath fp)833 bool Previewer::doSaveRenderedFrames(TFilePath fp) {
834   return m_imp->doSaveRenderedFrames(fp);
835 }
836 
837 //=============================================================================
838 
839 using namespace DVGui;
840 
doSaveRenderedFrames(TFilePath fp)841 bool Previewer::Imp::doSaveRenderedFrames(TFilePath fp) {
842   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
843   TOutputProperties *outputSettings =
844       scene->getProperties()->getOutputProperties();
845 
846   QStringList formats;
847   TLevelWriter::getSupportedFormats(formats, true);
848   Tiio::Writer::getSupportedFormats(formats, true);
849 
850   std::string ext = fp.getType();
851   if (ext == "") {
852     ext = outputSettings->getPath().getType();
853     fp  = fp.withType(ext);
854   }
855   if (fp.getName() == "") {
856     DVGui::warning(QObject::tr(
857         "The file name cannot be empty or contain any of the following "
858         "characters:(new line)  \\ / : * ? \"  |"));
859     return false;
860   }
861 
862   if (!formats.contains(QString::fromStdString(ext))) {
863     DVGui::warning(QObject::tr("Unsopporter raster format, cannot save"));
864     return false;
865   }
866 
867   if (fp.getWideName() == L"") fp = fp.withName(scene->getSceneName());
868   if (TFileType::getInfo(fp) == TFileType::RASTER_IMAGE || ext == "pct" ||
869       fp.getType() == "pic" || ext == "pict")  // pct e' un formato"livello" (ha
870                                                // i settings di quicktime) ma
871                                                // fatto di diversi frames
872     fp = fp.withFrame(TFrameId::EMPTY_FRAME);
873 
874   fp          = scene->decodeFilePath(fp);
875   bool exists = TFileStatus(fp.getParentDir()).doesExist();
876   if (!exists) {
877     try {
878       TFilePath parent = fp.getParentDir();
879       TSystem::mkDir(parent);
880       DvDirModel::instance()->refreshFolder(parent.getParentDir());
881     } catch (TException &e) {
882       error(QObject::tr("Cannot create %1 : %2",
883                         "Previewer warning %1:path %2:message")
884                 .arg(toQString(fp.getParentDir()))
885                 .arg(QString(::to_string(e.getMessage()).c_str())));
886       return false;
887     } catch (...) {
888       error(QObject::tr("Cannot create %1", "Previewer warning %1:path")
889                 .arg(toQString(fp.getParentDir())));
890       return false;
891     }
892   }
893 
894   if (TSystem::doesExistFileOrLevel(fp)) {
895     QString question(
896         QObject::tr("File %1 already exists.\nDo you want to overwrite it?")
897             .arg(toQString(fp)));
898     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
899                             QObject::tr("Cancel"), 0);
900     if (ret == 2) return false;
901   }
902 
903   try {
904     m_lw = TLevelWriterP(fp,
905                          outputSettings->getFileFormatProperties(fp.getType()));
906   } catch (TImageException &e) {
907     error(QString::fromStdString(::to_string(e.getMessage())));
908     return false;
909   }
910 
911   m_lw->setFrameRate(outputSettings->getFrameRate());
912 
913   m_currentFrameToSave = 1;
914 
915   ProgressBarMessager(
916       eBegin,
917       TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount())
918       .sendBlocking();
919 
920   QTimer::singleShot(50, m_owner, SLOT(saveFrame()));
921   return true;
922 }
923 
924 //-----------------------------------------------------------------------------
925 
saveFrame()926 void Previewer::Imp::saveFrame() {
927   static int savedFrames = 0;
928 
929   int frameCount =
930       TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount();
931 
932   assert(Pd);
933 
934   for (; m_currentFrameToSave <= frameCount; m_currentFrameToSave++) {
935     ProgressBarMessager(eIncrement, m_currentFrameToSave).sendBlocking();
936     if (!Pd) break;
937 
938     // Ensure that current frame actually have to be saved
939     int currFrameToSave                   = m_currentFrameToSave - 1;
940     std::map<int, FrameInfo>::iterator it = m_frames.find(currFrameToSave);
941     if (it == m_frames.end()) continue;
942 
943     if (m_pbStatus.size() < currFrameToSave ||
944         m_pbStatus[currFrameToSave] != FlipSlider::PBFrameFinished)
945       continue;
946 
947     TImageP img = TImageCache::instance()->get(
948         m_cachePrefix + std::to_string(currFrameToSave), false);
949     if (!img) continue;
950 
951     // Save the frame
952     TImageWriterP writer = m_lw->getFrameWriter(TFrameId(m_currentFrameToSave));
953     bool failureOnSaving = false;
954     if (!writer) continue;
955 
956     writer->save(img);
957     savedFrames++;
958     m_currentFrameToSave++;
959     QTimer::singleShot(50, m_owner, SLOT(saveFrame()));
960     return;
961   }
962 
963   // Output the save result
964   QString str =
965       QObject::tr("Saved %1 frames out of %2 in %3",
966                   "Previewer %1:savedframes %2:framecount %3:filepath")
967           .arg(QString(std::to_string(savedFrames).c_str()))
968           .arg(QString(std::to_string(frameCount).c_str()))
969           .arg(QString(::to_string(m_lw->getFilePath()).c_str()));
970 
971   if (!Pd) str = QObject::tr("Canceled! ", "Previewer") + str;
972 
973   ProgressBarMessager(eEnd, 0, str).send();
974 
975   m_currentFrameToSave = 0;
976   m_lw                 = TLevelWriterP();
977   savedFrames          = 0;
978 }
979 
980 //=============================================================================
981 // Previewer
982 //-----------------------------------------------------------------------------
983 
Previewer(bool subcamera)984 Previewer::Previewer(bool subcamera) : m_imp(new Imp(this)) {
985   m_imp->m_subcamera   = subcamera;
986   m_imp->m_cachePrefix = CACHEID + std::string(subcamera ? "SC" : "");
987 
988   TApp *app = TApp::instance();
989 
990   bool ret = true;
991 
992   // ret = ret && connect(app->getPaletteController()->getCurrentPalette(),
993   // SIGNAL(colorStyleChangedOnMouseRelease()),SLOT(onLevelChanged()));
994   // ret = ret && connect(app->getPaletteController()->getCurrentPalette(),
995   // SIGNAL(paletteChanged()),   SLOT(onLevelChanged()));
996   ret = ret && connect(app->getPaletteController()->getCurrentLevelPalette(),
997                        SIGNAL(colorStyleChangedOnMouseRelease()),
998                        SLOT(onLevelChanged()));
999   ret = ret && connect(app->getPaletteController()->getCurrentLevelPalette(),
1000                        SIGNAL(paletteChanged()), SLOT(onLevelChanged()));
1001 
1002   levelChangedTimer.setInterval(notificationDelay);
1003   fxChangedTimer.setInterval(notificationDelay);
1004   xsheetChangedTimer.setInterval(notificationDelay);
1005   objectChangedTimer.setInterval(notificationDelay);
1006   levelChangedTimer.setSingleShot(true);
1007   fxChangedTimer.setSingleShot(true);
1008   xsheetChangedTimer.setSingleShot(true);
1009   objectChangedTimer.setSingleShot(true);
1010 
1011   ret = ret && connect(app->getCurrentLevel(), SIGNAL(xshLevelChanged()),
1012                        &levelChangedTimer, SLOT(start()));
1013   ret = ret && connect(app->getCurrentFx(), SIGNAL(fxChanged()),
1014                        &fxChangedTimer, SLOT(start()));
1015   ret = ret && connect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()),
1016                        &xsheetChangedTimer, SLOT(start()));
1017   ret = ret && connect(app->getCurrentObject(), SIGNAL(objectChanged(bool)),
1018                        &objectChangedTimer, SLOT(start()));
1019 
1020   ret = ret && connect(&levelChangedTimer, SIGNAL(timeout()), this,
1021                        SLOT(onLevelChanged()));
1022   ret = ret &&
1023         connect(&fxChangedTimer, SIGNAL(timeout()), this, SLOT(onFxChanged()));
1024   ret = ret && connect(&xsheetChangedTimer, SIGNAL(timeout()), this,
1025                        SLOT(onXsheetChanged()));
1026   ret = ret && connect(&objectChangedTimer, SIGNAL(timeout()), this,
1027                        SLOT(onObjectChanged()));
1028 
1029   qRegisterMetaType<TRenderPort::RenderData>("TRenderPort::RenderData");
1030 
1031   ret = ret && connect(this, SIGNAL(startedFrame(TRenderPort::RenderData)),
1032                        this, SLOT(onStartedFrame(TRenderPort::RenderData)));
1033 
1034   ret = ret && connect(this, SIGNAL(renderedFrame(TRenderPort::RenderData)),
1035                        this, SLOT(onRenderedFrame(TRenderPort::RenderData)));
1036 
1037   ret = ret && connect(this, SIGNAL(failedFrame(TRenderPort::RenderData)), this,
1038                        SLOT(onFailedFrame(TRenderPort::RenderData)));
1039 
1040   // As a result of performing the connections above in the Previewer
1041   // constructor, no instance()
1042   // of it can be requested before a first scene has been completely
1043   // initialized.
1044   // Inform a global variable of the fact that a first instantiation was made.
1045   if (subcamera)
1046     previewerInstanceSC = this;
1047   else
1048     previewerInstance = this;
1049 
1050   assert(ret);
1051 }
1052 
1053 //-----------------------------------------------------------------------------
1054 
~Previewer()1055 Previewer::~Previewer() {}
1056 
1057 //-----------------------------------------------------------------------------
1058 
1059 //! Ritorna l'istanza del \b Previewer
instance(bool subcameraPreview)1060 Previewer *Previewer::instance(bool subcameraPreview) {
1061   static Previewer _instance(false);
1062   static Previewer _instanceSC(true);
1063 
1064   return subcameraPreview ? &_instanceSC : &_instance;
1065 }
1066 
1067 //-----------------------------------------------------------------------------
1068 
getProgressBarStatus() const1069 std::vector<UCHAR> &Previewer::getProgressBarStatus() const {
1070   return m_imp->m_pbStatus;
1071 }
1072 
1073 //-----------------------------------------------------------------------------
1074 
addListener(Listener * listener)1075 void Previewer::addListener(Listener *listener) {
1076   m_imp->m_listeners.insert(listener);
1077   connect(&listener->m_refreshTimer, SIGNAL(timeout()), this,
1078           SLOT(updateView()));
1079 }
1080 
1081 //-----------------------------------------------------------------------------
1082 
removeListener(Listener * listener)1083 void Previewer::removeListener(Listener *listener) {
1084   m_imp->m_listeners.erase(listener);
1085   disconnect(&listener->m_refreshTimer, SIGNAL(timeout()), this,
1086              SLOT(updateView()));
1087 
1088   if (m_imp->m_listeners.empty()) {
1089     m_imp->m_renderer.stopRendering(false);
1090 
1091     // Release all used context names
1092     std::string prefix("P");
1093     TPassiveCacheManager::instance()->releaseContextNamesWithPrefix(
1094         prefix + (m_imp->m_subcamera ? "SC" : "FU"));
1095   }
1096 }
1097 
1098 //-----------------------------------------------------------------------------
1099 
saveFrame()1100 void Previewer::saveFrame() { m_imp->saveFrame(); }
1101 
1102 //-----------------------------------------------------------------------------
1103 
saveRenderedFrames()1104 void Previewer::saveRenderedFrames() {
1105   if (TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount() == 0) {
1106     info(QObject::tr("No frame to save!"));
1107     return;
1108   }
1109   if (m_imp->m_currentFrameToSave != 0) {
1110     info(QObject::tr("Already saving!"));
1111     return;
1112   }
1113 
1114   static SavePreviewedPopup *savePopup = 0;
1115   if (!savePopup) savePopup = new SavePreviewedPopup;
1116 
1117   savePopup->setPreview(this);
1118 
1119   TFilePath outpath = TApp::instance()
1120                           ->getCurrentScene()
1121                           ->getScene()
1122                           ->getProperties()
1123                           ->getOutputProperties()
1124                           ->getPath();
1125   savePopup->setFolder(outpath.getParentDir());
1126   TFilePath name = outpath.withoutParentDir();
1127 
1128   savePopup->setFilename(
1129       name.getName() == ""
1130           ? name.withName(
1131                 TApp::instance()->getCurrentScene()->getScene()->getSceneName())
1132           : name);
1133 
1134   savePopup->show();
1135   savePopup->raise();
1136   savePopup->activateWindow();
1137 }
1138 
1139 //-----------------------------------------------------------------------------
1140 
1141 /*! Restituisce un puntatore al raster randerizzato se il frame e' disponibile,
1142     altrimenti comincia a calcolarlo*/
getRaster(int frame,bool renderIfNeeded) const1143 TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const {
1144   if (frame < 0) return TRasterP();
1145   std::map<int, Imp::FrameInfo>::iterator it = m_imp->m_frames.find(frame);
1146   if (it != m_imp->m_frames.end()) {
1147     if (frame < m_imp->m_pbStatus.size()) {
1148       if (m_imp->m_pbStatus[frame] == FlipSlider::PBFrameFinished ||
1149           !renderIfNeeded) {
1150         std::string str = m_imp->m_cachePrefix + std::to_string(frame);
1151         TRasterImageP rimg =
1152             (TRasterImageP)TImageCache::instance()->get(str, false);
1153         if (rimg) {
1154           TRasterP ras = rimg->getRaster();
1155           assert((TRaster32P)ras || (TRaster64P)ras);
1156           return ras;
1157         } else
1158           // Weird case - the frame was declared rendered, but no raster is
1159           // available...
1160           return TRasterP();
1161       } else {
1162         // Calculate the frame if it was not yet started
1163         if (m_imp->m_pbStatus[frame] == FlipSlider::PBFrameNotStarted)
1164           m_imp->refreshFrame(frame);
1165       }
1166     }
1167 
1168     // Retrieve the cached image, if any
1169     std::string str = m_imp->m_cachePrefix + std::to_string(frame);
1170     TRasterImageP rimg =
1171         (TRasterImageP)TImageCache::instance()->get(str, false);
1172     if (rimg) {
1173       TRasterP ras = rimg->getRaster();
1174       assert((TRaster32P)ras || (TRaster64P)ras);
1175       return ras;
1176     } else
1177       return TRasterP();
1178   } else {
1179     // Just schedule the frame for calculation, and return a void raster ptr
1180     if (renderIfNeeded) m_imp->refreshFrame(frame);
1181     return TRasterP();
1182   }
1183 }
1184 
1185 //-----------------------------------------------------------------------------
1186 
1187 //! Verifica se \b frame e' nella cache, cioe' se il frame e' disponibile
isFrameReady(int frame) const1188 bool Previewer::isFrameReady(int frame) const {
1189   if (frame < 0 || frame >= (int)m_imp->m_pbStatus.size()) return false;
1190 
1191   return m_imp->m_pbStatus[frame] == FlipSlider::PBFrameFinished;
1192 }
1193 
1194 //-----------------------------------------------------------------------------
1195 
isActive() const1196 bool Previewer::isActive() const { return !m_imp->m_listeners.empty(); }
1197 
1198 //-----------------------------------------------------------------------------
1199 
isBusy() const1200 bool Previewer::isBusy() const {
1201   return !m_imp->m_listeners.empty() && m_imp->m_computingFrameCount > 0;
1202 }
1203 
1204 //-----------------------------------------------------------------------------
1205 
1206 //! Richiama IMP::invalidateFrames(string aliasKeyframe) per aggiornare il frame
1207 //! \b fid.
onImageChange(TXshLevel * xl,const TFrameId & fid)1208 void Previewer::onImageChange(TXshLevel *xl, const TFrameId &fid) {
1209   TFilePath fp             = xl->getPath().withFrame(fid);
1210   std::string levelKeyword = ::to_string(fp);
1211 
1212   // Inform the cache managers of level invalidation
1213   if (!m_imp->m_subcamera)
1214     TPassiveCacheManager::instance()->invalidateLevel(levelKeyword);
1215 
1216   m_imp->updateAliasKeyword(levelKeyword);
1217 }
1218 
1219 //-----------------------------------------------------------------------------
1220 
clear(int frame)1221 void Previewer::clear(int frame) {
1222   m_imp->remove(frame);
1223   m_imp->updateProgressBarStatus();
1224 }
1225 
1226 //-----------------------------------------------------------------------------
1227 
clear()1228 void Previewer::clear() {
1229   m_imp->remove();
1230   m_imp->updateAliases();
1231   m_imp->updateFrameRange();
1232   m_imp->updateRenderSettings();
1233   m_imp->updateCamera();
1234   m_imp->updatePreviewRect();
1235   m_imp->updateProgressBarStatus();
1236 }
1237 
1238 //-----------------------------------------------------------------------------
1239 
clearAll()1240 void Previewer::clearAll() {
1241   Previewer::instance(false)->clear();
1242   Previewer::instance(true)->clear();
1243 }
1244 
1245 //-----------------------------------------------------------------------------
1246 
1247 //! Refreshes all scene frames
update()1248 void Previewer::update() {
1249   if (m_imp->m_listeners.empty()) return;
1250 
1251   m_imp->updateAliases();
1252   m_imp->updateFrameRange();
1253   m_imp->updateRenderSettings();
1254   m_imp->updateCamera();
1255   m_imp->updatePreviewRect();
1256   m_imp->updateProgressBarStatus();
1257 
1258   m_imp->notifyUpdate();
1259 }
1260 
1261 //-----------------------------------------------------------------------------
1262 
1263 //! Limited version of update(), just refreshes the view area.
updateView()1264 void Previewer::updateView() {
1265   if (m_imp->m_listeners.empty()) return;
1266 
1267   m_imp->updateFrameRange();
1268   m_imp->updateRenderSettings();
1269   m_imp->updateCamera();
1270   m_imp->updatePreviewRect();
1271   m_imp->updateProgressBarStatus();
1272 
1273   m_imp->notifyUpdate();
1274 }
1275 
1276 //-----------------------------------------------------------------------------
1277 
onLevelChange(TXshLevel * xl)1278 void Previewer::onLevelChange(TXshLevel *xl) {
1279   TFilePath fp             = xl->getPath();
1280   std::string levelKeyword = ::to_string(fp);
1281 
1282   // Inform the cache managers of level invalidation
1283   if (!m_imp->m_subcamera)
1284     TPassiveCacheManager::instance()->invalidateLevel(levelKeyword);
1285 
1286   m_imp->updateAliasKeyword(levelKeyword);
1287   m_imp->updateProgressBarStatus();
1288 
1289   m_imp->notifyUpdate();
1290 }
1291 
1292 //-----------------------------------------------------------------------------
1293 
onLevelChanged()1294 void Previewer::onLevelChanged() {
1295   TXshLevel *xl = TApp::instance()->getCurrentLevel()->getLevel();
1296   if (!xl) return;
1297 
1298   std::string levelKeyword;
1299   TFilePath fp = xl->getPath();
1300   levelKeyword = ::to_string(fp.withType(""));
1301 
1302   // Inform the cache managers of level invalidation
1303   if (!m_imp->m_subcamera)
1304     TPassiveCacheManager::instance()->invalidateLevel(levelKeyword);
1305 
1306   m_imp->updateAliasKeyword(levelKeyword);
1307   m_imp->updateProgressBarStatus();
1308 
1309   // Seems that the scene viewer does not update in this case...
1310   m_imp->notifyUpdate();
1311 }
1312 
1313 //-----------------------------------------------------------------------------
1314 
onFxChanged()1315 void Previewer::onFxChanged() {
1316   m_imp->updateAliases();
1317   m_imp->updateProgressBarStatus();
1318 
1319   m_imp->notifyUpdate();
1320 }
1321 
1322 //-----------------------------------------------------------------------------
1323 
onXsheetChanged()1324 void Previewer::onXsheetChanged() {
1325   m_imp->updateRenderSettings();
1326   m_imp->updateCamera();
1327   m_imp->updateFrameRange();
1328   m_imp->updateAliases();
1329   m_imp->updateProgressBarStatus();
1330 
1331   m_imp->notifyUpdate();
1332 }
1333 
1334 //-----------------------------------------------------------------------------
1335 
onObjectChanged()1336 void Previewer::onObjectChanged() {
1337   m_imp->updateAliases();
1338   m_imp->updateProgressBarStatus();
1339 
1340   m_imp->notifyUpdate();
1341 }
1342 
1343 //-----------------------------------------------------------------------------
1344 
onChange(const TFxChange & change)1345 void Previewer::onChange(const TFxChange &change) { onObjectChanged(); }
1346 
1347 //-----------------------------------------------------------------------------
1348 
emitStartedFrame(const TRenderPort::RenderData & renderData)1349 void Previewer::emitStartedFrame(const TRenderPort::RenderData &renderData) {
1350   emit startedFrame(renderData);
1351 }
1352 
1353 //-----------------------------------------------------------------------------
1354 
emitRenderedFrame(const TRenderPort::RenderData & renderData)1355 void Previewer::emitRenderedFrame(const TRenderPort::RenderData &renderData) {
1356   emit renderedFrame(renderData);
1357 }
1358 
1359 //-----------------------------------------------------------------------------
1360 
emitFailedFrame(const TRenderPort::RenderData & renderData)1361 void Previewer::emitFailedFrame(const TRenderPort::RenderData &renderData) {
1362   emit failedFrame(renderData);
1363 }
1364 
1365 //-----------------------------------------------------------------------------
1366 
onStartedFrame(TRenderPort::RenderData renderData)1367 void Previewer::onStartedFrame(TRenderPort::RenderData renderData) {
1368   // Invoke the corresponding function. This happens in the MAIN THREAD
1369   m_imp->doOnRenderRasterStarted(renderData);
1370 }
1371 
1372 //-----------------------------------------------------------------------------
1373 
onRenderedFrame(TRenderPort::RenderData renderData)1374 void Previewer::onRenderedFrame(TRenderPort::RenderData renderData) {
1375   m_imp->doOnRenderRasterCompleted(renderData);
1376 }
1377 
1378 //-----------------------------------------------------------------------------
1379 
onFailedFrame(TRenderPort::RenderData renderData)1380 void Previewer::onFailedFrame(TRenderPort::RenderData renderData) {
1381   m_imp->doOnRenderRasterFailed(renderData);
1382 }
1383 
1384 //-----------------------------------------------------------------------------
1385 
1386 //! The suspendRendering method allows suspension of the previewer's rendering
1387 //! activity for safety purposes, typically related to the fact that no
1388 //! rendering
1389 //! process should actually be performed as the underlying scene is about to
1390 //! change
1391 //! or being destroyed. Upon suspension, further rendering requests are silently
1392 //! rejected - and currently active ones are canceled and waited until they are
1393 //! no
1394 //! longer active.
1395 //! NOTE: This method is currently static declared, since the Previewer must be
1396 //! be instanced only after a consistent scene has been initialized. This method
1397 //! is allowed to bypass such limitation.
suspendRendering(bool suspend)1398 void Previewer::suspendRendering(bool suspend) {
1399   suspendedRendering = suspend;
1400   if (suspend && previewerInstance)
1401     previewerInstance->m_imp->m_renderer.stopRendering(true);
1402   if (suspend && previewerInstanceSC)
1403     previewerInstanceSC->m_imp->m_renderer.stopRendering(true);
1404 }
1405