1
2
3 // System includes
4 #include "tsystem.h"
5 #include "timagecache.h"
6
7 // Geometry
8 #include "tgeometry.h"
9
10 // Image
11 #include "tiio.h"
12 #include "timageinfo.h"
13 #include "trop.h"
14 #include "tropcm.h"
15
16 // Sound
17 #include "tsop.h"
18 #include "tsound.h"
19
20 // Strings
21 #include "tconvert.h"
22
23 // File-related includes
24 #include "tfilepath.h"
25 #include "tfiletype.h"
26 #include "filebrowsermodel.h"
27 #include "fileviewerpopup.h"
28
29 // OpenGL
30 #include "tgl.h"
31 #include "tvectorgl.h"
32 #include "tvectorrenderdata.h"
33
34 // Qt helpers
35 #include "toonzqt/gutil.h"
36 #include "toonzqt/imageutils.h"
37
38 // App-Stage includes
39 #include "tapp.h"
40 #include "toutputproperties.h"
41 #include "toonz/txsheethandle.h"
42 #include "toonz/txshsimplelevel.h"
43 #include "toonz/levelproperties.h"
44 #include "toonz/tscenehandle.h"
45 #include "toonz/toonzscene.h"
46 #include "toonz/sceneproperties.h"
47 #include "toonz/txshlevelhandle.h"
48 #include "toonz/tcamera.h"
49 #include "toonz/preferences.h"
50 #include "toonz/tproject.h"
51
52 // Image painting
53 #include "toonz/imagepainter.h"
54
55 // Preview
56 #include "previewfxmanager.h"
57
58 // Panels
59 #include "pane.h"
60
61 // recent files
62 #include "mainwindow.h"
63
64 // Other widgets
65 #include "toonzqt/flipconsole.h"
66 #include "toonzqt/dvdialog.h"
67 #include "filmstripselection.h"
68 #include "castselection.h"
69 #include "histogrampopup.h"
70
71 // Qt includes
72 #include <QApplication>
73 #include <QDesktopWidget>
74 #include <QSettings>
75 #include <QPainter>
76 #include <QDialogButtonBox>
77 #include <QAbstractButton>
78 #include <QLabel>
79 #include <QRadioButton>
80 #include <QSlider>
81 #include <QButtonGroup>
82 #include <QToolBar>
83 #include <QMainWindow>
84 #include <QUrl>
85 #include <QObject>
86 #include <QDesktopServices>
87 #include <QVBoxLayout>
88 #include <QHBoxLayout>
89 #include <QPushButton>
90
91 #include <stdint.h> // for uintptr_t
92
93 #ifdef _WIN32
94 #include "avicodecrestrictions.h"
95 #endif
96
97 #include "flipbook.h"
98
99 //-----------------------------------------------------------------------------
100
101 using namespace ImageUtils;
102
103 namespace {
getShortcut(const char * id)104 QString getShortcut(const char *id) {
105 return QString::fromStdString(
106 CommandManager::instance()->getShortcutFromId(id));
107 }
108 } // namespace
109
110 //-----------------------------------------------------------------------------
111
112 namespace {
113 /* inline TRect getImageBounds(const TImageP& img)
114 {
115 if(TRasterImageP ri= img)
116 return ri->getRaster()->getBounds();
117 else if(TToonzImageP ti= img)
118 return ti->getRaster()->getBounds();
119 else
120 {
121 TVectorImageP vi= img;
122 return convert(vi->getBBox());
123 }
124 }*/
125
126 //-----------------------------------------------------------------------------
127
getImageBoundsD(const TImageP & img)128 inline TRectD getImageBoundsD(const TImageP &img) {
129 if (TRasterImageP ri = img)
130 return TRectD(0, 0, ri->getRaster()->getLx(), ri->getRaster()->getLy());
131 else if (TToonzImageP ti = img)
132 return TRectD(0, 0, ti->getSize().lx, ti->getSize().ly);
133 else {
134 TVectorImageP vi = img;
135 return vi->getBBox();
136 }
137 }
138 } // namespace
139
140 //=============================================================================
141 /*! \class FlipBook
142 \brief The FlipBook class provides to view level images.
143
144 Inherits \b QWidget.
145
146 The object is composed of grid layout \b QGridLayout which
147 contains an image
148 viewer \b ImageViewer and a double button bar. It is possible
149 decide widget
150 title and which button bar show by setting QString and bool in
151 constructor.
152
153 You can set level to show in FlipBook using \b setLevel(); and
154 call
155 onDrawFrame() to show FlipBook current frame. The current frame
156 can be
157 set directly using setCurrentFrame(int index) or using slot
158 methods connected
159 to button bar.
160
161 \sa FlipBookPool class.
162 */
FlipBook(QWidget * parent,QString viewerTitle,std::vector<int> flipConsoleButtonMask,UCHAR flags,bool isColorModel)163 FlipBook::FlipBook(QWidget *parent, QString viewerTitle,
164 std::vector<int> flipConsoleButtonMask, UCHAR flags,
165 bool isColorModel) //, bool showOnlyPlayBackgroundButton)
166 : QWidget(parent),
167 m_viewerTitle(viewerTitle),
168 m_levelNames(),
169 m_levels(),
170 m_playSound(false),
171 m_snd(0),
172 m_player(0)
173 //, m_doCompare(false)
174 ,
175 m_currentFrameToSave(0),
176 m_lw(),
177 m_lr(),
178 m_loadPopup(0),
179 m_savePopup(0),
180 m_shrink(1),
181 m_isPreviewFx(false),
182 m_previewedFx(0),
183 m_previewXsh(0),
184 m_previewUpdateTimer(this),
185 m_xl(0),
186 m_title1(),
187 m_poolIndex(-1),
188 m_freezed(false),
189 m_loadbox(),
190 m_dim(),
191 m_loadboxes(),
192 m_freezeButton(0),
193 m_flags(flags) {
194 setAcceptDrops(true);
195 setFocusPolicy(Qt::StrongFocus);
196
197 // flipConsoleButtonMask = flipConsoleButtonMask & ~FlipConsole::eSubCamera;
198
199 ImageUtils::FullScreenWidget *fsWidget =
200 new ImageUtils::FullScreenWidget(this);
201
202 m_imageViewer = new ImageViewer(
203 fsWidget, this,
204 std::find(flipConsoleButtonMask.begin(), flipConsoleButtonMask.end(),
205 FlipConsole::eHisto) == flipConsoleButtonMask.end());
206 fsWidget->setWidget(m_imageViewer);
207
208 setFocusProxy(m_imageViewer);
209 m_title = m_viewerTitle;
210 m_imageViewer->setIsColorModel(isColorModel);
211
212 // layout
213 QVBoxLayout *mainLayout = new QVBoxLayout(this);
214 mainLayout->setMargin(0);
215 mainLayout->setSpacing(0);
216 {
217 mainLayout->addWidget(fsWidget, 1);
218 m_flipConsole = new FlipConsole(
219 mainLayout, flipConsoleButtonMask, true, 0,
220 (viewerTitle == "") ? "FlipConsole" : viewerTitle, this, !isColorModel);
221 mainLayout->addWidget(m_flipConsole);
222 }
223 setLayout(mainLayout);
224
225 // signal-slot connection
226 bool ret = connect(m_flipConsole, SIGNAL(buttonPressed(FlipConsole::EGadget)),
227 this, SLOT(onButtonPressed(FlipConsole::EGadget)));
228
229 m_flipConsole->setFrameRate(TApp::instance()
230 ->getCurrentScene()
231 ->getScene()
232 ->getProperties()
233 ->getOutputProperties()
234 ->getFrameRate());
235
236 mainLayout->addWidget(m_flipConsole);
237
238 m_previewUpdateTimer.setSingleShot(true);
239
240 ret = ret && connect(parentWidget(), SIGNAL(closeButtonPressed()), this,
241 SLOT(onCloseButtonPressed()));
242 ret = ret && connect(parentWidget(), SIGNAL(doubleClick(QMouseEvent *)), this,
243 SLOT(onDoubleClick(QMouseEvent *)));
244 ret = ret && connect(&m_previewUpdateTimer, SIGNAL(timeout()), this,
245 SLOT(performFxUpdate()));
246
247 assert(ret);
248
249 m_viewerTitle = (m_viewerTitle.isEmpty()) ? tr("Flipbook") : m_viewerTitle;
250 parentWidget()->setWindowTitle(m_viewerTitle);
251 }
252
253 //-----------------------------------------------------------------------------
254 /*! add freeze button to the flipbook. called from the function
255 PreviewFxManager::openFlipBook.
256 this button will hide for re-use, at onCloseButtonPressed
257 */
addFreezeButtonToTitleBar()258 void FlipBook::addFreezeButtonToTitleBar() {
259 // If there is the button already, then reuse it.
260 if (m_freezeButton) {
261 m_freezeButton->show();
262 return;
263 }
264
265 // If there is not, then newly make it
266 TPanel *panel = qobject_cast<TPanel *>(parentWidget());
267 if (panel) {
268 TPanelTitleBar *titleBar = panel->getTitleBar();
269 m_freezeButton = new TPanelTitleBarButton(
270 titleBar, getIconThemePath("actions/20/pane_freeze.svg"));
271 m_freezeButton->setToolTip("Freeze");
272 titleBar->add(QPoint(-64, 0), m_freezeButton);
273 connect(m_freezeButton, SIGNAL(toggled(bool)), this, SLOT(freeze(bool)));
274 QPoint p(titleBar->width() - 64, 0);
275 m_freezeButton->move(p);
276 m_freezeButton->show();
277 }
278 }
279
280 //-----------------------------------------------------------------------------
281
freeze(bool on)282 void FlipBook::freeze(bool on) {
283 if (on)
284 freezePreview();
285 else
286 unfreezePreview();
287 }
288
289 //-----------------------------------------------------------------------------
290
focusInEvent(QFocusEvent * e)291 void FlipBook::focusInEvent(QFocusEvent *e) {
292 m_flipConsole->makeCurrent();
293 QWidget::focusInEvent(e);
294 }
295
296 //-----------------------------------------------------------------------------
297
298 namespace {
299
300 enum { eBegin, eIncrement, eEnd };
301
302 static DVGui::ProgressDialog *Pd = 0;
303
304 class ProgressBarMessager final : public TThread::Message {
305 public:
306 int m_choice;
307 int m_val;
308 QString m_str;
ProgressBarMessager(int choice,int val,const QString & str="")309 ProgressBarMessager(int choice, int val, const QString &str = "")
310 : m_choice(choice), m_val(val), m_str(str) {}
onDeliver()311 void onDeliver() override {
312 switch (m_choice) {
313 case eBegin:
314 if (!Pd)
315 Pd = new DVGui::ProgressDialog(
316 QObject::tr("Saving previewed frames...."), QObject::tr("Cancel"),
317 0, m_val);
318 else
319 Pd->setMaximum(m_val);
320 Pd->show();
321 break;
322 case eIncrement:
323 if (Pd->wasCanceled()) {
324 delete Pd;
325 Pd = 0;
326 } else {
327 // if (m_val==Pd->maximum()) Pd->hide();
328 Pd->setValue(m_val);
329 }
330 break;
331 case eEnd: {
332 DVGui::info(m_str);
333 delete Pd;
334 Pd = 0;
335 } break;
336 default:
337 assert(false);
338 }
339 }
340
clone() const341 TThread::Message *clone() const override {
342 return new ProgressBarMessager(*this);
343 }
344 };
345
346 } // namespace
347
348 //=============================================================================
349
LoadImagesPopup(FlipBook * flip)350 LoadImagesPopup::LoadImagesPopup(FlipBook *flip)
351 : FileBrowserPopup(tr("Load Images"), Options(WITH_APPLY_BUTTON),
352 tr("Append"), new QFrame(0))
353 , m_flip(flip)
354 , m_minFrame(0)
355 , m_maxFrame(1000000)
356 , m_step(1)
357 , m_shrink(1) {
358 QFrame *frameRangeFrame = (QFrame *)m_customWidget;
359
360 frameRangeFrame->setObjectName("customFrame");
361 frameRangeFrame->setFrameStyle(QFrame::StyledPanel);
362 // frameRangeFrame->setFixedHeight(30);
363
364 m_fromField = new DVGui::LineEdit(this);
365 m_toField = new DVGui::LineEdit(this);
366 m_stepField = new DVGui::LineEdit("1", this);
367 m_shrinkField = new DVGui::LineEdit("1", this);
368
369 // Define the append/load filter types
370 m_appendFilterTypes << "3gp"
371 << "mov"
372 << "jpg"
373 << "png"
374 << "tga"
375 << "tif"
376 << "tiff"
377 << "bmp"
378 << "sgi"
379 << "rgb"
380 << "nol";
381
382 #ifdef _WIN32
383 m_appendFilterTypes << "avi";
384 #endif
385
386 m_loadFilterTypes << "tlv"
387 << "pli" << m_appendFilterTypes;
388 m_appendFilterTypes << "psd";
389
390 // layout
391 QHBoxLayout *frameRangeLayout = new QHBoxLayout();
392 frameRangeLayout->setMargin(5);
393 frameRangeLayout->setSpacing(5);
394 {
395 frameRangeLayout->addStretch(1);
396
397 frameRangeLayout->addWidget(new QLabel(tr("From:")), 0);
398 frameRangeLayout->addWidget(m_fromField, 0);
399
400 frameRangeLayout->addSpacing(5);
401
402 frameRangeLayout->addWidget(new QLabel(tr("To:")), 0);
403 frameRangeLayout->addWidget(m_toField, 0);
404
405 frameRangeLayout->addSpacing(10);
406
407 frameRangeLayout->addWidget(new QLabel(tr("Step:")), 0);
408 frameRangeLayout->addWidget(m_stepField, 0);
409
410 frameRangeLayout->addSpacing(5);
411
412 frameRangeLayout->addWidget(new QLabel(tr("Shrink:")));
413 frameRangeLayout->addWidget(m_shrinkField, 0);
414 }
415 frameRangeFrame->setLayout(frameRangeLayout);
416
417 // Make signal-slot connections
418 bool ret = true;
419
420 ret = ret && connect(m_fromField, SIGNAL(editingFinished()), this,
421 SLOT(onEditingFinished()));
422 ret = ret && connect(m_toField, SIGNAL(editingFinished()), this,
423 SLOT(onEditingFinished()));
424 ret = ret && connect(m_stepField, SIGNAL(editingFinished()), this,
425 SLOT(onEditingFinished()));
426 ret = ret && connect(m_shrinkField, SIGNAL(editingFinished()), this,
427 SLOT(onEditingFinished()));
428
429 ret = ret && connect(this, SIGNAL(filePathClicked(const TFilePath &)),
430 SLOT(onFilePathClicked(const TFilePath &)));
431
432 assert(ret);
433
434 setOkText(tr("Load"));
435
436 setWindowTitle(tr("Load / Append Images"));
437 }
438
439 //-----------------------------------------------------------------------------
440
onEditingFinished()441 void LoadImagesPopup::onEditingFinished() {
442 int val;
443 val = m_fromField->text().toInt();
444 m_from = (val < m_minFrame) ? m_minFrame : val;
445 val = m_toField->text().toInt();
446 m_to = (val > m_maxFrame) ? m_maxFrame : val;
447
448 if (m_to < m_from) m_to = m_from;
449
450 val = m_stepField->text().toInt();
451 m_step = (val < 1) ? 1 : val;
452 val = m_shrinkField->text().toInt();
453 m_shrink = (val < 1) ? 1 : val;
454
455 m_fromField->setText(QString::number(m_from));
456 m_toField->setText(QString::number(m_to));
457 m_stepField->setText(QString::number(m_step));
458 m_shrinkField->setText(QString::number(m_shrink));
459 }
460
461 //-----------------------------------------------------------------------------
462
execute()463 bool LoadImagesPopup::execute() { return doLoad(false); }
464
465 //-----------------------------------------------------------------------------
466 /*! Append images with apply button
467 */
executeApply()468 bool LoadImagesPopup::executeApply() { return doLoad(true); }
469
470 //-----------------------------------------------------------------------------
471
doLoad(bool append)472 bool LoadImagesPopup::doLoad(bool append) {
473 if (m_selectedPaths.empty()) return false;
474
475 ::viewFile(*m_selectedPaths.begin(), m_from, m_to, m_step, m_shrink, 0,
476 m_flip, append);
477
478 // register recent files
479 std::set<TFilePath>::const_iterator pt;
480 for (pt = m_selectedPaths.begin(); pt != m_selectedPaths.end(); ++pt) {
481 RecentFiles::instance()->addFilePath(
482 toQString(
483 TApp::instance()->getCurrentScene()->getScene()->decodeFilePath(
484 (*pt))),
485 RecentFiles::Flip);
486 }
487 return true;
488 }
489
490 //-----------------------------------------------------------------------------
491
onFilePathClicked(const TFilePath & fp)492 void LoadImagesPopup::onFilePathClicked(const TFilePath &fp) {
493 TLevel::Iterator it;
494 TLevelP level;
495 TLevelReaderP lr;
496
497 if (fp == TFilePath()) goto clear;
498
499 lr = TLevelReaderP(fp);
500 if (!lr) goto clear;
501
502 level = lr->loadInfo();
503
504 if (!level || level->getFrameCount() == 0) goto clear;
505
506 it = level->begin();
507 m_to = m_from = it->first.getNumber();
508
509 for (; it != level->end(); ++it) m_to = it->first.getNumber();
510
511 if (m_from == -2 && m_to == -2) m_from = m_to = 1;
512
513 m_minFrame = m_from;
514 m_maxFrame = m_to;
515 m_fromField->setText(QString::number(m_from));
516 m_toField->setText(QString::number(m_to));
517 return;
518
519 clear:
520
521 m_minFrame = 0;
522 m_maxFrame = 10000000;
523 m_fromField->setText("");
524 m_toField->setText("");
525 }
526
527 //=============================================================================
528
SaveImagesPopup(FlipBook * flip)529 SaveImagesPopup::SaveImagesPopup(FlipBook *flip)
530 : FileBrowserPopup(tr("Save Flipbook Images")), m_flip(flip) {
531 setOkText(tr("Save"));
532 }
533
execute()534 bool SaveImagesPopup::execute() {
535 if (m_selectedPaths.empty()) return false;
536
537 return m_flip->doSaveImages(*m_selectedPaths.begin());
538 }
539
540 //=============================================================================
loadImages()541 void FlipBook::loadImages() {
542 if (!m_loadPopup) {
543 m_loadPopup = new LoadImagesPopup(this); //, frameRangeFrame);
544 // move the initial folder to the project root
545 m_loadPopup->setFolder(
546 TProjectManager::instance()->getCurrentProjectPath().getParentDir());
547 }
548
549 m_loadPopup->show();
550 m_loadPopup->raise();
551 m_loadPopup->activateWindow();
552 }
553
554 //=============================================================================
555
canAppend()556 bool FlipBook::canAppend() {
557 // Images can be appended if:
558 // a) There is a name (in particular, an extension) representing currently
559 // held ones.
560 // b) This flipbook is not holding a preview (inappropriate and problematic).
561 // c) The level has no palette. Otherwise, appended images may have a
562 // different palette.
563 return !m_levels.empty() && !m_previewedFx && !m_palette;
564 }
565
566 //=============================================================================
567
saveImages()568 void FlipBook::saveImages() {
569 if (!m_savePopup) m_savePopup = new SaveImagesPopup(this);
570
571 // initialize the default path every time
572 TOutputProperties *op = TApp::instance()
573 ->getCurrentScene()
574 ->getScene()
575 ->getProperties()
576 ->getOutputProperties();
577 m_savePopup->setFolder(TApp::instance()
578 ->getCurrentScene()
579 ->getScene()
580 ->decodeFilePath(op->getPath())
581 .getParentDir());
582 m_savePopup->setFilename(op->getPath().withFrame().withoutParentDir());
583
584 m_savePopup->show();
585 m_savePopup->raise();
586 m_savePopup->activateWindow();
587 }
588
589 //=============================================================================
590
~FlipBook()591 FlipBook::~FlipBook() {
592 if (m_loadPopup) delete m_loadPopup;
593 if (m_savePopup) delete m_savePopup;
594 }
595
596 //=============================================================================
597
doSaveImages(TFilePath fp)598 bool FlipBook::doSaveImages(TFilePath fp) {
599 QStringList formats;
600 TLevelWriter::getSupportedFormats(formats, true);
601 Tiio::Writer::getSupportedFormats(formats, true);
602
603 ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
604 TOutputProperties *outputSettings =
605 scene->getProperties()->getOutputProperties();
606
607 std::string ext = fp.getType();
608
609 // Open a notice that the previewFx is rendered in 8bpc regardless of the
610 // output settings.
611 if (m_isPreviewFx && outputSettings->getRenderSettings().m_bpp == 64) {
612 QString question =
613 "Save previewed images :\nImages will be saved in 8 bit per channel "
614 "with this command.\nDo you want to save images?";
615 int ret =
616 DVGui::MsgBox(question, QObject::tr("Save"), QObject::tr("Cancel"), 0);
617 if (ret == 2 || ret == 0) return false;
618 }
619
620 #ifdef _WIN32
621 if (ext == "avi") {
622 TPropertyGroup *props = outputSettings->getFileFormatProperties(ext);
623 std::string codecName = props->getProperty(0)->getValueAsString();
624 TDimension res = scene->getCurrentCamera()->getRes();
625 if (!AviCodecRestrictions::canWriteMovie(::to_wstring(codecName), res)) {
626 QString msg(
627 QObject::tr("The resolution of the output camera does not fit with "
628 "the options chosen for the output file format."));
629 DVGui::warning(msg);
630 return false;
631 }
632 }
633 #endif
634
635 if (ext == "") {
636 ext = outputSettings->getPath().getType();
637 fp = fp.withType(ext);
638 }
639 if (fp.getName() == "") {
640 DVGui::warning(
641 tr("The file name cannot be empty or contain any of the following "
642 "characters:(new line) \\ / : * ? \" |"));
643 return false;
644 }
645
646 if (!formats.contains(QString::fromStdString(ext))) {
647 DVGui::warning(
648 tr("It is not possible to save because the selected file format is not "
649 "supported."));
650 return false;
651 }
652
653 int from, to, step;
654 m_flipConsole->getFrameRange(from, to, step);
655
656 if (m_currentFrameToSave != 0) {
657 DVGui::info("Already saving!");
658 return true;
659 }
660
661 if (TFileType::getInfo(fp) == TFileType::RASTER_IMAGE || ext == "pct" ||
662 ext == "pic" || ext == "pict") // pct e' un formato"livello" (ha i
663 // settings di quicktime) ma fatto di
664 // diversi frames
665 fp = fp.withFrame(TFrameId::EMPTY_FRAME);
666
667 fp = scene->decodeFilePath(fp);
668 bool exists = TFileStatus(fp.getParentDir()).doesExist();
669 if (!exists) {
670 try {
671 TFilePath parent = fp.getParentDir();
672 TSystem::mkDir(parent);
673 DvDirModel::instance()->refreshFolder(parent.getParentDir());
674 } catch (TException &e) {
675 DVGui::error("Cannot create " + toQString(fp.getParentDir()) + " : " +
676 QString(::to_string(e.getMessage()).c_str()));
677 return false;
678 } catch (...) {
679 DVGui::error("Cannot create " + toQString(fp.getParentDir()));
680 return false;
681 }
682 }
683
684 if (TSystem::doesExistFileOrLevel(fp)) {
685 QString question(tr("File %1 already exists.\nDo you want to overwrite it?")
686 .arg(toQString(fp)));
687 int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
688 QObject::tr("Cancel"));
689 if (ret == 2) return false;
690 }
691
692 try {
693 m_lw = TLevelWriterP(fp,
694 outputSettings->getFileFormatProperties(fp.getType()));
695 } catch (...) {
696 DVGui::error("It is not possible to save Flipbook content.");
697 return false;
698 }
699
700 m_lw->setFrameRate(outputSettings->getFrameRate());
701
702 m_currentFrameToSave = 1;
703
704 ProgressBarMessager(eBegin, m_framesCount).sendBlocking();
705
706 QTimer::singleShot(50, this, SLOT(saveImage()));
707 return true;
708 }
709
710 //-----------------------------------------------------------------------------
711
saveImage()712 void FlipBook::saveImage() {
713 static int savedFrames = 0;
714
715 assert(Pd);
716 int from, to, step;
717
718 m_flipConsole->getFrameRange(from, to, step);
719
720 for (; m_currentFrameToSave <= m_framesCount; m_currentFrameToSave++) {
721 ProgressBarMessager(eIncrement, m_currentFrameToSave).sendBlocking();
722 if (!Pd) break;
723
724 int actualFrame = from + (m_currentFrameToSave - 1) * step;
725 TImageP img = getCurrentImage(actualFrame);
726 if (!img) continue;
727 TImageWriterP writer = m_lw->getFrameWriter(TFrameId(actualFrame));
728 bool failureOnSaving = false;
729 if (!writer) continue;
730 try {
731 writer->save(img);
732 } catch (...) {
733 QString str(tr("It is not possible to save Flipbook content."));
734 ProgressBarMessager(eEnd, 0, str).send();
735 m_currentFrameToSave = 0;
736 m_lw = TLevelWriterP();
737 savedFrames = 0;
738 return;
739 }
740 savedFrames++;
741 // if (!m_pb->changeFraction(m_currentFrameToSave,
742 // TApp::instance()->getCurrentXsheet()->getXsheet()->getFrameCount()))
743 // break;
744 m_currentFrameToSave++;
745
746 QTimer::singleShot(50, this, SLOT(saveImage()));
747 return;
748 }
749
750 QString str = tr("Saved %1 frames out of %2 in %3")
751 .arg(std::to_string(savedFrames).c_str())
752 .arg(std::to_string(m_framesCount).c_str())
753 .arg(::to_string(m_lw->getFilePath()).c_str());
754
755 if (!Pd) str = "Canceled! " + str;
756
757 ProgressBarMessager(eEnd, 0, str).send();
758
759 m_currentFrameToSave = 0;
760 m_lw = TLevelWriterP();
761 savedFrames = 0;
762 }
763
764 //=============================================================================
765
onButtonPressed(FlipConsole::EGadget button)766 void FlipBook::onButtonPressed(FlipConsole::EGadget button) {
767 switch (button) {
768 case FlipConsole::eSound:
769 m_playSound = !m_playSound;
770 break;
771
772 case FlipConsole::eHisto:
773 m_imageViewer->showHistogram();
774 break;
775
776 case FlipConsole::eSaveImg: {
777 TRect loadbox = m_loadbox;
778 m_loadbox = TRect();
779 TImageP img = getCurrentImage(m_flipConsole->getCurrentFrame());
780 m_loadbox = loadbox;
781 if (!img) {
782 DVGui::warning(tr("There are no rendered images to save."));
783 return;
784 } else if ((TVectorImageP)img) {
785 DVGui::warning(
786 tr("It is not possible to take or compare snapshots for Toonz vector "
787 "levels."));
788 return;
789 }
790 TRasterImageP ri(img);
791 TToonzImageP ti(img);
792 TImageP clonedImg;
793 if (ri)
794 clonedImg = TRasterImageP(ri->getRaster()->clone());
795 else
796 clonedImg = TToonzImageP(ti->getRaster()->clone(), ti->getSavebox());
797 TImageCache::instance()->add(QString("TnzCompareImg"), clonedImg);
798 break;
799 }
800
801 case FlipConsole::eCompare:
802 if ((TVectorImageP)getCurrentImage(m_flipConsole->getCurrentFrame())) {
803 DVGui::warning(
804 tr("It is not possible to take or compare snapshots for Toonz vector "
805 "levels."));
806 m_flipConsole->setChecked(FlipConsole::eCompare, false);
807 return;
808 }
809 break;
810
811 case FlipConsole::eSave:
812 saveImages();
813 break;
814 default:
815 break;
816 }
817 }
818
819 //=============================================================================
820 // FlipBookPool
821 //-----------------------------------------------------------------------------
822
823 /*! \class FlipBookPool
824 \brief The FlipBookPool class is used to store used flipbook
825 viewers.
826
827 Flipbooks are generally intended as temporary but friendly floating widgets,
828 that gets displayed when a rendered scene or image needs to be shown.
829 Since a user may require that the geometry of a flipbook is to be remembered
830 between rendering tasks - perhaps even between different Toonz sessions -
831 flipbooks are always stored for later use in a \b FlipBookPool class.
832
833 This class implements the basical features to \b pop a flipbook from the
834 pool
835 or \b push a used one; plus, it provides the \b save and \b load functions
836 for persistent storage between Toonz sessions.
837
838 \sa FlipBook class.
839 */
840
FlipBookPool()841 FlipBookPool::FlipBookPool() : m_overallFlipCount(0) {
842 qRegisterMetaType<ImagePainter::VisualSettings>(
843 "ImagePainter::VisualSettings");
844 }
845
846 //-----------------------------------------------------------------------------
847
~FlipBookPool()848 FlipBookPool::~FlipBookPool() {}
849
850 //-----------------------------------------------------------------------------
851
instance()852 FlipBookPool *FlipBookPool::instance() {
853 static FlipBookPool poolInstance;
854 return &poolInstance;
855 }
856
857 //-----------------------------------------------------------------------------
858
push(FlipBook * flipbook)859 void FlipBookPool::push(FlipBook *flipbook) {
860 m_pool.insert(pair<int, FlipBook *>(flipbook->getPoolIndex(), flipbook));
861 }
862
863 //-----------------------------------------------------------------------------
864
865 //! Extracts the first unused flipbook from the flipbook pool.
866 //! If all known flipbooks are shown, allocates a new flipbook with the
867 //! first unused flipbook geometry in a geometry pool.
868 //! Again, if all recorded geometry are used by some existing flipbook, a
869 //! default geometry is used.
pop()870 FlipBook *FlipBookPool::pop() {
871 FlipBook *flipbook;
872 TPanel *panel;
873
874 TMainWindow *currentRoom = TApp::instance()->getCurrentRoom();
875
876 if (m_pool.empty()) {
877 panel = TPanelFactory::createPanel(currentRoom, "FlipBook");
878 panel->setFloating(true);
879
880 flipbook = static_cast<FlipBook *>(panel->widget());
881
882 // Set geometry
883 static int x = 0, y = 0;
884 if (m_geometryPool.empty()) {
885 panel->setGeometry(x += 50, y += 50, 400, 300);
886 flipbook->setPoolIndex(m_overallFlipCount);
887 m_overallFlipCount++;
888 } else {
889 flipbook->setPoolIndex(m_geometryPool.begin()->first);
890 QRect geometry(m_geometryPool.begin()->second);
891 panel->setGeometry(geometry);
892 if ((geometry & QApplication::desktop()->availableGeometry(panel))
893 .isEmpty())
894 panel->move(x += 50, y += 50);
895 m_geometryPool.erase(m_geometryPool.begin());
896 }
897 } else {
898 flipbook = m_pool.begin()->second;
899 panel = (TPanel *)flipbook->parent();
900 m_pool.erase(m_pool.begin());
901 }
902
903 // The panel need to be added to currentRoom's layout control.
904 currentRoom->addDockWidget(panel);
905 panel->raise();
906 panel->show();
907
908 return flipbook;
909 }
910
911 //-----------------------------------------------------------------------------
912
913 //! Saves the content of this flipbook pool.
save() const914 void FlipBookPool::save() const {
915 QSettings history(toQString(m_historyPath), QSettings::IniFormat);
916 history.clear();
917
918 history.setValue("count", m_overallFlipCount);
919
920 history.beginGroup("flipbooks");
921
922 std::map<int, FlipBook *>::const_iterator it;
923 for (it = m_pool.begin(); it != m_pool.end(); ++it) {
924 history.beginGroup(QString::number(it->first));
925 TPanel *panel = static_cast<TPanel *>(it->second->parent());
926 history.setValue("geometry", panel->geometry());
927 history.endGroup();
928 }
929
930 std::map<int, QRect>::const_iterator jt;
931 for (jt = m_geometryPool.begin(); jt != m_geometryPool.end(); ++jt) {
932 history.beginGroup(QString::number(jt->first));
933 history.setValue("geometry", jt->second);
934 history.endGroup();
935 }
936
937 history.endGroup();
938 }
939
940 //-----------------------------------------------------------------------------
941
942 //! Loads the pool from input history
load(const TFilePath & historyPath)943 void FlipBookPool::load(const TFilePath &historyPath) {
944 QSettings history(toQString(historyPath), QSettings::IniFormat);
945 m_historyPath = historyPath;
946
947 m_pool.clear();
948 m_geometryPool.clear();
949
950 m_overallFlipCount = history.value("count").toInt();
951
952 history.beginGroup("flipbooks");
953
954 QStringList flipBooks(history.childGroups());
955 QStringList::iterator it;
956 for (it = flipBooks.begin(); it != flipBooks.end(); ++it) {
957 history.beginGroup(*it);
958
959 // Retrieve flipbook geometry
960 QVariant geom = history.value("geometry");
961
962 // Insert geometry
963 m_geometryPool.insert(pair<int, QRect>(it->toInt(), geom.toRect()));
964
965 history.endGroup();
966 }
967
968 history.endGroup();
969 }
970
971 //=============================================================================
972
973 //! Returns the level frame number corresponding to passed flipbook index
flipbookIndexToLevelFrame(int index)974 TFrameId FlipBook::Level::flipbookIndexToLevelFrame(int index) {
975 TLevel::Iterator it;
976 int levelPos;
977 if (m_incrementalIndexing) {
978 levelPos = (index - 1) * m_step;
979 it = m_level->getTable()->find(m_fromIndex);
980 advance(it, levelPos);
981 } else {
982 levelPos = m_fromIndex + (index - 1) * m_step;
983 it = m_level->getTable()->find(levelPos);
984 }
985 if (it == m_level->end()) return TFrameId();
986 return it->first;
987 }
988
989 //-----------------------------------------------------------------------------
990
991 //! Returns the number of flipbook indexes available for this level
getIndexesCount()992 int FlipBook::Level::getIndexesCount() {
993 return m_incrementalIndexing ? (m_level->getFrameCount() - 1) / m_step + 1
994 : (m_toIndex - m_fromIndex) / m_step + 1;
995 }
996
997 //=============================================================================
998
isSavable() const999 bool FlipBook::isSavable() const {
1000 if (m_levels.empty()) return false;
1001
1002 for (int i = 0; i < m_levels.size(); i++)
1003 if (m_levels[i].m_fp != TFilePath() &&
1004 (m_levels[i].m_fp.getType() == "tlv" ||
1005 m_levels[i].m_fp.getType() == "pli"))
1006 return false;
1007
1008 return true;
1009 }
1010
1011 //=============================================================================
1012
1013 /*! Set the level contained in \b fp to FlipBook; if level exist show in image
1014 viewer its first frame, set current frame to 1.
1015 It's possible to change level palette, in fact if \b palette is
1016 different
1017 from 0 set level palette to \b palette.
1018 */
setLevel(const TFilePath & fp,TPalette * palette,int from,int to,int step,int shrink,TSoundTrack * snd,bool append,bool isToonzOutput)1019 void FlipBook::setLevel(const TFilePath &fp, TPalette *palette, int from,
1020 int to, int step, int shrink, TSoundTrack *snd,
1021 bool append, bool isToonzOutput) {
1022 try {
1023 if (!append) {
1024 clearCache();
1025 m_levelNames.clear();
1026 m_levels.clear();
1027 }
1028 m_snd = 0;
1029 m_xl = 0;
1030
1031 m_flipConsole->enableProgressBar(false);
1032 m_flipConsole->setProgressBarStatus(0);
1033 m_flipConsole->enableButton(FlipConsole::eSound, snd != 0);
1034 m_flipConsole->enableButton(FlipConsole::eDefineLoadBox, true);
1035 m_flipConsole->enableButton(FlipConsole::eUseLoadBox, true);
1036 if (fp == TFilePath()) return;
1037
1038 m_shrink = shrink;
1039
1040 if (fp.getDots() == ".." && fp.getType() != "noext")
1041 m_levelNames.push_back(toQString(fp.withoutParentDir().withFrame()));
1042 else
1043 m_levelNames.push_back(toQString(fp.withoutParentDir()));
1044
1045 m_snd = snd;
1046
1047 if (TSystem::doesExistFileOrLevel(fp)) // is a viewfile
1048 {
1049 // m_flipConsole->enableButton(FlipConsole::eCheckBg,
1050 // true);//fp.getType()!="pli");
1051
1052 m_lr = TLevelReaderP(fp);
1053
1054 bool supportsRandomAccess = doesSupportRandomAccess(fp, isToonzOutput);
1055 if (supportsRandomAccess) m_lr->enableRandomAccessRead(isToonzOutput);
1056
1057 bool randomAccessRead = supportsRandomAccess && isToonzOutput;
1058 bool incrementalIndexing = m_isPreviewFx ? true : false;
1059
1060 TLevelP level = m_lr->loadInfo();
1061
1062 if (!level || level->getFrameCount() == 0) {
1063 if (m_flags & eDontKeepFilesOpened) m_lr = TLevelReaderP();
1064 return;
1065 }
1066
1067 // For the color model, get the reference fids from palette and delete
1068 // unneeded from the table
1069 if (m_imageViewer->isColorModel() && palette) {
1070 std::vector<TFrameId> fids = palette->getRefLevelFids();
1071
1072 // when loading a single-frame, standard raster image into the
1073 // ColorModel, skip here.
1074 // If the fid == NO_FRAME(=-2), fids stores 0.
1075 if (!fids.empty() && !(fids.size() == 1 && fids[0].getNumber() == 0)) {
1076 // make the fid list to be deleted
1077 std::vector<TFrameId> deleteList;
1078 TLevel::Iterator it;
1079 for (it = level->begin(); it != level->end(); it++) {
1080 // If the fid is not included in the reference list, then delete it
1081 int i;
1082 for (i = 0; i < (int)fids.size(); i++) {
1083 if (fids[i].getNumber() == it->first.getNumber()) break;
1084 }
1085 if (i == fids.size()) {
1086 deleteList.push_back(it->first);
1087 }
1088 }
1089 // delete list items here
1090 if (!deleteList.empty())
1091 for (int i = 0; i < (int)deleteList.size(); i++)
1092 level->getTable()->erase(deleteList[i]);
1093 }
1094 }
1095
1096 int fromIndex, toIndex;
1097
1098 // in order to avoid that the current frame unexpectedly moves to 1 on the
1099 // Color Model once editing the style
1100 int current = -1;
1101
1102 if (from == -1 && to == -1) {
1103 fromIndex = level->begin()->first.getNumber();
1104 toIndex = (--level->end())->first.getNumber();
1105 if (m_imageViewer->isColorModel())
1106 current = m_flipConsole->getCurrentFrame();
1107 incrementalIndexing = true;
1108 } else {
1109 TLevel::Iterator it = level->begin();
1110
1111 // Adjust the frame interval to read. There is one special case:
1112 // If the level read did not support random access, *AND* the level to
1113 // show was just rendered,
1114 // we have to assume that no level update happened, and the
1115 // from-to-step infos are lost.
1116 // So, shift the requested interval from 1 and place step to 1.
1117 fromIndex = from;
1118 toIndex = to;
1119 if (isToonzOutput && !supportsRandomAccess) {
1120 fromIndex = 1;
1121 toIndex = level->getFrameCount();
1122 step = 1;
1123 }
1124
1125 if (level->begin()->first.getNumber() != TFrameId::NO_FRAME) {
1126 fromIndex = std::max(fromIndex, level->begin()->first.getNumber());
1127 toIndex = std::min(toIndex, (--level->end())->first.getNumber());
1128 } else {
1129 fromIndex = level->begin()->first.getNumber();
1130 toIndex = (--level->end())->first.getNumber();
1131 incrementalIndexing = true;
1132 }
1133
1134 // Workaround to display simple background images when loading from
1135 // the right-click menu context
1136 fromIndex = std::min(fromIndex, toIndex);
1137 }
1138
1139 Level levelToPush(level, fp, fromIndex, toIndex, step);
1140 levelToPush.m_randomAccessRead = randomAccessRead;
1141 levelToPush.m_incrementalIndexing = incrementalIndexing;
1142
1143 int formatIdx = Preferences::instance()->matchLevelFormat(fp);
1144 if (formatIdx >= 0 &&
1145 Preferences::instance()
1146 ->levelFormat(formatIdx)
1147 .m_options.m_premultiply) {
1148 levelToPush.m_premultiply = true;
1149 }
1150
1151 m_levels.push_back(levelToPush);
1152
1153 // Get the frames count to be shown in this flipbook level
1154 m_framesCount = levelToPush.getIndexesCount();
1155
1156 assert(m_framesCount <= level->getFrameCount());
1157
1158 // this value will be used in loadAndCacheAllTlvImages later
1159 int addingFrameAmount = m_framesCount;
1160
1161 if (append && !m_levels.empty()) {
1162 int oldFrom, oldTo, oldStep;
1163 m_flipConsole->getFrameRange(oldFrom, oldTo, oldStep);
1164 assert(oldFrom == 1);
1165 assert(oldStep == 1);
1166 m_framesCount += oldTo;
1167 }
1168
1169 m_flipConsole->setFrameRange(1, m_framesCount, 1, current);
1170
1171 if (palette && level->getPalette() != palette) level->setPalette(palette);
1172
1173 m_palette = level->getPalette();
1174
1175 const TImageInfo *ii = m_lr->getImageInfo();
1176
1177 if (ii) m_dim = TDimension(ii->m_lx / m_shrink, ii->m_ly / m_shrink);
1178
1179 int levelFrameCount = 0;
1180 for (int lev = 0; lev < m_levels.size(); lev++)
1181 levelFrameCount += m_levels[lev].m_level->getFrameCount();
1182
1183 if (levelFrameCount == 1)
1184 m_title = " :: 1 Frame";
1185 else
1186 m_title = " :: " + QString::number(levelFrameCount) + " Frames";
1187
1188 // color model does not concern about the pixel size
1189 if (ii && !m_imageViewer->isColorModel())
1190 m_title = m_title + " :: " + QString::number(ii->m_lx) + "x" +
1191 QString::number(ii->m_ly) + " Pixels";
1192
1193 if (shrink > 1)
1194 m_title = m_title + " :: " + "Shrink: " + QString::number(shrink);
1195
1196 // when using the flip module, this signal is to show the loaded level
1197 // names in application's title bar
1198 QString arg = QString("Flip : %1").arg(m_levelNames[0]);
1199 emit imageLoaded(arg);
1200
1201 // When viewing the tlv, try to cache all frames at the beginning.
1202 if (!m_imageViewer->isColorModel() && fp.getType() == "tlv" &&
1203 !(m_flags & eDontKeepFilesOpened) && !m_isPreviewFx) {
1204 loadAndCacheAllTlvImages(levelToPush,
1205 m_framesCount - addingFrameAmount + 1, // from
1206 m_framesCount); // to
1207 }
1208
1209 // An old archived bug says that simulatenous open for read of the same
1210 // tlv are not allowed...
1211 // if(m_lr && m_lr->getFilePath().getType()=="tlv")
1212 // m_lr = TLevelReaderP();
1213 } else // is a render
1214 {
1215 m_flipConsole->enableButton(FlipConsole::eCheckBg, true);
1216 m_previewedFx = 0;
1217 m_previewXsh = 0;
1218 m_levels.clear();
1219 m_flipConsole->setFrameRange(from, to, step);
1220
1221 m_framesCount = (to - from) / step + 1;
1222 m_title = tr("Rendered Frames :: From %1 To %2 :: Step %3")
1223 .arg(QString::number(from))
1224 .arg(QString::number(to))
1225 .arg(QString::number(step));
1226 if (shrink > 1)
1227 m_title = m_title + tr(" :: Shrink ") + QString::number(shrink);
1228 }
1229
1230 // parentWidget()->setWindowTitle(m_title);
1231 m_imageViewer->setHistogramEnable(true);
1232 m_imageViewer->setHistogramTitle(m_levelNames[0]);
1233 m_flipConsole->enableButton(FlipConsole::eSave, isSavable());
1234 m_flipConsole->showCurrentFrame();
1235 if (m_flags & eDontKeepFilesOpened) m_lr = TLevelReaderP();
1236 } catch (...) {
1237 return;
1238 }
1239 }
1240
1241 //-----------------------------------------------------------------------------
1242
setTitle(const QString & title)1243 void FlipBook::setTitle(const QString &title) {
1244 m_viewerTitle = title;
1245 if (!m_previewedFx && !m_levelNames.empty())
1246 m_title = m_viewerTitle + " :: " + m_levelNames[0];
1247 else
1248 m_title = m_viewerTitle;
1249 }
1250
1251 //-----------------------------------------------------------------------------
1252
setLevel(TXshSimpleLevel * xl)1253 void FlipBook::setLevel(TXshSimpleLevel *xl) {
1254 try {
1255 clearCache();
1256
1257 m_xl = xl;
1258
1259 m_levelNames.push_back(QString::fromStdWString(xl->getName()));
1260 m_snd = 0;
1261 m_previewedFx = 0;
1262 m_previewXsh = 0;
1263 m_levels.clear();
1264
1265 m_flipConsole->enableButton(FlipConsole::eSound, false);
1266
1267 m_shrink = 1;
1268 int step = 1;
1269
1270 m_framesCount = (m_xl->getFrameCount() - 1) / step + 1;
1271 m_flipConsole->setFrameRange(1, m_framesCount, step);
1272 m_flipConsole->enableProgressBar(false);
1273 m_flipConsole->setProgressBarStatus(0);
1274 m_palette = m_xl->getPalette();
1275
1276 const LevelProperties *p = m_xl->getProperties();
1277
1278 m_title = m_viewerTitle + " :: " + m_levelNames[0];
1279
1280 if (m_framesCount == 1)
1281 m_title = m_title + " :: 1 Frame";
1282 else
1283 m_title = m_title + " :: " + QString::number(m_framesCount) + " Frames";
1284
1285 if (p) m_dim = p->getImageRes();
1286
1287 if (p)
1288 m_title = m_title + " :: " + QString::number(p->getImageRes().lx) +
1289 "x" + QString::number(p->getImageRes().ly) + " Pixels";
1290
1291 if (m_shrink > 1)
1292 m_title = m_title + " :: " + "Shrink: " + QString::number(m_shrink);
1293
1294 m_imageViewer->setHistogramEnable(true);
1295 m_imageViewer->setHistogramTitle(m_levelNames[0]);
1296
1297 m_flipConsole->showCurrentFrame();
1298
1299 } catch (...) {
1300 return;
1301 }
1302 }
1303
1304 //-----------------------------------------------------------------------------
1305
setLevel(TFx * previewedFx,TXsheet * xsh,TLevel * level,TPalette * palette,int from,int to,int step,int currentFrame,TSoundTrack * snd)1306 void FlipBook::setLevel(TFx *previewedFx, TXsheet *xsh, TLevel *level,
1307 TPalette *palette, int from, int to, int step,
1308 int currentFrame, TSoundTrack *snd) {
1309 m_xl = 0;
1310 m_previewedFx = previewedFx;
1311 m_previewXsh = xsh;
1312 m_isPreviewFx = true;
1313 m_levels.clear();
1314 m_levels.push_back(Level(level, TFilePath(), from - 1, to - 1, step));
1315 m_levelNames.clear();
1316 m_levelNames.push_back(QString::fromStdString(level->getName()));
1317 m_title = m_viewerTitle;
1318 m_flipConsole->setFrameRange(from, to, step, currentFrame);
1319 m_flipConsole->enableProgressBar(true);
1320
1321 m_flipConsole->enableButton(FlipConsole::eDefineLoadBox, false);
1322 m_flipConsole->enableButton(FlipConsole::eUseLoadBox, false);
1323 m_flipConsole->enableButton(FlipConsole::eSound, snd != 0);
1324 m_snd = snd;
1325 m_framesCount = (to - from) / step + 1;
1326
1327 m_imageViewer->setHistogramEnable(true);
1328 m_imageViewer->setHistogramTitle(m_levelNames[0]);
1329 m_flipConsole->showCurrentFrame();
1330 }
1331
1332 //-----------------------------------------------------------------------------
1333
getPreviewedFx() const1334 TFx *FlipBook::getPreviewedFx() const {
1335 return m_isPreviewFx ? m_previewedFx.getPointer() : 0;
1336 }
1337
1338 //-----------------------------------------------------------------------------
1339
getPreviewXsheet() const1340 TXsheet *FlipBook::getPreviewXsheet() const {
1341 return m_isPreviewFx ? m_previewXsh.getPointer() : 0;
1342 }
1343
1344 //-----------------------------------------------------------------------------
1345
getPreviewedImageGeometry() const1346 TRectD FlipBook::getPreviewedImageGeometry() const {
1347 if (!m_isPreviewFx) return TRectD();
1348
1349 // Build viewer's geometry
1350 QRect viewerGeom(m_imageViewer->geometry());
1351 viewerGeom.adjust(-1, -1, 1, 1);
1352 TRectD viewerGeomD(viewerGeom.left(), viewerGeom.top(),
1353 viewerGeom.right() + 1, viewerGeom.bottom() + 1);
1354 TPointD viewerCenter((viewerGeomD.x0 + viewerGeomD.x1) * 0.5,
1355 (viewerGeomD.y0 + viewerGeomD.y1) * 0.5);
1356
1357 // NOTE: The above adjust() is imposed to counter the geometry removal
1358 // specified in function
1359 // FlipBook::onDoubleClick.
1360
1361 // Build viewer-to-camera affine
1362 TAffine viewToCam(m_imageViewer->getViewAff().inv() *
1363 TTranslation(-viewerCenter));
1364
1365 return viewToCam * viewerGeomD;
1366 }
1367
1368 //-----------------------------------------------------------------------------
1369
schedulePreviewedFxUpdate()1370 void FlipBook::schedulePreviewedFxUpdate() {
1371 if (m_previewedFx)
1372 m_previewUpdateTimer.start(
1373 1000); // The effective fx update will happen in 1 msec.
1374 }
1375
1376 //-----------------------------------------------------------------------------
1377
performFxUpdate()1378 void FlipBook::performFxUpdate() {
1379 // refresh only when the subcamera is active
1380 if (PreviewFxManager::instance()->isSubCameraActive(m_previewedFx))
1381 PreviewFxManager::instance()->refreshView(m_previewedFx);
1382 }
1383
1384 //-----------------------------------------------------------------------------
1385
regenerate()1386 void FlipBook::regenerate() {
1387 PreviewFxManager::instance()->reset(TFxP(m_previewedFx));
1388 }
1389
1390 //-----------------------------------------------------------------------------
1391
regenerateFrame()1392 void FlipBook::regenerateFrame() {
1393 PreviewFxManager::instance()->reset(m_previewedFx, getCurrentFrame() - 1);
1394 }
1395
1396 //-----------------------------------------------------------------------------
1397
clonePreview()1398 void FlipBook::clonePreview() {
1399 if (!m_previewedFx) return;
1400
1401 FlipBook *newFlip =
1402 PreviewFxManager::instance()->showNewPreview(m_previewedFx, true);
1403 newFlip->m_imageViewer->setViewAff(m_imageViewer->getViewAff());
1404 PreviewFxManager::instance()->refreshView(m_previewedFx);
1405 }
1406
1407 //-----------------------------------------------------------------------------
1408
freezePreview()1409 void FlipBook::freezePreview() {
1410 if (!m_previewedFx) return;
1411
1412 PreviewFxManager::instance()->freeze(this);
1413
1414 m_freezed = true;
1415
1416 // sync the button state when triggered by shotcut
1417 if (m_freezeButton) m_freezeButton->setPressed(true);
1418 }
1419
1420 //-----------------------------------------------------------------------------
1421
unfreezePreview()1422 void FlipBook::unfreezePreview() {
1423 if (!m_previewedFx) return;
1424
1425 PreviewFxManager::instance()->unfreeze(this);
1426
1427 m_freezed = false;
1428
1429 // sync the button state when triggered by shotcut
1430 if (m_freezeButton) m_freezeButton->setPressed(false);
1431 }
1432
1433 //-----------------------------------------------------------------------------
1434
setProgressBarStatus(const std::vector<UCHAR> * pbStatus)1435 void FlipBook::setProgressBarStatus(const std::vector<UCHAR> *pbStatus) {
1436 m_flipConsole->setProgressBarStatus(pbStatus);
1437 }
1438
1439 //-----------------------------------------------------------------------------
1440
getProgressBarStatus() const1441 const std::vector<UCHAR> *FlipBook::getProgressBarStatus() const {
1442 return m_flipConsole->getProgressBarStatus();
1443 }
1444
1445 //-----------------------------------------------------------------------------
1446
showFrame(int frame)1447 void FlipBook::showFrame(int frame) {
1448 if (frame < 0) return;
1449 m_flipConsole->setCurrentFrame(frame);
1450 m_flipConsole->showCurrentFrame();
1451 }
1452
1453 //-----------------------------------------------------------------------------
1454
playAudioFrame(int frame)1455 void FlipBook::playAudioFrame(int frame) {
1456 static bool first = true;
1457 static bool audioCardInstalled;
1458 if (!m_snd || !m_playSound) return;
1459
1460 if (first) {
1461 audioCardInstalled = TSoundOutputDevice::installed();
1462 first = false;
1463 }
1464
1465 if (!audioCardInstalled) return;
1466
1467 if (!m_player) {
1468 m_player = new TSoundOutputDevice();
1469 m_player->attach(this);
1470 }
1471 if (m_player) {
1472 // Flipbook does not currently support double fps - thus, casting to int in
1473 // soundtrack playback, too
1474 int fps = TApp::instance()
1475 ->getCurrentScene()
1476 ->getScene()
1477 ->getProperties()
1478 ->getOutputProperties()
1479 ->getFrameRate();
1480
1481 int samplePerFrame = int(m_snd->getSampleRate()) / fps;
1482 TINT32 firstSample = (frame - 1) * samplePerFrame;
1483 TINT32 lastSample = firstSample + samplePerFrame;
1484
1485 try {
1486 m_player->play(m_snd, firstSample, lastSample, false, false);
1487 } catch (TSoundDeviceException &e) {
1488 std::string msg;
1489 if (e.getType() == TSoundDeviceException::UnsupportedFormat) {
1490 try {
1491 TSoundTrackFormat fmt =
1492 m_player->getPreferredFormat(m_snd->getFormat());
1493 m_player->play(TSop::convert(m_snd, fmt), firstSample, lastSample,
1494 false, false);
1495 } catch (TSoundDeviceException &ex) {
1496 throw TException(ex.getMessage());
1497 return;
1498 }
1499 }
1500 }
1501 }
1502 }
1503
1504 //-----------------------------------------------------------------------------
1505
getCurrentImage(int frame)1506 TImageP FlipBook::getCurrentImage(int frame) {
1507 std::string id = "";
1508 TFrameId fid;
1509 TFilePath fp;
1510
1511 bool randomAccessRead = false;
1512 bool incrementalIndexing = false;
1513 bool premultiply = false;
1514 if (m_xl) // is an xsheet level
1515 {
1516 if (m_xl->getFrameCount() <= 0) return 0;
1517 return m_xl->getFrame(m_xl->index2fid(frame - 1), false);
1518 } else if (!m_levels.empty()) // is a viewfile or a previewFx
1519 {
1520 TLevelP level;
1521 QString levelName;
1522 int from, to, step;
1523 m_flipConsole->getFrameRange(from, to, step);
1524
1525 int frameIndex = m_previewedFx ? ((frame - from) / step) + 1 : frame;
1526
1527 int i = 0;
1528 // Search all subsequent levels on the flipbook and retrieve the one
1529 // containing the required frame
1530 for (i = 0; i < m_levels.size(); i++) {
1531 int frameIndexesCount = m_levels[i].getIndexesCount();
1532 if (frameIndex > 0 && frameIndex <= frameIndexesCount) break;
1533 frameIndex -= frameIndexesCount;
1534 }
1535
1536 if (i == m_levels.size() || frame < 0) return 0;
1537
1538 frame--;
1539
1540 // Now, get the right frame from the level
1541
1542 level = m_levels[i].m_level;
1543 fp = m_levels[i].m_fp; // fp=empty when previewing fx
1544 randomAccessRead = m_levels[i].m_randomAccessRead;
1545 incrementalIndexing = m_levels[i].m_incrementalIndexing;
1546 levelName = m_levelNames[i];
1547 fid = m_levels[i].flipbookIndexToLevelFrame(frameIndex);
1548 premultiply = m_levels[i].m_premultiply;
1549 if (fid == TFrameId()) return 0;
1550 id = levelName.toStdString() + fid.expand(TFrameId::NO_PAD) +
1551 ((m_isPreviewFx) ? "" : ::to_string(this));
1552
1553 if (!m_isPreviewFx)
1554 m_title1 = m_viewerTitle + " :: " + fp.withoutParentDir().withFrame(fid);
1555 else
1556 m_title1 = "";
1557 } else if (m_levelNames.empty())
1558 return 0;
1559 else // is a render
1560 id = m_levelNames[0].toStdString() + std::to_string(frame);
1561
1562 bool showSub = m_flipConsole->isChecked(FlipConsole::eUseLoadBox);
1563
1564 if (TImageCache::instance()->isCached(id)) {
1565 TRect loadbox;
1566 std::map<std::string, TRect>::const_iterator it = m_loadboxes.find(id);
1567 if (it != m_loadboxes.end()) loadbox = it->second;
1568
1569 // Resubmit the image to the cache as the 'last one' seen by the flipbook.
1570 // TImageCache::instance()->add(toString(m_poolIndex) + "lastFlipFrame",
1571 // img);
1572 // m_lastViewedFrame = frame+1;
1573 if ((showSub && m_loadbox == loadbox) || (!showSub && loadbox == TRect()))
1574 return TImageCache::instance()->get(id, false);
1575 else
1576 TImageCache::instance()->remove(id);
1577 }
1578 if (fp != TFilePath() && !m_isPreviewFx) {
1579 int lx = 0, oriLx = 0;
1580 // TLevelReaderP lr(fp);
1581 if (!m_lr || (fp != m_lr->getFilePath())) {
1582 m_lr = TLevelReaderP(fp);
1583 m_lr->enableRandomAccessRead(randomAccessRead);
1584 }
1585 if (!m_lr) return 0;
1586 // try to get image info only when loading tlv or pli as it is quite time
1587 // consuming
1588 if (fp.getType() == "tlv" || fp.getType() == "pli") {
1589 if (m_lr->getImageInfo()) lx = oriLx = m_lr->getImageInfo()->m_lx;
1590 }
1591 TImageReaderP ir = m_lr->getFrameReader(fid);
1592 ir->setShrink(m_shrink);
1593 if (m_loadbox != TRect() && showSub) {
1594 ir->setRegion(m_loadbox);
1595 lx = m_loadbox.getLx();
1596 }
1597
1598 TImageP img = ir->load();
1599
1600 if (img) {
1601 TRasterImageP ri = ((TRasterImageP)img);
1602 TToonzImageP ti = ((TToonzImageP)img);
1603 if (premultiply) {
1604 if (ri)
1605 TRop::premultiply(ri->getRaster());
1606 else if (ti)
1607 TRop::premultiply(ti->getRaster());
1608 }
1609
1610 // se e' stata caricata una sottoimmagine alcuni formati in realta'
1611 // caricano tutto il raster e fanno extract, non si ha quindi alcun
1612 // risparmio di occupazione di memoria; alloco un raster grande
1613 // giusto copio la region e butto quello originale.
1614 if (ri && showSub && m_loadbox != TRect() &&
1615 ri->getRaster()->getLx() == oriLx) // questo serve perche' per avi e
1616 // mov la setRegion e'
1617 // completamente ignorata...
1618 ri->setRaster(ri->getRaster()->extract(m_loadbox)->clone());
1619 else if (ri && ri->getRaster()->getWrap() > ri->getRaster()->getLx())
1620 ri->setRaster(ri->getRaster()->clone());
1621 else if (ti && ti->getCMapped()->getWrap() > ti->getCMapped()->getLx())
1622 ti->setCMapped(ti->getCMapped()->clone());
1623
1624 if ((fp.getType() == "tlv" || fp.getType() == "pli") && m_shrink > 1 &&
1625 (lx == 0 || (ri && ri->getRaster()->getLx() == lx) ||
1626 (ti && ti->getRaster()->getLx() == lx))) {
1627 if (ri)
1628 ri->setRaster(TRop::shrink(ri->getRaster(), m_shrink));
1629 else if (ti)
1630 ti->setCMapped(TRop::shrink(ti->getRaster(), m_shrink));
1631 }
1632
1633 TPalette *palette = img->getPalette();
1634 if (m_palette && (!palette || palette != m_palette))
1635 img->setPalette(m_palette);
1636 TImageCache::instance()->add(id, img);
1637 m_loadboxes[id] = showSub ? m_loadbox : TRect();
1638 }
1639
1640 // An old archived bug says that simulatenous open for read of the same tlv
1641 // are not allowed...
1642 // if(fp.getType()=="tlv")
1643 // m_lr = TLevelReaderP();
1644 if (m_flags & eDontKeepFilesOpened) m_lr = TLevelReaderP();
1645 return img;
1646 } else if (fp == TFilePath() && m_isPreviewFx) {
1647 if (!TImageCache::instance()->isCached(id)) {
1648 /*string lastFrameCacheId(toString(m_poolIndex) + "lastFlipFrame");
1649 if(TImageCache::instance()->isCached(lastFrameCacheId))
1650 return TImageCache::instance()->get(lastFrameCacheId, false);
1651 else*/
1652 return 0;
1653 // showFrame(m_lastViewedFrame);
1654 }
1655 }
1656
1657 return 0;
1658 }
1659
1660 //-----------------------------------------------------------------------------
1661
1662 /*! Set current level frame to image viewer. Add the view image in cache.
1663 */
onDrawFrame(int frame,const ImagePainter::VisualSettings & vs)1664 void FlipBook::onDrawFrame(int frame, const ImagePainter::VisualSettings &vs) {
1665 try {
1666 m_imageViewer->setVisual(vs);
1667
1668 TImageP img = getCurrentImage(frame);
1669
1670 if (!img) return;
1671
1672 m_imageViewer->setImage(img);
1673 } catch (...) {
1674 m_imageViewer->setImage(TImageP());
1675 }
1676
1677 if (m_playSound && !vs.m_drawBlankFrame) playAudioFrame(frame);
1678 }
1679
1680 //-----------------------------------------------------------------------------
1681
swapBuffers()1682 void FlipBook::swapBuffers() { m_imageViewer->doSwapBuffers(); }
1683
1684 //-----------------------------------------------------------------------------
1685
changeSwapBehavior(bool enable)1686 void FlipBook::changeSwapBehavior(bool enable) {
1687 m_imageViewer->changeSwapBehavior(enable);
1688 }
1689
1690 //-----------------------------------------------------------------------------
1691
setLoadbox(const TRect & box)1692 void FlipBook::setLoadbox(const TRect &box) {
1693 m_loadbox =
1694 (m_dim.lx > 0) ? box * TRect(0, 0, m_dim.lx - 1, m_dim.ly - 1) : box;
1695 }
1696
1697 //----------------------------------------------------------------
1698
clearCache()1699 void FlipBook::clearCache() {
1700 TLevel::Iterator it;
1701
1702 if (m_levelNames.empty()) return;
1703 int i;
1704
1705 if (!m_levels.empty()) // is a viewfile
1706 for (i = 0; i < m_levels.size(); i++)
1707 for (it = m_levels[i].m_level->begin(); it != m_levels[i].m_level->end();
1708 ++it)
1709 TImageCache::instance()->remove(
1710 m_levelNames[i].toStdString() +
1711 std::to_string(it->first.getNumber()) +
1712 ((m_isPreviewFx) ? "" : ::to_string(this)));
1713 else {
1714 int from, to, step;
1715 m_flipConsole->getFrameRange(from, to, step);
1716 for (int i = from; i <= to; i += step) // is a render
1717 // color model may loading a part of frames in the level
1718 if (m_imageViewer->isColorModel() && m_palette) {
1719 // get the actually-loaded frame list
1720 std::vector<TFrameId> fids(m_palette->getRefLevelFids());
1721 if (!fids.empty() && (int)fids.size() >= i) {
1722 int frame = fids[i - 1].getNumber();
1723 TImageCache::instance()->remove(m_levelNames[0].toStdString() +
1724 std::to_string(frame));
1725 } else {
1726 TImageCache::instance()->remove(m_levelNames[0].toStdString() +
1727 std::to_string(i));
1728 }
1729 } else
1730 TImageCache::instance()->remove(m_levelNames[0].toStdString() +
1731 std::to_string(i));
1732 }
1733 }
1734
1735 //-----------------------------------------------------------------------------
1736
onCloseButtonPressed()1737 void FlipBook::onCloseButtonPressed() {
1738 m_flipConsole->setActive(false);
1739 closeFlipBook(this);
1740
1741 reset();
1742
1743 // hide freeze button in preview fx window
1744 if (m_freezeButton) {
1745 m_freezeButton->hide();
1746 m_imageViewer->setIsRemakingPreviewFx(false);
1747 }
1748
1749 // Return the flipbook to the pool in case it was popped from it.
1750 if (m_poolIndex >= 0) FlipBookPool::instance()->push(this);
1751 }
1752
1753 //-----------------------------------------------------------------------------
1754
showHistogram()1755 void ImageViewer::showHistogram() {
1756 if (!m_isHistogramEnable) return;
1757 if (m_histogramPopup->isVisible())
1758 m_histogramPopup->raise();
1759 else {
1760 m_histogramPopup->setImage(getImage());
1761 m_histogramPopup->show();
1762 }
1763 }
1764
1765 //-----------------------------------------------------------------------------
1766
dragEnterEvent(QDragEnterEvent * e)1767 void FlipBook::dragEnterEvent(QDragEnterEvent *e) {
1768 const QMimeData *mimeData = e->mimeData();
1769 bool isResourceDrop = acceptResourceDrop(mimeData->urls());
1770 if (!isResourceDrop &&
1771 !mimeData->hasFormat("application/vnd.toonz.drawings") &&
1772 !mimeData->hasFormat(CastItems::getMimeFormat()))
1773 return;
1774
1775 for (const QUrl &url : mimeData->urls()) {
1776 TFilePath fp(url.toLocalFile().toStdWString());
1777 std::string type = fp.getType();
1778 if (type == "tzp" || type == "tzu" || type == "tnz" || type == "scr" ||
1779 type == "mesh")
1780 return;
1781 }
1782 if (mimeData->hasFormat(CastItems::getMimeFormat())) {
1783 const CastItems *items = dynamic_cast<const CastItems *>(mimeData);
1784 if (!items) return;
1785
1786 int i;
1787 for (i = 0; i < items->getItemCount(); i++) {
1788 CastItem *item = items->getItem(i);
1789 TXshSimpleLevel *sl = item->getSimpleLevel();
1790 if (!sl) return;
1791 }
1792 }
1793
1794 if (isResourceDrop) {
1795 // Force CopyAction
1796 e->setDropAction(Qt::CopyAction);
1797 // For files, don't accept original proposed action in case it's a move
1798 e->accept();
1799 } else
1800 e->acceptProposedAction();
1801 }
1802
1803 //-----------------------------------------------------------------------------
1804
dropEvent(QDropEvent * e)1805 void FlipBook::dropEvent(QDropEvent *e) {
1806 const QMimeData *mimeData = e->mimeData();
1807 bool isResourceDrop = acceptResourceDrop(mimeData->urls());
1808 if (mimeData->hasUrls()) {
1809 for (const QUrl &url : mimeData->urls()) {
1810 TFilePath fp(url.toLocalFile().toStdWString());
1811 if (TFileType::getInfo(fp) != TFileType::UNKNOW_FILE) setLevel(fp);
1812 if (isResourceDrop) {
1813 // Force CopyAction
1814 e->setDropAction(Qt::CopyAction);
1815 // For files, don't accept original proposed action in case it's a move
1816 e->accept();
1817 } else
1818 e->acceptProposedAction();
1819 return;
1820 }
1821 } else if (mimeData->hasFormat(
1822 "application/vnd.toonz.drawings")) // drag-drop from film
1823 // strip
1824 {
1825 TFilmstripSelection *s =
1826 dynamic_cast<TFilmstripSelection *>(TSelection::getCurrent());
1827 TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
1828 if (!s || !sl) return;
1829 TXshSimpleLevel *newSl = new TXshSimpleLevel();
1830 newSl->setScene(sl->getScene());
1831 newSl->setType(sl->getType());
1832 newSl->setPalette(sl->getPalette());
1833 newSl->clonePropertiesFrom(sl);
1834 const std::set<TFrameId> &fids = s->getSelectedFids();
1835 std::set<TFrameId>::const_iterator it;
1836 for (it = fids.begin(); it != fids.end(); ++it)
1837 newSl->setFrame(*it, sl->getFrame(*it, false)->cloneImage());
1838 setLevel(newSl);
1839 } else if (mimeData->hasFormat(
1840 CastItems::getMimeFormat())) // Drag-Drop from castviewer
1841 {
1842 const CastItems *items = dynamic_cast<const CastItems *>(mimeData);
1843 if (!items) return;
1844
1845 int i;
1846 for (i = 0; i < items->getItemCount(); i++) {
1847 CastItem *item = items->getItem(i);
1848 if (TXshSimpleLevel *sl = item->getSimpleLevel()) setLevel(sl);
1849 }
1850 }
1851 m_flipConsole->makeCurrent();
1852 }
1853
1854 //-------------------------------------------------------------------
1855
reset()1856 void FlipBook::reset() {
1857 if (!m_isPreviewFx) // The cache is owned by the PreviewFxManager otherwise
1858 clearCache();
1859 else
1860 PreviewFxManager::instance()->detach(this);
1861
1862 m_levelNames.clear();
1863 m_levels.clear();
1864 m_framesCount = 0;
1865 m_palette = 0;
1866 m_imageViewer->setImage(TImageP());
1867 m_imageViewer->hideHistogram();
1868 m_isPreviewFx = false;
1869 m_previewedFx = 0;
1870 m_previewXsh = 0;
1871 m_freezed = false;
1872 // sync the freeze button
1873 if (m_freezeButton) m_freezeButton->setPressed(false);
1874 m_flipConsole->pressButton(FlipConsole::ePause);
1875 if (m_playSound) m_flipConsole->pressButton(FlipConsole::eSound);
1876 if (m_player) m_player->stop();
1877 if (m_flipConsole->isChecked(FlipConsole::eDefineLoadBox))
1878 m_flipConsole->pressButton(FlipConsole::eDefineLoadBox);
1879 if (m_flipConsole->isChecked(FlipConsole::eUseLoadBox))
1880 m_flipConsole->pressButton(FlipConsole::eUseLoadBox);
1881
1882 m_flipConsole->enableButton(FlipConsole::eDefineLoadBox, true);
1883 m_flipConsole->enableButton(FlipConsole::eUseLoadBox, true);
1884
1885 m_lr = TLevelReaderP();
1886
1887 m_dim = TDimension();
1888 m_loadbox = TRect();
1889 m_loadboxes.clear();
1890 // m_lastViewedFrame = -1;
1891 // TImageCache::instance()->remove(toString(m_poolIndex) + "lastFlipFrame");
1892
1893 m_flipConsole->enableProgressBar(false);
1894 m_flipConsole->setProgressBarStatus(0);
1895 m_flipConsole->setFrameRange(1, 1, 1);
1896
1897 setTitle(tr("Flipbook"));
1898 parentWidget()->setWindowTitle(m_title);
1899
1900 update();
1901 }
1902
1903 //-------------------------------------------------------------------
1904
showEvent(QShowEvent * e)1905 void FlipBook::showEvent(QShowEvent *e) {
1906 TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
1907 connect(sceneHandle, SIGNAL(sceneChanged()), m_imageViewer, SLOT(update()));
1908 // for updating the blank frame button
1909 if (!m_imageViewer->isColorModel()) {
1910 connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)),
1911 m_flipConsole, SLOT(onPreferenceChanged(const QString &)));
1912 m_flipConsole->onPreferenceChanged("");
1913 }
1914 m_flipConsole->setActive(true);
1915 m_imageViewer->update();
1916 }
1917
1918 //-------------------------------------------------------------------
1919
hideEvent(QHideEvent * e)1920 void FlipBook::hideEvent(QHideEvent *e) {
1921 TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
1922 disconnect(sceneHandle, SIGNAL(sceneChanged()), m_imageViewer,
1923 SLOT(update()));
1924 if (!m_imageViewer->isColorModel()) {
1925 disconnect(sceneHandle, SIGNAL(preferenceChanged(const QString &)),
1926 m_flipConsole, SLOT(onPreferenceChanged(const QString &)));
1927 }
1928 m_flipConsole->setActive(false);
1929 }
1930
1931 //-----------------------------------------------------------------------------
1932
resizeEvent(QResizeEvent * e)1933 void FlipBook::resizeEvent(QResizeEvent *e) { schedulePreviewedFxUpdate(); }
1934
1935 //-----------------------------------------------------------------------------
1936
adaptGeometry(const TRect & interestingImgRect,const TRect & imgRect)1937 void FlipBook::adaptGeometry(const TRect &interestingImgRect,
1938 const TRect &imgRect) {
1939 TRectD imgRectD(imgRect.x0, imgRect.y0, imgRect.x1 + 1, imgRect.y1 + 1);
1940 TRectD interestingImgRectD(interestingImgRect.x0, interestingImgRect.y0,
1941 interestingImgRect.x1 + 1,
1942 interestingImgRect.y1 + 1);
1943
1944 TAffine toWidgetRef(m_imageViewer->getImgToWidgetAffine(imgRectD));
1945 TRectD interestGeomD(toWidgetRef * interestingImgRectD);
1946 TRectD imageGeomD(toWidgetRef * imgRectD);
1947 adaptWidGeometry(
1948 TRect(tceil(interestGeomD.x0), tceil(interestGeomD.y0),
1949 tfloor(interestGeomD.x1) - 1, tfloor(interestGeomD.y1) - 1),
1950 TRect(tceil(imageGeomD.x0), tceil(imageGeomD.y0),
1951 tfloor(imageGeomD.x1) - 1, tfloor(imageGeomD.y1) - 1),
1952 true);
1953 }
1954
1955 //-----------------------------------------------------------------------------
1956 /*! When Fx preview is called without the subcamera, render the full region
1957 of camera by resize flipbook and zoom-out the rendered image.
1958 */
adaptGeometryForFullPreview(const TRect & imgRect)1959 void FlipBook::adaptGeometryForFullPreview(const TRect &imgRect) {
1960 TRectD imgRectD(imgRect.x0, imgRect.y0, imgRect.x1 + 1, imgRect.y1 + 1);
1961
1962 // Get screen geometry
1963 TPanel *panel = static_cast<TPanel *>(parentWidget());
1964 if (!panel->isFloating()) return;
1965 QDesktopWidget *desk =
1966 static_cast<QApplication *>(QApplication::instance())->desktop();
1967 QRect screenGeom = desk->availableGeometry(panel);
1968
1969 while (1) {
1970 TAffine toWidgetRef(m_imageViewer->getImgToWidgetAffine(imgRectD));
1971 TRectD imageGeomD(toWidgetRef * imgRectD);
1972 TRect imageGeom(tceil(imageGeomD.x0) - 1, tceil(imageGeomD.y0) - 1,
1973 tfloor(imageGeomD.x1) + 1, tfloor(imageGeomD.y1) + 1);
1974
1975 if (imageGeom.getLx() <= screenGeom.width() &&
1976 imageGeom.getLy() <= screenGeom.height()) {
1977 adaptWidGeometry(imageGeom, imageGeom, false);
1978 break;
1979 } else
1980 m_imageViewer->zoomQt(false, false);
1981 }
1982 }
1983
1984 //-----------------------------------------------------------------------------
1985
1986 //! Adapts panel geometry to that of passed rect.
adaptWidGeometry(const TRect & interestWidGeom,const TRect & imgWidGeom,bool keepPosition)1987 void FlipBook::adaptWidGeometry(const TRect &interestWidGeom,
1988 const TRect &imgWidGeom, bool keepPosition) {
1989 TPanel *panel = static_cast<TPanel *>(parentWidget());
1990 if (!panel->isFloating()) return;
1991
1992 // Extract image position in screen coordinates
1993 QRect qgeom(interestWidGeom.x0, interestWidGeom.y0, interestWidGeom.getLx(),
1994 interestWidGeom.getLy());
1995 QRect interestGeom(m_imageViewer->mapToGlobal(qgeom.topLeft()),
1996 m_imageViewer->mapToGlobal(qgeom.bottomRight()));
1997 qgeom = QRect(imgWidGeom.x0, imgWidGeom.y0, imgWidGeom.getLx(),
1998 imgWidGeom.getLy());
1999 QRect imageGeom(m_imageViewer->mapToGlobal(qgeom.topLeft()),
2000 m_imageViewer->mapToGlobal(qgeom.bottomRight()));
2001
2002 // qDebug("tgeom= [%d, %d] x [%d, %d]", tgeom.x0, tgeom.x1, tgeom.y0,
2003 // tgeom.y1);
2004 // qDebug("imagegeom= [%d, %d] x [%d, %d]", imageGeom.left(),
2005 // imageGeom.right(),
2006 // imageGeom.top(), imageGeom.bottom());
2007
2008 // Get screen geometry
2009 QDesktopWidget *desk =
2010 static_cast<QApplication *>(QApplication::instance())->desktop();
2011 QRect screenGeom = desk->availableGeometry(panel);
2012
2013 // Get panel margin measures
2014 QRect margins;
2015 QRect currView(m_imageViewer->geometry());
2016 currView.moveTo(m_imageViewer->mapToGlobal(currView.topLeft()));
2017 QRect panelGeom(panel->geometry());
2018
2019 margins.setLeft(panelGeom.left() - currView.left());
2020 margins.setRight(panelGeom.right() - currView.right());
2021 margins.setTop(panelGeom.top() - currView.top());
2022 margins.setBottom(panelGeom.bottom() - currView.bottom());
2023
2024 // Build the minimum flipbook geometry. Adjust the interesting geometry
2025 // according to it.
2026 QSize flipMinimumSize(panel->minimumSize());
2027 flipMinimumSize -=
2028 QSize(margins.right() - margins.left(), margins.bottom() - margins.top());
2029 QSize minAddition(
2030 tceil(std::max(0, flipMinimumSize.width() - interestGeom.width()) * 0.5),
2031 tceil(std::max(0, flipMinimumSize.height() - interestGeom.height()) *
2032 0.5));
2033 interestGeom.adjust(-minAddition.width(), -minAddition.height(),
2034 minAddition.width(), minAddition.height());
2035
2036 // Translate to keep the current view top-left corner, if required
2037 if (keepPosition) {
2038 QPoint shift(currView.topLeft() - interestGeom.topLeft());
2039 interestGeom.translate(shift);
2040 imageGeom.translate(shift);
2041 }
2042
2043 // Intersect with the screen geometry
2044 QRect newViewerGeom(screenGeom);
2045 newViewerGeom.adjust(-margins.left(), -margins.top(), -margins.right(),
2046 -margins.bottom());
2047
2048 // when fx previewing in full size (i.e. keepPosition is false ),
2049 // try to translate geometry and keep the image inside the viewer as much as
2050 // posiible
2051 if (keepPosition)
2052 newViewerGeom &= interestGeom;
2053 else if (newViewerGeom.intersects(interestGeom)) {
2054 int d_ns = 0;
2055 int d_ew = 0;
2056 if (interestGeom.top() < newViewerGeom.top())
2057 d_ns = newViewerGeom.top() - interestGeom.top();
2058 else if (interestGeom.bottom() > newViewerGeom.bottom())
2059 d_ns = newViewerGeom.bottom() - interestGeom.bottom();
2060 if (interestGeom.left() < newViewerGeom.left())
2061 d_ew = newViewerGeom.left() - interestGeom.left();
2062 else if (interestGeom.right() > newViewerGeom.right())
2063 d_ew = newViewerGeom.right() - interestGeom.right();
2064 if (d_ns || d_ew) {
2065 interestGeom.translate(d_ew, d_ns);
2066 imageGeom.translate(d_ew, d_ns);
2067 }
2068 newViewerGeom &= interestGeom;
2069 }
2070
2071 // qDebug("new Viewer= [%d, %d] x [%d, %d]", newViewerGeom.left(),
2072 // newViewerGeom.right(),
2073 // newViewerGeom.top(), newViewerGeom.bottom());
2074
2075 // Calculate the pan of content image in order to compensate for our geometry
2076 // change
2077 QPointF imageGeomCenter((imageGeom.left() + imageGeom.right() + 1) * 0.5,
2078 (imageGeom.top() + imageGeom.bottom() + 1) * 0.5);
2079 QPointF newViewerGeomCenter(
2080 (newViewerGeom.left() + newViewerGeom.right() + 1) * 0.5,
2081 (newViewerGeom.top() + newViewerGeom.bottom() + 1) * 0.5);
2082
2083 /*QPointF imageGeomCenter(
2084 (imageGeom.width()) * 0.5,
2085 (imageGeom.height()) * 0.5
2086 );
2087 QPointF newViewerGeomCenter(
2088 (newViewerGeom.width()) * 0.5,
2089 (newViewerGeom.height()) * 0.5
2090 );*/
2091
2092 // NOTE: If delta == (0,0) the image is at center. Typically happens when
2093 // imageGeom doesn't intersect
2094 // the screen geometry.
2095 QPointF delta(imageGeomCenter - newViewerGeomCenter);
2096 TAffine aff(m_imageViewer->getViewAff());
2097 aff.a13 = delta.x();
2098 aff.a23 = -delta.y();
2099
2100 // Calculate new panel geometry
2101 newViewerGeom.adjust(margins.left(), margins.top(), margins.right(),
2102 margins.bottom());
2103
2104 // Apply changes
2105 m_imageViewer->setViewAff(aff);
2106 panel->setGeometry(newViewerGeom);
2107 }
2108
2109 //-----------------------------------------------------------------------------
2110
onDoubleClick(QMouseEvent * me)2111 void FlipBook::onDoubleClick(QMouseEvent *me) {
2112 TImageP img(m_imageViewer->getImage());
2113 if (!img) return;
2114
2115 TAffine toWidgetRef(m_imageViewer->getImgToWidgetAffine());
2116 TRectD pixGeomD(TScale(1.0 / (double)getDevPixRatio()) * toWidgetRef *
2117 getImageBoundsD(img));
2118 // TRectD pixGeomD(toWidgetRef * getImageBoundsD(img));
2119 TRect pixGeom(tceil(pixGeomD.x0), tceil(pixGeomD.y0), tfloor(pixGeomD.x1) - 1,
2120 tfloor(pixGeomD.y1) - 1);
2121
2122 // NOTE: The previous line has ceils and floor inverted on purpose. The reason
2123 // is the following:
2124 // As the viewer's zoom level is arbitrary, the image is likely to have a not
2125 // integer geometry
2126 // with respect to the widget - the problem is, we cannot take the closest
2127 // integer rect ENCLOSING ours,
2128 // or the ImageViewer class adds blank lines on image rendering.
2129 // So, we do the converse - take the closest ENCLOSED one - eventually to be
2130 // compensated when
2131 // performing the inverse.
2132
2133 adaptWidGeometry(pixGeom, pixGeom, false);
2134 }
2135
2136 //-----------------------------------------------------------------------------
2137
minimize(bool doMinimize)2138 void FlipBook::minimize(bool doMinimize) {
2139 m_imageViewer->setVisible(!doMinimize);
2140 m_flipConsole->showHideAllParts(!doMinimize);
2141 }
2142
2143 //-----------------------------------------------------------------------------
2144 /*! When viewing the tlv, try to cache all frames at the beginning.
2145 NOTE : fromFrame and toFrame are frame numbers displayed on the flipbook
2146 */
loadAndCacheAllTlvImages(Level level,int fromFrame,int toFrame)2147 void FlipBook::loadAndCacheAllTlvImages(Level level, int fromFrame,
2148 int toFrame) {
2149 TFilePath fp = level.m_fp;
2150 if (!m_lr || (fp != m_lr->getFilePath())) m_lr = TLevelReaderP(fp);
2151 if (!m_lr) return;
2152
2153 // show the wait cursor when loading a level with more than 50 frames
2154 if (toFrame - fromFrame > 50) QApplication::setOverrideCursor(Qt::WaitCursor);
2155
2156 int lx = 0, oriLx = 0;
2157 if (m_lr->getImageInfo()) lx = oriLx = m_lr->getImageInfo()->m_lx;
2158
2159 std::string fileName = toQString(fp.withoutParentDir()).toStdString();
2160
2161 for (int f = fromFrame; f <= toFrame; f++) {
2162 TFrameId fid = level.flipbookIndexToLevelFrame(f);
2163 if (fid == TFrameId()) continue;
2164
2165 std::string id =
2166 fileName + fid.expand(TFrameId::NO_PAD) + ::to_string(this);
2167
2168 TImageReaderP ir = m_lr->getFrameReader(fid);
2169 ir->setShrink(m_shrink);
2170
2171 TImageP img = ir->load();
2172
2173 if (!img) continue;
2174
2175 TToonzImageP ti = ((TToonzImageP)img);
2176 if (!ti) continue;
2177
2178 if (ti->getCMapped()->getWrap() > ti->getCMapped()->getLx())
2179 ti->setCMapped(ti->getCMapped()->clone());
2180 if (m_shrink > 1 && (lx == 0 || ti->getRaster()->getLx() == lx))
2181 ti->setCMapped(TRop::shrink(ti->getRaster(), m_shrink));
2182
2183 TPalette *palette = img->getPalette();
2184
2185 if (m_palette && (!palette || palette != m_palette))
2186 img->setPalette(m_palette);
2187
2188 TImageCache::instance()->add(id, img);
2189 m_loadboxes[id] = TRect();
2190 }
2191
2192 m_lr = TLevelReaderP();
2193
2194 // revert the cursor
2195 if (toFrame - fromFrame > 50) QApplication::restoreOverrideCursor();
2196 }
2197
2198 //=============================================================================
2199 // Utility
2200
2201 //-----------------------------------------------------------------------------
2202
2203 //! Displays the passed file on a Flipbook, supporting a wide range of options.
2204 //! Possible options include:
2205 //! \li The range, step and shrink parameters for the loaded level
2206 //! \li A soundtrack to accompany the level's images
2207 //! \li The flipbook where the file is to be opened. If none, a new one is
2208 //! created.
2209 //! \li Whether the level must replace an existing one on the flipbook, or it
2210 //! must
2211 //! rather be appended at its end
2212 //! \li In case the file has a movie format and it is known to be a toonz
2213 //! output,
2214 //! some additional random access information may be retrieved (i.e. images may
2215 //! map
2216 //! to specific frames).
2217 // returns pointer to the opened flipbook to control modality.
viewFile(const TFilePath & path,int from,int to,int step,int shrink,TSoundTrack * snd,FlipBook * flipbook,bool append,bool isToonzOutput)2218 FlipBook *viewFile(const TFilePath &path, int from, int to, int step,
2219 int shrink, TSoundTrack *snd, FlipBook *flipbook,
2220 bool append, bool isToonzOutput) {
2221 // In case the step and shrink informations are invalid, load them from
2222 // preferences
2223 if (step == -1 || shrink == -1) {
2224 int _step = 1, _shrink = 1;
2225 Preferences::instance()->getViewValues(_shrink, _step);
2226 if (step == -1) step = _step;
2227 if (shrink == -1) shrink = _shrink;
2228 }
2229
2230 // Movie files must not have the ".." extension
2231 if ((path.getType() == "mov" || path.getType() == "avi" ||
2232 path.getType() == "3gp") &&
2233 path.isLevelName()) {
2234 DVGui::warning(QObject::tr("%1 has an invalid extension format.")
2235 .arg(QString::fromStdString(path.getLevelName())));
2236 return NULL;
2237 }
2238
2239 // Windows Screen Saver - avoid
2240 if (path.getType() == "scr") return NULL;
2241
2242 // Avi and movs may be viewed by an external viewer, depending on preferences
2243 if ((path.getType() == "mov" || path.getType() == "avi") && !flipbook) {
2244 QString str;
2245 QSettings().value("generatedMovieViewEnabled", str);
2246 if (str.toInt() != 0) {
2247 TSystem::showDocument(path);
2248 return NULL;
2249 }
2250 }
2251
2252 // Retrieve a blank flipbook
2253 if (!flipbook)
2254 flipbook = FlipBookPool::instance()->pop();
2255 else if (!append)
2256 flipbook->reset();
2257
2258 // Assign the passed level with associated infos
2259 flipbook->setLevel(path, 0, from, to, step, shrink, snd, append,
2260 isToonzOutput);
2261 return flipbook;
2262 }
2263
2264 //-----------------------------------------------------------------------------
2265