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 ®ion, 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