1
2
3 #include "toonz/toonzscene.h"
4
5 // TnzLib includes
6 #include "toonz/txsheet.h"
7 #include "toonz/txshcell.h"
8 #include "toonz/txshsimplelevel.h"
9 #include "toonz/txshchildlevel.h"
10 #include "toonz/txshleveltypes.h"
11 #include "toonz/txshlevelcolumn.h"
12 #include "toonz/txshsoundcolumn.h"
13 #include "toonz/txshsoundlevel.h"
14 #include "toonz/sceneproperties.h"
15 #include "toonz/stage.h"
16 #include "toonz/stagevisitor.h"
17 #include "toonz/levelset.h"
18 #include "toonz/tstageobjecttree.h"
19 #include "toonz/observer.h"
20 #include "toonz/namebuilder.h"
21 #include "toonz/tproject.h"
22 #include "toonz/toonzimageutils.h"
23 #include "toonz/childstack.h"
24 #include "toonz/levelproperties.h"
25 #include "toonz/tcamera.h"
26 #include "toonz/sceneresources.h"
27 #include "toonz/preferences.h"
28 #include "toonz/fullcolorpalette.h"
29 #include "toonz/txshpalettecolumn.h"
30 #include "toonz/txshpalettelevel.h"
31 #include "toonz/toonzfolders.h"
32
33 // TnzCore includes
34 #include "timagecache.h"
35 #include "tstream.h"
36 #include "tstreamexception.h"
37 #include "tofflinegl.h"
38 #include "tenv.h"
39 #include "tsystem.h"
40 #include "tfiletype.h"
41 #include "tlevel_io.h"
42 #include "ttoonzimage.h"
43 #include "tlogger.h"
44 #include "tvectorimage.h"
45 #include "tcontenthistory.h"
46 #include "toutputproperties.h"
47 #include "trop.h"
48
49 TOfflineGL *currentOfflineGL = 0;
50
51 #include <QProgressDialog>
52
53 #ifdef MACOSX
54 #include <QSurfaceFormat>
55 #include <QOffscreenSurface>
56 #include <QOpenGLContext>
57 #include <QOpenGLFramebufferObject>
58 #endif
59 //=============================================================================
60 // Utility functions
61 //=============================================================================
62 namespace {
63 // tentatively update the scene file version from 71.0 to 71.1 in order to
64 // manage PNG level settings
65 const VersionNumber l_currentVersion(71, 1);
66 // TODO: Revise VersionNumber with OT version (converting 71.0 -> 14.0 , 71.1
67 // -> 15.0)
68
69 //-----------------------------------------------------------------------------
70
getFolderName(int levelType)71 std::string getFolderName(int levelType) {
72 switch (levelType) {
73 case TZI_XSHLEVEL:
74 return TProject::Inputs;
75 case PLI_XSHLEVEL:
76 return TProject::Drawings;
77 case TZP_XSHLEVEL:
78 return TProject::Drawings;
79 case OVL_XSHLEVEL:
80 return TProject::Extras;
81 default:
82 return TProject::Extras;
83 }
84 }
85
86 //-----------------------------------------------------------------------------
87
88 /*
89 TFilePath adjustTypeAndFrame(int levelType, TFilePath fp)
90 {
91 switch(levelType)
92 {
93 case TZI_XSHLEVEL:
94 if(fp.getType()=="") fp=fp.withType("tif");
95 return fp.withFrame(TFrameId::EMPTY_FRAME);
96 case PLI_XSHLEVEL:
97 return fp.withFrame(TFrameId::NO_FRAME)
98 .withType("pli");
99 case TZP_XSHLEVEL:
100 return fp.withFrame(TFrameId::NO_FRAME)
101 .withType("tlv");
102 case OVL_XSHLEVEL:
103 default:
104 if(fp.getType()=="") fp=fp.withType("png");
105 return fp;
106 }
107 }
108 */
109
110 //-----------------------------------------------------------------------------
111
getUntitledScenesDir()112 TFilePath getUntitledScenesDir() {
113 return ToonzFolder::getCacheRootFolder() + "temp";
114 }
115
116 //-----------------------------------------------------------------------------
117
118 // se path = head + tail ritorna true e assegna head
checkTail(TFilePath path,TFilePath tail,TFilePath & head)119 bool checkTail(TFilePath path, TFilePath tail, TFilePath &head) {
120 for (;;) {
121 if (tail == TFilePath()) {
122 head = path;
123 return true;
124 }
125 if (path == TFilePath()) return false;
126 if (path.withoutParentDir() != tail.withoutParentDir()) return false;
127 path = path.getParentDir();
128 tail = tail.getParentDir();
129 }
130 }
131
132 //-----------------------------------------------------------------------------
133
makeSceneIcon(ToonzScene * scene)134 void makeSceneIcon(ToonzScene *scene) {
135 TDimension cameraSize = scene->getCurrentCamera()->getRes();
136
137 int maxSize = 128;
138 TDimension iconCameraSize = cameraSize;
139 if (cameraSize.lx > cameraSize.ly) {
140 iconCameraSize.ly = maxSize * cameraSize.ly / cameraSize.lx;
141 iconCameraSize.lx = maxSize;
142 } else {
143 iconCameraSize.lx = maxSize * cameraSize.lx / cameraSize.ly;
144 iconCameraSize.ly = maxSize;
145 }
146
147 TRaster32P ras(iconCameraSize);
148 TPixel32 bgColor = scene->getProperties()->getBgColor();
149 ras->fill(bgColor);
150 scene->renderFrame(ras, 0);
151
152 TFilePath iconPath = scene->getIconPath();
153 if (TSystem::touchParentDir(iconPath)) TImageWriter::save(iconPath, ras);
154 }
155
156 //-----------------------------------------------------------------------------
157
deleteUntitledScene(const TFilePath & fp)158 void deleteUntitledScene(const TFilePath &fp) {
159 if (TFileStatus(fp).isDirectory()) {
160 TFilePath tempDir = getUntitledScenesDir();
161 if (TFileStatus(tempDir).isDirectory() &&
162 tempDir.isAncestorOf(fp)) // per sicurezza
163 {
164 try {
165 TSystem::rmDirTree(fp);
166 } catch (...) {
167 assert(0);
168 }
169 } else
170 assert(0);
171 }
172 }
173
174 //-----------------------------------------------------------------------------
175
saveBackup(TFilePath path)176 static void saveBackup(TFilePath path) {
177 int totalBackups = Preferences::instance()->getBackupKeepCount();
178 totalBackups -= 1;
179 TFilePath backup = path.withType(path.getType() + ".bak");
180 TFilePath prevBackup =
181 path.withType(path.getType() + ".bak" + std::to_string(totalBackups));
182 while (--totalBackups >= 0) {
183 std::string bakExt =
184 ".bak" + (totalBackups > 0 ? std::to_string(totalBackups) : "");
185 backup = path.withType(path.getType() + bakExt);
186 if (TSystem::doesExistFileOrLevel(backup)) {
187 try {
188 TSystem::copyFileOrLevel_throw(prevBackup, backup);
189 } catch (...) {
190 }
191 }
192 prevBackup = backup;
193 }
194
195 try {
196 if (TSystem::doesExistFileOrLevel(backup))
197 TSystem::removeFileOrLevel_throw(backup);
198 TSystem::copyFileOrLevel_throw(backup, path);
199 } catch (...) {
200 }
201 }
202
203 //-----------------------------------------------------------------------------
204
205 // serve per correggere un problema verificatosi da Bianco (con la beta3)
206 // vengono create coppie di livelli con lo stesso nome e in genere path diverso
207
fixBiancoProblem(ToonzScene * scene,TXsheet * xsh)208 void fixBiancoProblem(ToonzScene *scene, TXsheet *xsh) {
209 TLevelSet *levelSet = scene->getLevelSet();
210 std::set<TXsheet *> visited, tovisit;
211 tovisit.insert(xsh);
212 while (!tovisit.empty()) {
213 xsh = *tovisit.begin();
214 xsh->setScene(scene); // sound problem
215 visited.insert(xsh);
216 tovisit.erase(xsh);
217 int c0 = 0, c1 = xsh->getColumnCount() - 1;
218 for (int c = c0; c <= c1; c++) {
219 if (xsh->isColumnEmpty(c)) continue;
220 TXshColumn *column = xsh->getColumn(c);
221 if (!column || !column->getLevelColumn()) continue;
222 TXshLevelColumn *lcolumn = column->getLevelColumn();
223 int r0 = 0, r1 = -1;
224 lcolumn->getRange(r0, r1);
225 for (int r = r0; r <= r1; r++) {
226 TXshCell cell = lcolumn->getCell(r);
227 if (cell.isEmpty()) continue;
228 TXshLevel *xl = cell.m_level.getPointer();
229 scene->getLevelSet()->insertLevel(xl); // per sicurezza
230 xl->setScene(scene);
231 if (TXshChildLevel *childLevel = xl->getChildLevel()) {
232 TXsheet *childXsh = childLevel->getXsheet();
233 if (visited.count(childXsh) > 0) continue;
234 tovisit.insert(childXsh);
235 } else {
236 TXshLevel *xl2 = levelSet->getLevel(xl->getName());
237 if (xl2) {
238 if (xl2 != xl) {
239 cell.m_level = xl2;
240 lcolumn->setCell(r, cell);
241 }
242 } else {
243 xl->setScene(scene);
244 levelSet->insertLevel(xl);
245 }
246 }
247 }
248 }
249 }
250 }
251
252 //-----------------------------------------------------------------------------
253 } // namespace
254 //-----------------------------------------------------------------------------
255
256 //=============================================================================
257
deleteAllUntitledScenes()258 static void deleteAllUntitledScenes() {
259 TFilePath tempDir = getUntitledScenesDir();
260 try {
261 if (TFileStatus(tempDir).isDirectory()) {
262 TFilePathSet fps;
263 TSystem::readDirectory(fps, tempDir);
264 TFilePathSet::iterator fpsIt;
265 for (fpsIt = fps.begin(); fpsIt != fps.end(); ++fpsIt) {
266 TFilePath fp = *fpsIt;
267 if (TFileStatus(fp).isDirectory() &&
268 fp.getName().find("untitled") != -1)
269 TSystem::rmDirTree(fp);
270 }
271 }
272 } catch (...) {
273 }
274 }
275
276 //=============================================================================
277 // ToonzScene
278
ToonzScene()279 ToonzScene::ToonzScene() : m_contentHistory(0), m_isUntitled(true) {
280 m_childStack = new ChildStack(this);
281 m_properties = new TSceneProperties();
282 m_levelSet = new TLevelSet();
283 m_project = new TProject();
284 m_project->addRef();
285 }
286
287 //-----------------------------------------------------------------------------
288
~ToonzScene()289 ToonzScene::~ToonzScene() {
290 delete m_properties;
291 delete m_levelSet;
292 delete m_childStack;
293 delete m_contentHistory;
294
295 assert(m_project);
296 if (m_project) m_project->release();
297 }
298
299 //-----------------------------------------------------------------------------
300
setSceneName(std::wstring name)301 void ToonzScene::setSceneName(std::wstring name) {
302 m_scenePath = m_scenePath.withName(name);
303 }
304
305 //-----------------------------------------------------------------------------
306
clear()307 void ToonzScene::clear() {
308 if (isUntitled()) deleteUntitledScene(getScenePath().getParentDir());
309
310 m_childStack->clear();
311
312 m_scenePath = TFilePath();
313 TSceneProperties *properties = m_properties;
314 m_properties = new TSceneProperties();
315 delete properties;
316 m_levelSet->clear();
317 }
318
319 //-----------------------------------------------------------------------------
320
setProject(TProject * project)321 void ToonzScene::setProject(TProject *project) {
322 assert(project);
323
324 if (project != m_project) {
325 if (project) project->addRef();
326 if (m_project) m_project->release();
327 m_project = project;
328 }
329 }
330
331 //-----------------------------------------------------------------------------
332
getProject() const333 TProject *ToonzScene::getProject() const { return m_project; }
334
335 //-----------------------------------------------------------------------------
336
setScenePath(const TFilePath & fp)337 void ToonzScene::setScenePath(const TFilePath &fp) {
338 m_scenePath = fp;
339 m_isUntitled = false;
340 }
341
342 //-----------------------------------------------------------------------------
343
isUntitled() const344 bool ToonzScene::isUntitled() const {
345 return m_scenePath == TFilePath() || m_isUntitled;
346 }
347
348 //-----------------------------------------------------------------------------
349
load(const TFilePath & path,bool withProgressDialog)350 void ToonzScene::load(const TFilePath &path, bool withProgressDialog) {
351 loadNoResources(path);
352 loadResources(withProgressDialog);
353 }
354
355 //-----------------------------------------------------------------------------
356
357 // Funzioncina veloce per trovare il numero di frame di una scena senza caricare
358 // nulla. (implementato per Toonz 6.0 beta 1)
loadFrameCount(const TFilePath & fp)359 int ToonzScene::loadFrameCount(const TFilePath &fp) {
360 TIStream is(fp);
361 if (!is) throw TException(fp.getWideString() + L": Can't open file");
362 try {
363 // Leggo il primo tag (<tnz/tab>) ed estraggo il framecount (se c'e')
364 std::string tagName = "";
365 if (!is.matchTag(tagName)) throw TException("Bad file format");
366
367 if (tagName == "tab" || tagName == "tnz") {
368 int frameCount;
369 if (is.getTagParam("framecount", frameCount))
370 return frameCount;
371 else
372 return 0;
373 } else
374 throw TException("Bad file format");
375 } catch (TException &e) {
376 throw TIStreamException(is, e);
377 } catch (...) {
378 throw TIStreamException(is);
379 }
380
381 return 0;
382 }
383
384 //-----------------------------------------------------------------------------
385
loadNoResources(const TFilePath & fp)386 void ToonzScene::loadNoResources(const TFilePath &fp) {
387 clear();
388
389 TProjectManager *pm = TProjectManager::instance();
390 TProjectP sceneProject = pm->loadSceneProject(fp);
391 if (!sceneProject) return;
392
393 setProject(sceneProject.getPointer());
394
395 loadTnzFile(fp);
396 getXsheet()->updateFrameCount();
397 }
398
399 //-----------------------------------------------------------------------------
400 /*--
401 * プログレスダイアログをGUIからの実行時でのみ表示させる。tcomposerから実行の場合は表示させない
402 * --*/
loadResources(bool withProgressDialog)403 void ToonzScene::loadResources(bool withProgressDialog) {
404 /*--- m_levelSet->getLevelCount()が10個以上のとき表示させる ---*/
405 QProgressDialog *progressDialog = 0;
406 if (withProgressDialog && m_levelSet->getLevelCount() >= 10) {
407 progressDialog = new QProgressDialog("Loading Scene Resources", "", 0,
408 m_levelSet->getLevelCount());
409 progressDialog->setModal(true);
410 progressDialog->setAutoReset(
411 true); /*--maximumに到達したら自動でresetを呼ぶ--*/
412 progressDialog->setAutoClose(true); /*--resetが呼ばれたら自動で閉じる--*/
413 progressDialog->setAttribute(Qt::WA_DeleteOnClose,
414 true); /*--閉じたら自動でDeleteされる--*/
415 progressDialog->setCancelButton(0);
416 progressDialog->setValue(0);
417 progressDialog->show();
418 }
419
420 int i;
421 for (i = 0; i < m_levelSet->getLevelCount(); i++) {
422 if (progressDialog) progressDialog->setValue(i + 1);
423
424 TXshLevel *level = m_levelSet->getLevel(i);
425 try {
426 level->load();
427 } catch (...) {
428 }
429 }
430 getXsheet()->updateFrameCount();
431 }
432
433 //-----------------------------------------------------------------------------
434
loadTnzFile(const TFilePath & fp)435 void ToonzScene::loadTnzFile(const TFilePath &fp) {
436 bool reading22 = false;
437 TIStream is(fp);
438 if (!is) throw TException(fp.getWideString() + L": Can't open file");
439 try {
440 std::string tagName = "";
441 if (!is.matchTag(tagName)) throw TException("Bad file format");
442
443 if (tagName == "tab" || tagName == "tnz") {
444 std::string rootTagName = tagName;
445 std::string v = is.getTagAttribute("version");
446 VersionNumber versionNumber(0, 0);
447 int k = v.find(".");
448 if (k != (int)std::string::npos && 0 < k && k < (int)v.length()) {
449 versionNumber.first = std::stoi(v.substr(0, k));
450 versionNumber.second = std::stoi(v.substr(k + 1));
451 }
452 if (versionNumber == VersionNumber(0, 0))
453 throw TException("Bad version number :" + v);
454 setVersionNumber(versionNumber);
455 is.setVersion(versionNumber);
456 while (is.matchTag(tagName)) {
457 if (tagName == "generator") {
458 std::string program = is.getString();
459 // TODO: This obsolete condition should be removed before releasing OT
460 // v2.2 !
461 reading22 = program.find("2.2") != std::string::npos;
462 } else if (tagName == "properties")
463 m_properties->loadData(is, false);
464 else if (tagName == "palette") // per compatibilita' beta1
465 {
466 TPalette *palette = new TPalette;
467 is >> *palette;
468 delete palette;
469 } else if (tagName == "levelSet")
470 m_levelSet->loadData(is);
471 else if (tagName == "levels") {
472 // obsoleto
473 if (!reading22) assert(0);
474 while (!is.eos()) {
475 TPersist *p = 0;
476 is >> p;
477 TXshLevel *xshLevel = dynamic_cast<TXshLevel *>(p);
478 if (xshLevel) {
479 xshLevel->setScene(this);
480 TXshSimpleLevel *sl = xshLevel->getSimpleLevel();
481 if (reading22 && sl && sl->getPath() == TFilePath()) {
482 sl->setType(PLI_XSHLEVEL);
483 sl->setPath(TFilePath("+drawings/") +
484 (sl->getName() + L".pli"));
485 }
486 m_levelSet->insertLevel(xshLevel);
487 }
488 }
489 } else if (tagName == "xsheet")
490 is >> *getXsheet();
491 else if (tagName == "history") {
492 std::string historyData, s;
493 while (!is.eos()) {
494 is >> s;
495 historyData += s;
496 }
497 TContentHistory *history = getContentHistory(true);
498 history->deserialize(QString::fromStdString(historyData));
499 } else
500 throw TException(tagName + " : unexpected tag");
501
502 if (!is.matchEndTag()) throw TException(tagName + " : missing end tag");
503 }
504 if (!is.matchEndTag())
505 throw TException(rootTagName + " : missing end tag");
506 } else
507 throw TException("Bad file format");
508
509 setScenePath(fp);
510
511 for (int i = 0; i < m_levelSet->getLevelCount(); i++)
512 m_levelSet->getLevel(i)->setScene(this);
513
514 } catch (TException &e) {
515 throw TIStreamException(is, e);
516 } catch (...) {
517 throw TIStreamException(is);
518 }
519
520 m_properties->cloneCamerasTo(getXsheet()->getStageObjectTree());
521 fixBiancoProblem(this, getXsheet());
522 }
523
524 //-----------------------------------------------------------------------------
525
526 // saveUntitled() viene chiamata subito dopo newScene
527 // serve principalmente come lock per evitare che vengano create due scene
528 // con lo stesso nome.
529
setUntitled()530 void ToonzScene::setUntitled() {
531 m_isUntitled = true;
532 const std::string baseName = "untitled";
533 TFilePath tempDir = getUntitledScenesDir();
534 if (TFileStatus(tempDir).doesExist() == false) {
535 try {
536 TSystem::mkDir(tempDir);
537 } catch (...) {
538 }
539 }
540
541 std::string name = baseName;
542 if (TFileStatus(tempDir + name).doesExist()) {
543 int count = 2;
544 do {
545 name = baseName + std::to_string(count++);
546 } while (TFileStatus(tempDir + name).doesExist());
547 }
548 TFilePath fp = tempDir + name + (name + ".tnz");
549 try {
550 TSystem::touchParentDir(fp);
551 } catch (...) {
552 assert(0);
553 }
554 m_scenePath = fp;
555 }
556
557 //-----------------------------------------------------------------------------
558
559 // When saving as sub-xsheet, the sub becomes top. So, its cameras must be
560 // associated to the scene properties.
561 class CameraRedirection {
562 ToonzScene *m_scene;
563 TXsheet *m_xsh;
564
565 public:
CameraRedirection(ToonzScene * scene,TXsheet * xsh)566 CameraRedirection(ToonzScene *scene, TXsheet *xsh)
567 : m_scene(scene), m_xsh(xsh) {
568 if (!xsh) xsh = m_scene->getTopXsheet();
569
570 m_scene->getProperties()->cloneCamerasFrom(xsh->getStageObjectTree());
571 }
572
~CameraRedirection()573 ~CameraRedirection() {
574 if (m_xsh)
575 m_scene->getProperties()->cloneCamerasFrom(
576 m_scene->getTopXsheet()->getStageObjectTree());
577 }
578 };
579
580 //-----------------------------------------------------------------------------
581
save(const TFilePath & fp,TXsheet * subxsh)582 void ToonzScene::save(const TFilePath &fp, TXsheet *subxsh) {
583 TFilePath oldScenePath = getScenePath();
584 TFilePath newScenePath = fp;
585
586 CameraRedirection redir(this, subxsh);
587
588 bool wasUntitled = isUntitled();
589
590 setScenePath(fp);
591
592 TFileStatus fs(newScenePath);
593 if (fs.doesExist() && !fs.isWritable())
594 throw TSystemException(newScenePath,
595 "The scene cannot be saved: it is a read only "
596 "scene.\n All resources have been saved.");
597
598 TFilePath scenePath = decodeFilePath(fp);
599 TFilePath scenePathTemp(scenePath.getWideString() +
600 QString(".tmp").toStdWString());
601
602 if (Preferences::instance()->isBackupEnabled() &&
603 oldScenePath == newScenePath && TFileStatus(scenePath).doesExist())
604 saveBackup(scenePath);
605
606 if (TFileStatus(scenePathTemp).doesExist())
607 TSystem::removeFileOrLevel(scenePathTemp);
608
609 // TSystem::touchFile(scenePath);
610 TSystem::touchFile(scenePathTemp);
611 makeSceneIcon(this);
612
613 // TOStream os(scenePath, compressionEnabled);
614 // TOStream os(scenePath, false);
615 {
616 TOStream os(scenePathTemp, false);
617 if (!os.checkStatus())
618 throw TException("Could not open temporary save file");
619
620 TXsheet *xsh = subxsh;
621 if (xsh == 0) xsh = m_childStack->getTopXsheet();
622
623 std::map<std::string, std::string> attr;
624 attr["version"] =
625 (QString::number(l_currentVersion.first) +
626 "." // From now on, version numbers in saved files will have
627 + QString::number(
628 l_currentVersion.second)) // the signature "MAJOR.MINOR", where:
629 .toStdString(); //
630 attr["framecount"] =
631 QString::number( // MAJOR = Toonz version number * 10 (eg 7.0 => 70)
632 xsh->getFrameCount())
633 .toStdString(); // MINOR = Reset to 0 after each major
634 // increment,
635 // and
636 // advancing on its own when fixing bugs.
637 os.openChild("tnz", attr);
638
639 os.child("generator") << TEnv::getApplicationFullName();
640 os.openChild("properties");
641 m_properties->saveData(os);
642 os.closeChild();
643
644 if (subxsh) {
645 std::set<TXshLevel *> saveSet;
646 subxsh->getUsedLevels(saveSet);
647 m_levelSet->setSaveSet(saveSet);
648 }
649 os.openChild("levelSet");
650 m_levelSet->saveData(os);
651 os.closeChild();
652 std::set<TXshLevel *> emptySaveSet;
653 m_levelSet->setSaveSet(emptySaveSet);
654
655 os.openChild("xsheet");
656 os << *xsh;
657 os.closeChild();
658
659 if (getContentHistory()) {
660 os.openChild("history");
661 QString data = getContentHistory()->serialize();
662 int i = 0, j;
663 // non scrivo tutta la std::string di seguito per evitare problemi se
664 // diventa
665 // troppo lunga. Cerco di spezzarla in modo che sia "bella da leggere" nel
666 // tnz
667 while ((j = data.indexOf("||", i)) >= i) {
668 os << data.mid(i, j - i + 1).toStdWString();
669 os.cr();
670 i = j + 1;
671 }
672 os << data.mid(i).toStdWString();
673 os.closeChild();
674 }
675
676 os.closeChild();
677 bool status = os.checkStatus();
678 if (!status) throw TException("Could not complete the temporary save");
679 }
680
681 if (TFileStatus(scenePathTemp).doesExist())
682 TSystem::renameFile(scenePath, scenePathTemp, true);
683
684 if (subxsh) {
685 setScenePath(oldScenePath);
686 if (wasUntitled) setUntitled();
687 } else {
688 if (wasUntitled) deleteUntitledScene(oldScenePath.getParentDir());
689 }
690 // update the last saved version
691 setVersionNumber(l_currentVersion);
692 }
693
694 //-----------------------------------------------------------------------------
695
getFrameCount() const696 int ToonzScene::getFrameCount() const {
697 TXsheet *xsh = getXsheet();
698 return xsh ? xsh->getFrameCount() : 0;
699 }
700
701 //-----------------------------------------------------------------------------
702
renderFrame(const TRaster32P & ras,int row,const TXsheet * xsh,bool checkFlags) const703 void ToonzScene::renderFrame(const TRaster32P &ras, int row, const TXsheet *xsh,
704 bool checkFlags) const {
705 if (xsh == 0) xsh = getXsheet();
706
707 TCamera *camera = xsh->getStageObjectTree()->getCurrentCamera();
708 TDimension cameraRes = camera->getRes();
709 TDimensionD cameraSize = camera->getSize();
710
711 // voglio che la camera sia completamente contenuta dentro raster
712 double sx = (double)ras->getLx() / (double)cameraSize.lx;
713 double sy = (double)ras->getLy() / (double)cameraSize.ly;
714 double sc = (sx < sy) ? sx : sy;
715
716 const TAffine &cameraAff =
717 xsh->getPlacement(xsh->getStageObjectTree()->getCurrentCameraId(), row);
718 const TAffine &viewAff = TScale(sc / Stage::inch) * cameraAff.inv();
719
720 TRect clipRect(ras->getBounds());
721 TOfflineGL ogl(ras->getSize());
722 currentOfflineGL = &ogl;
723
724 ogl.makeCurrent();
725 {
726 glTranslated(0.5 * ras->getLx(), 0.5 * ras->getLy(), 0.0);
727
728 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
729 glClear(GL_COLOR_BUFFER_BIT);
730
731 ImagePainter::VisualSettings vs;
732 vs.m_plasticVisualSettings.m_drawMeshesWireframe = false;
733 vs.m_forSceneIcon = true;
734
735 Stage::RasterPainter painter(ras->getSize(), viewAff, clipRect, vs,
736 checkFlags);
737 Stage::visit(painter, const_cast<ToonzScene *>(this),
738 const_cast<TXsheet *>(xsh), row);
739
740 painter.flushRasterImages();
741 glFlush();
742
743 TRop::over(ras, ogl.getRaster());
744 }
745 ogl.doneCurrent();
746
747 currentOfflineGL = 0;
748 }
749
750 //-----------------------------------------------------------------------------
751
752 //! Performs a camera-stand render of the specified xsheet in the specified
753 //! placedRect,
754 //! with known world/placed reference change - and returns the result in a
755 //! 32-bit raster.
756
renderFrame(const TRaster32P & ras,int row,const TXsheet * xsh,const TRectD & placedRect,const TAffine & worldToPlacedAff) const757 void ToonzScene::renderFrame(const TRaster32P &ras, int row, const TXsheet *xsh,
758 const TRectD &placedRect,
759 const TAffine &worldToPlacedAff) const {
760 // Build reference change affines
761 const TAffine &placedToOglRefAff =
762 TScale(ras->getLx() / placedRect.getLx(),
763 ras->getLy() / placedRect.getLy()) *
764 TTranslation(-0.5 * (placedRect.x0 + placedRect.x1),
765 -0.5 * (placedRect.y0 + placedRect.y1));
766
767 const TAffine &cameraAff =
768 xsh->getPlacement(xsh->getStageObjectTree()->getCurrentCameraId(), row);
769
770 const TAffine &worldToOglRefAff =
771 placedToOglRefAff * worldToPlacedAff * cameraAff.inv();
772
773 TRect clipRect(ras->getBounds());
774
775 // fix for plastic tool applied to subxsheet
776 #ifdef MACOSX
777 QSurfaceFormat format;
778 format.setProfile(QSurfaceFormat::CompatibilityProfile);
779
780 std::unique_ptr<QOffscreenSurface> surface(new QOffscreenSurface());
781 surface->setFormat(format);
782 surface->create();
783
784 glPushAttrib(GL_ALL_ATTRIB_BITS);
785 glMatrixMode(GL_MODELVIEW), glPushMatrix();
786 glMatrixMode(GL_PROJECTION), glPushMatrix();
787 #else
788 TOfflineGL ogl(ras->getSize());
789 currentOfflineGL = &ogl;
790 ogl.makeCurrent();
791 #endif
792 {
793 #ifdef MACOSX
794 std::unique_ptr<QOpenGLFramebufferObject> fb(
795 new QOpenGLFramebufferObject(ras->getLx(), ras->getLy()));
796
797 fb->bind();
798 assert(glGetError() == GL_NO_ERROR);
799
800 glViewport(0, 0, ras->getLx(), ras->getLy());
801
802 glMatrixMode(GL_PROJECTION);
803 glLoadIdentity();
804 gluOrtho2D(0, ras->getLx(), 0, ras->getLy());
805
806 glMatrixMode(GL_MODELVIEW);
807 glLoadIdentity();
808 #endif
809 glTranslated(0.5 * ras->getLx(), 0.5 * ras->getLy(), 0.0);
810
811 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
812 glClear(GL_COLOR_BUFFER_BIT);
813
814 ImagePainter::VisualSettings vs;
815 vs.m_plasticVisualSettings.m_drawMeshesWireframe = false;
816
817 Stage::RasterPainter painter(ras->getSize(), worldToOglRefAff, clipRect, vs,
818 false);
819 Stage::visit(painter, const_cast<ToonzScene *>(this),
820 const_cast<TXsheet *>(xsh), row);
821
822 painter.flushRasterImages();
823 glFlush();
824 #ifdef MACOSX
825 QImage img =
826 fb->toImage().scaled(QSize(ras->getLx(), ras->getLy()),
827 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
828
829 int wrap = ras->getLx() * sizeof(TPixel32);
830 uchar *srcPix = img.bits();
831 uchar *dstPix = ras->getRawData() + wrap * (ras->getLy() - 1);
832 for (int y = 0; y < ras->getLy(); y++) {
833 memcpy(dstPix, srcPix, wrap);
834 dstPix -= wrap;
835 srcPix += wrap;
836 }
837 fb->release();
838 assert(glGetError() == GL_NO_ERROR);
839 #else
840 TRop::over(ras, ogl.getRaster());
841 #endif
842 }
843 #ifdef MACOSX
844 glMatrixMode(GL_MODELVIEW), glPopMatrix();
845 glMatrixMode(GL_PROJECTION), glPopMatrix();
846
847 glPopAttrib();
848 #else
849 ogl.doneCurrent();
850 currentOfflineGL = 0;
851 #endif
852 }
853
854 //-----------------------------------------------------------------------------
855
createNewLevel(int type,std::wstring levelName,const TDimension & dim,double dpi,TFilePath fp)856 TXshLevel *ToonzScene::createNewLevel(int type, std::wstring levelName,
857 const TDimension &dim, double dpi,
858 TFilePath fp) {
859 TLevelSet *levelSet = getLevelSet();
860
861 if (type == TZI_XSHLEVEL) // TZI type corresponds to the 'Scan Level'
862 type = OVL_XSHLEVEL; // default option. See Toonz Preferences class.
863
864 if (type == CHILD_XSHLEVEL && levelName == L"") levelName = L"sub";
865
866 // Select a different unique level name in case it already exists (either in
867 // scene or on disk)
868 {
869 const std::unique_ptr<NameBuilder> nameBuilder(
870 NameBuilder::getBuilder(levelName));
871
872 for (;;) {
873 levelName = nameBuilder->getNext();
874 /*-- levelが既にロード済みなら、次の名前を取得 --*/
875 if (m_levelSet->getLevel(levelName) != 0) continue;
876
877 /*-- LevelSetの中に同じファイルパスのLevelがあるかをチェック --*/
878 if (type != CHILD_XSHLEVEL && type != PLT_XSHLEVEL) {
879 if (fp.isEmpty()) fp = getDefaultLevelPath(type, levelName);
880 TFilePath actualFp = decodeFilePath(fp);
881
882 if (TSystem::doesExistFileOrLevel(
883 actualFp)) // if(TFileStatus(actualFp).doesExist()) continue;
884 {
885 fp = TFilePath();
886 continue;
887 }
888
889 int l, lCount = levelSet->getLevelCount();
890 for (l = 0; l != lCount; ++l) {
891 TXshLevel *xl = levelSet->getLevel(l);
892 if (!xl) continue;
893
894 TXshSimpleLevel *sl = xl->getSimpleLevel();
895 if (!sl) continue;
896
897 TFilePath lfp = decodeFilePath(sl->getPath());
898 if (actualFp == lfp) break;
899 }
900
901 if (l < lCount) {
902 /*-- fpが既存のLevelと重複したため、再設定する --*/
903 fp = TFilePath();
904 continue;
905 }
906 }
907 break;
908 }
909 }
910
911 TXshLevel *xl = 0;
912 if (type == CHILD_XSHLEVEL) {
913 TXshChildLevel *cl = new TXshChildLevel(levelName);
914 cl->setScene(this);
915 cl->getXsheet()->setScene(this);
916 xl = cl;
917
918 // Include the project's default cameras
919 const TSceneProperties &props =
920 TProjectManager::instance()->getCurrentProject()->getSceneProperties();
921
922 props.cloneCamerasTo(cl->getXsheet()->getStageObjectTree());
923 } else if (type == PLT_XSHLEVEL) {
924 TXshPaletteLevel *pl = new TXshPaletteLevel(levelName);
925 pl->setScene(this);
926 xl = pl;
927 } else {
928 TXshSimpleLevel *sl = new TXshSimpleLevel(levelName);
929
930 sl->setScene(this);
931 sl->setType(type);
932 sl->setPath(fp);
933 sl->setDirtyFlag(true);
934
935 sl->initializePalette();
936 sl->initializeResolutionAndDpi();
937
938 xl = sl;
939 }
940
941 m_levelSet->insertLevel(xl);
942 TNotifier::instance()->notify(TCastChange());
943
944 return xl;
945 }
946
947 //-----------------------------------------------------------------------------
948
getXsheet() const949 TXsheet *ToonzScene::getXsheet() const { return m_childStack->getXsheet(); }
950
951 //-----------------------------------------------------------------------------
952
getTopXsheet() const953 TXsheet *ToonzScene::getTopXsheet() const {
954 return m_childStack->getTopXsheet();
955 }
956
957 //-----------------------------------------------------------------------------
958
959 struct LevelType {
960 int m_ltype;
961 bool m_oldLevelFlag;
962 bool m_vectorNotPli;
963 std::string m_ext;
964 };
965
966 //-----------------------------------------------------------------------------
967
getLevelType(const TFilePath & fp)968 static LevelType getLevelType(const TFilePath &fp) {
969 LevelType ret;
970 ret.m_ltype = UNKNOWN_XSHLEVEL;
971 ret.m_oldLevelFlag = false;
972 ret.m_vectorNotPli = false;
973 ret.m_ext = fp.getType();
974 std::string format = ret.m_ext;
975
976 TFileType::Type type = TFileType::getInfo(fp);
977
978 switch (type) {
979 case TFileType::RASTER_IMAGE:
980 case TFileType::RASTER_LEVEL:
981 case TFileType::CMAPPED_LEVEL:
982 if (format == "tzp" || format == "tzu") {
983 ret.m_ltype = TZP_XSHLEVEL;
984 ret.m_oldLevelFlag = true;
985 ret.m_ext = "tlv";
986 } else if (format == "tzl" || format == "tlv")
987 ret.m_ltype = TZP_XSHLEVEL;
988 else if (format == "tzi")
989 ret.m_ltype = TZI_XSHLEVEL;
990 else
991 ret.m_ltype = OVL_XSHLEVEL;
992 break;
993
994 case TFileType::VECTOR_LEVEL:
995 if (format == "svg") {
996 ret.m_vectorNotPli = true;
997 ret.m_ext = "pli";
998 }
999 /* if (format == "svg")
1000 ret.m_ext = "pli";*/
1001 ret.m_ltype = PLI_XSHLEVEL;
1002 break;
1003
1004 case TFileType::AUDIO_LEVEL:
1005 ret.m_ltype = SND_XSHLEVEL;
1006 break;
1007
1008 case TFileType::MESH_IMAGE:
1009 case TFileType::MESH_LEVEL:
1010 ret.m_ltype = MESH_XSHLEVEL;
1011 break;
1012 default:
1013 break;
1014 }
1015
1016 return ret;
1017 }
1018
1019 //-----------------------------------------------------------------------------
1020
getImportedLevelPath(const TFilePath path) const1021 TFilePath ToonzScene::getImportedLevelPath(const TFilePath path) const {
1022 if (TFileType::getInfo(path) == TFileType::AUDIO_LEVEL)
1023 return path.withParentDir(TFilePath("+extras"));
1024 else if (TFileType::getInfo(path) == TFileType::PALETTE_LEVEL)
1025 return path.withParentDir(TFilePath("+palettes"));
1026
1027 const LevelType <ype = getLevelType(path);
1028 if (ltype.m_ltype == UNKNOWN_XSHLEVEL) return path;
1029
1030 const std::wstring &levelName = path.getWideName();
1031 const std::string &dots = path.getDots();
1032
1033 TFilePath importedLevelPath =
1034 getDefaultLevelPath(ltype.m_ltype, levelName).getParentDir() +
1035 path.getLevelNameW();
1036
1037 if (dots == "..")
1038 importedLevelPath = importedLevelPath.withFrame(TFrameId::EMPTY_FRAME);
1039
1040 if (importedLevelPath.getType() == "tlv") // Type shouldn't have changed...
1041 importedLevelPath =
1042 importedLevelPath.withNoFrame(); // Plus, should be unnecessary...
1043
1044 return importedLevelPath;
1045 }
1046
1047 //-----------------------------------------------------------------------------
1048
1049 /* tzp,tzu->tlv */
convertLevelIfNeeded(TFilePath & levelPath)1050 bool ToonzScene::convertLevelIfNeeded(TFilePath &levelPath) {
1051 LevelType ltype = getLevelType(levelPath);
1052 TFilePath fp = levelPath;
1053 if (ltype.m_vectorNotPli) {
1054 // livello flash o svg
1055 levelPath = levelPath.withType("pli");
1056 TLevelWriterP lw(levelPath);
1057 TLevelReaderP lr(fp);
1058 if (!lr) return false;
1059 TLevelP inLevel = lr->loadInfo();
1060 if (!inLevel || inLevel->getFrameCount() == 0) return false;
1061 TLevelP outLevel;
1062 for (TLevel::Iterator it = inLevel->begin(); it != inLevel->end(); ++it) {
1063 TVectorImageP img = lr->getFrameReader(it->first)->load();
1064 if (!img) continue;
1065 lw->getFrameWriter(it->first)->save(img);
1066 }
1067 } else if (ltype.m_oldLevelFlag) {
1068 TLevelP outLevel;
1069 // livello Toonz 4.6
1070 levelPath = TFilePath(levelPath.getParentDir().getWideString() + L"\\" +
1071 levelPath.getWideName() + L".tlv");
1072 if (TSystem::doesExistFileOrLevel(levelPath))
1073 TSystem::removeFileOrLevel(levelPath);
1074 TFilePath pltPath = TFilePath(levelPath.getParentDir().getWideString() +
1075 L"\\" + levelPath.getWideName() + L".tpl");
1076 if (TSystem::doesExistFileOrLevel(pltPath))
1077 TSystem::removeFileOrLevel(pltPath);
1078
1079 TLevelWriterP lw(levelPath);
1080 lw->setIconSize(Preferences::instance()->getIconSize());
1081 TPaletteP palette =
1082 ToonzImageUtils::loadTzPalette(fp.withType("plt").withNoFrame());
1083 TLevelReaderP lr(fp);
1084 if (!lr) return false;
1085 TLevelP inLevel = lr->loadInfo();
1086 if (!inLevel || inLevel->getFrameCount() == 0) return false;
1087 outLevel->setPalette(palette.getPointer());
1088 try {
1089 for (TLevel::Iterator it = inLevel->begin(); it != inLevel->end(); ++it) {
1090 TToonzImageP img = lr->getFrameReader(it->first)->load();
1091 if (!img) continue;
1092 img->setPalette(palette.getPointer());
1093 lw->getFrameWriter(it->first)->save(img);
1094 }
1095 } catch (TException &e) {
1096 // devo buttare il tlv che stavo salvando!
1097 QString msg = QString::fromStdWString(e.getMessage());
1098 if (msg == QString("Old 4.1 Palette")) {
1099 lw = TLevelWriterP();
1100 if (TSystem::doesExistFileOrLevel(levelPath))
1101 TSystem::removeFileOrLevel(levelPath);
1102 throw e;
1103 }
1104 }
1105 lw = TLevelWriterP(); // bisogna liberare prima lw di outLevel,
1106 // altrimenti la paletta che lw vuole scrivere e' gia' stata
1107 // loutLeveliberata.
1108 }
1109 return true;
1110 }
1111
1112 //-----------------------------------------------------------------------------
1113
loadLevel(const TFilePath & actualPath,const LevelOptions * levelOptions,std::wstring levelName,const std::vector<TFrameId> & fIds)1114 TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath,
1115 const LevelOptions *levelOptions,
1116 std::wstring levelName,
1117 const std::vector<TFrameId> &fIds) {
1118 LevelType ltype = getLevelType(actualPath);
1119 if (ltype.m_ltype == UNKNOWN_XSHLEVEL) return 0;
1120
1121 TFilePath levelPath = actualPath;
1122
1123 // Ensure the level name is unique
1124
1125 if (QString::fromStdWString(levelName).isEmpty()) {
1126 // if the option is set in the preferences,
1127 // remove the scene numbers("c####_") from the file name
1128 levelName = getLevelNameWithoutSceneNumber(levelPath.getWideName());
1129 }
1130
1131 NameModifier nm(levelName);
1132 levelName = nm.getNext();
1133 while (m_levelSet->hasLevel(levelName)) levelName = nm.getNext();
1134
1135 // Discriminate sound levels
1136 if (ltype.m_ltype == SND_XSHLEVEL) {
1137 TXshSoundLevel *sl = new TXshSoundLevel(levelName);
1138 sl->setType(ltype.m_ltype);
1139 sl->setScene(this);
1140 sl->setPath(codeFilePath(levelPath));
1141
1142 try {
1143 sl->load();
1144 } catch (const std::string &msg) // Intercepting std::string exceptions
1145 {
1146 throw TException(msg);
1147 } // from load, and rethrowing... curious!
1148
1149 m_levelSet->insertLevel(sl);
1150 return sl;
1151 } else {
1152 TXshSimpleLevel *xl = new TXshSimpleLevel(levelName);
1153 xl->setType(ltype.m_ltype);
1154 xl->setScene(this);
1155
1156 if (!convertLevelIfNeeded(levelPath))
1157 return 0; // Conversion failed, bail out
1158
1159 xl->setPath(codeFilePath(levelPath), true);
1160
1161 try {
1162 if (fIds.size() != 0 && !ltype.m_oldLevelFlag)
1163 xl->load(fIds);
1164 else
1165 xl->load();
1166 } catch (const std::string &msg) {
1167 throw TException(msg);
1168 }
1169
1170 if (ltype.m_oldLevelFlag)
1171 xl->setDirtyFlag(true); // Not set on old level formats?
1172
1173 LevelProperties *lp = xl->getProperties();
1174 assert(lp);
1175
1176 if (levelOptions)
1177 lp->options() = *levelOptions;
1178 else {
1179 const Preferences &prefs = *Preferences::instance();
1180 int formatIdx = prefs.matchLevelFormat(
1181 levelPath); // Should I use actualPath here? It's mostly
1182 // irrelevant anyway, it's for old tzp/tzu...
1183 if (formatIdx >= 0)
1184 lp->options() = prefs.levelFormat(formatIdx).m_options;
1185 else {
1186 // Default subsampling values are assigned from scene properties
1187 if (xl->getType() == OVL_XSHLEVEL)
1188 lp->setSubsampling(getProperties()->getFullcolorSubsampling());
1189 else if (xl->getType() == TZP_XSHLEVEL)
1190 lp->setSubsampling(getProperties()->getTlvSubsampling());
1191 }
1192 }
1193
1194 if (lp->getDpiPolicy() == LevelProperties::DP_ImageDpi) {
1195 // We must check whether the image actually has a dpi.
1196 const TPointD &imageDpi = xl->getImageDpi();
1197
1198 if (imageDpi == TPointD() ||
1199 Preferences::instance()->getUnits() == "pixel" ||
1200 Preferences::instance()->isIgnoreImageDpiEnabled()) {
1201 // Change to "Custom Dpi" policy and use camera dpi
1202 TStageObjectId cameraId =
1203 getXsheet()->getStageObjectTree()->getCurrentCameraId();
1204
1205 lp->setDpiPolicy(LevelProperties::DP_CustomDpi);
1206 lp->setDpi(getCurrentCamera()->getDpi());
1207 } else {
1208 // Has dpi alright - assign it to custom dpi, too
1209 lp->setDpi(imageDpi);
1210 }
1211 }
1212
1213 m_levelSet->insertLevel(xl);
1214
1215 return xl;
1216 }
1217
1218 return 0;
1219 }
1220
1221 //-----------------------------------------------------------------------------
1222
decodeFilePath(const TFilePath & path) const1223 TFilePath ToonzScene::decodeFilePath(const TFilePath &path) const {
1224 TProject *project = getProject();
1225 bool projectIsEmpty = project->getFolderCount() ? false : true;
1226 TFilePath fp = path;
1227
1228 std::wstring head;
1229 TFilePath tail;
1230 path.split(head, tail);
1231
1232 std::string h;
1233 std::wstring s;
1234 if (head != L"") {
1235 // in case of the project folder aliases
1236 if (head[0] == L'+') {
1237 if (TProjectManager::instance()->isTabModeEnabled()) {
1238 return m_scenePath.getParentDir() +
1239 (m_scenePath.getWideName() + L"_files") + tail;
1240 }
1241
1242 if (TProjectManager::instance()->isTabKidsModeEnabled()) {
1243 return m_scenePath.getParentDir() + m_scenePath.getWideName() + tail;
1244 }
1245
1246 if (projectIsEmpty) {
1247 TFilePath dir = m_scenePath.getParentDir();
1248 if (dir.getName() == "scenes")
1249 return dir.withName(head.substr(1)) + tail;
1250 else
1251 return dir + tail;
1252 }
1253 if (project) {
1254 h = ::to_string(head.substr(1));
1255 TFilePath f = project->getFolder(h);
1256 if (f != TFilePath()) s = f.getWideString();
1257 }
1258 }
1259 // in case of the scene folder alias
1260 else if (head == L"$scenefolder") {
1261 TFilePath dir = m_scenePath.getParentDir();
1262 return dir + tail;
1263 }
1264 }
1265 if (s != L"") {
1266 std::map<std::wstring, std::wstring> table;
1267
1268 // if the scene is untitled and path expansion depends on the scene
1269 // (because the expansion contains $ scenename, $ scenepath, or it uses the
1270 // savepath)
1271 if (m_isUntitled &&
1272 (s.find(L"$scene") != std::wstring::npos ||
1273 project->getUseScenePath(h) ||
1274 fp.getParentDir().getName() == getScenePath().getName())) {
1275 TFilePath parentDir = getScenePath().getParentDir();
1276 fp = parentDir + head.substr(1) + tail;
1277 } else {
1278 TFilePath scenePath = getScenePath();
1279 TFilePath scenePathRoot;
1280 if (project)
1281 scenePathRoot = project->getFolder(TProject::Scenes);
1282 else
1283 scenePathRoot = scenePath.getParentDir();
1284
1285 if (scenePathRoot != TFilePath() && !scenePathRoot.isAbsolute() &&
1286 project)
1287 scenePathRoot = project->getProjectFolder() + scenePathRoot;
1288
1289 if (TSystem::isUNC(scenePath) && !TSystem::isUNC(scenePathRoot))
1290 scenePathRoot = TSystem::toUNC(scenePathRoot);
1291
1292 if (scenePathRoot.isAncestorOf(scenePath))
1293 scenePath = scenePath - scenePathRoot;
1294
1295 table[L"$scenepath"] = scenePath.withType("").getWideString();
1296 table[L"$scenename"] = scenePath.withType("").getWideString();
1297
1298 std::map<std::wstring, std::wstring>::reverse_iterator it;
1299 for (it = table.rbegin(); it != table.rend(); ++it) {
1300 std::wstring keyword = it->first;
1301 int i = 0;
1302 for (;;) {
1303 int j = s.find(keyword, i);
1304 if (j == (int)std::wstring::npos) break;
1305 s.replace(j, keyword.length(), it->second);
1306 i = j;
1307 }
1308 }
1309 fp = TFilePath(s) + tail;
1310 }
1311 }
1312
1313 if (fp != TFilePath() && !fp.isAbsolute() && project)
1314 fp = project->getProjectFolder() + fp;
1315 return fp;
1316 }
1317
1318 //-----------------------------------------------------------------------------
1319
codeFilePath(const TFilePath & path) const1320 TFilePath ToonzScene::codeFilePath(const TFilePath &path) const {
1321 TFilePath fp(path);
1322 TProject *project = getProject();
1323
1324 Preferences::PathAliasPriority priority =
1325 Preferences::instance()->getPathAliasPriority();
1326 if (priority == Preferences::SceneFolderAlias &&
1327 codeFilePathWithSceneFolder(fp)) {
1328 return fp;
1329 }
1330
1331 if (project)
1332 for (int i = 0; i < project->getFolderCount(); i++) {
1333 TFilePath folderName("+" + project->getFolderName(i));
1334 TFilePath folderPath = decodeFilePath(folderName);
1335 if (folderPath.isAncestorOf(fp)) {
1336 fp = folderName + (fp - folderPath);
1337 return fp;
1338 }
1339 }
1340
1341 if (priority == Preferences::ProjectFolderAliases)
1342 codeFilePathWithSceneFolder(fp);
1343
1344 return fp;
1345 }
1346
1347 //-----------------------------------------------------------------------------
1348 // if the path is codable with $scenefolder alias, replace it and return true
1349
codeFilePathWithSceneFolder(TFilePath & path) const1350 bool ToonzScene::codeFilePathWithSceneFolder(TFilePath &path) const {
1351 // if the scene is untitled, then do nothing and return false
1352 if (isUntitled()) return false;
1353 TFilePath sceneFolderPath = m_scenePath.getParentDir();
1354 if (sceneFolderPath.isAncestorOf(path)) {
1355 path = TFilePath("$scenefolder") + (path - sceneFolderPath);
1356 return true;
1357 }
1358 return false;
1359 }
1360
1361 //-----------------------------------------------------------------------------
1362
getDefaultLevelPath(int levelType,std::wstring levelName) const1363 TFilePath ToonzScene::getDefaultLevelPath(int levelType,
1364 std::wstring levelName) const {
1365 TProject *project = getProject();
1366 assert(project);
1367 TFilePath levelPath;
1368 QString scanLevelType;
1369 switch (levelType) {
1370 case TZI_XSHLEVEL:
1371 scanLevelType = Preferences::instance()->getScanLevelType();
1372 levelPath = TFilePath(levelName + L".." + scanLevelType.toStdWString());
1373 break;
1374 case PLI_XSHLEVEL:
1375 levelPath = TFilePath(levelName).withType("pli");
1376 break;
1377 case TZP_XSHLEVEL:
1378 levelPath = TFilePath(levelName).withType("tlv");
1379 break;
1380 case OVL_XSHLEVEL:
1381 levelPath = TFilePath(levelName + L"..tif");
1382 break;
1383 default:
1384 levelPath = TFilePath(levelName + L"..png");
1385 }
1386
1387 if (!isUntitled() && Preferences::instance()->getPathAliasPriority() ==
1388 Preferences::SceneFolderAlias)
1389 return TFilePath("$scenefolder") + levelPath;
1390
1391 std::string folderName = getFolderName(levelType);
1392 if (project->getUseScenePath(folderName))
1393 return TFilePath("+" + folderName) + getSavePath() + levelPath;
1394 else
1395 return TFilePath("+" + folderName) + levelPath;
1396 }
1397
1398 //-----------------------------------------------------------------------------
1399
1400 const std::wstring savePathString(L"$savepath");
1401
1402 //-----------------------------------------------------------------------------
1403
codeSavePath(TFilePath path) const1404 TFilePath ToonzScene::codeSavePath(TFilePath path) const {
1405 if (path == TFilePath()) return path;
1406 TFilePath savePath = getSavePath();
1407 if (savePath == TFilePath()) return path; // non dovrebbe succedere mai
1408 TFilePath filename;
1409 TFilePath originalPath = path;
1410 if (savePath.withoutParentDir() != path.withoutParentDir()) {
1411 TFilePath parentDir = path.getParentDir();
1412 if (parentDir != TFilePath() && !parentDir.isRoot()) {
1413 filename = path.withoutParentDir();
1414 path = parentDir;
1415 } else
1416 return originalPath;
1417 }
1418
1419 TFilePath head;
1420 if (!checkTail(path, savePath, head)) return originalPath;
1421 if (head.getParentDir() != TFilePath() || head == TFilePath() ||
1422 head.getWideString()[0] != L'+')
1423 return originalPath;
1424 std::string folderName = ::to_string(head.getWideString().substr(1));
1425 if (!getProject()->getUseScenePath(folderName)) return originalPath;
1426 return head + savePathString + filename;
1427 }
1428
1429 //-----------------------------------------------------------------------------
1430
decodeSavePath(TFilePath path) const1431 TFilePath ToonzScene::decodeSavePath(TFilePath path) const {
1432 std::wstring s = path.getWideString();
1433 int i = s.find(savePathString);
1434 if (i != (int)std::wstring::npos) {
1435 TFilePath savePath = getSavePath();
1436 s.replace(i, savePathString.length(), savePath.getWideString());
1437 return TFilePath(s);
1438 }
1439 // in case of the scene folder alias (start with "$scenefolder")
1440 else if (s.find(L"$scenefolder") == 0) {
1441 s.replace(0, 12, m_scenePath.getParentDir().getWideString());
1442 return TFilePath(s);
1443 } else
1444 return path;
1445 }
1446
1447 //-----------------------------------------------------------------------------
1448
isExternPath(const TFilePath & fp) const1449 bool ToonzScene::isExternPath(const TFilePath &fp) const {
1450 TProject *project = m_project;
1451 assert(project);
1452 for (int i = 0; i < project->getFolderCount(); i++) {
1453 if (project->getFolderName(i) == "scenes") continue;
1454
1455 TFilePath folderPath =
1456 decodeFilePath(TFilePath("+" + project->getFolderName(i)));
1457 if (folderPath.isAncestorOf(fp)) return false;
1458 }
1459 return true;
1460 }
1461
1462 //-----------------------------------------------------------------------------
1463
getCurrentCamera()1464 TCamera *ToonzScene::getCurrentCamera() {
1465 return getXsheet()->getStageObjectTree()->getCurrentCamera();
1466 }
1467
1468 //-----------------------------------------------------------------------------
1469
getCurrentPreviewCamera()1470 TCamera *ToonzScene::getCurrentPreviewCamera() {
1471 return getXsheet()->getStageObjectTree()->getCurrentPreviewCamera();
1472 }
1473
1474 //-----------------------------------------------------------------------------
1475
getContentHistory(bool createIfNeeded)1476 TContentHistory *ToonzScene::getContentHistory(bool createIfNeeded) {
1477 if (!m_contentHistory && createIfNeeded)
1478 m_contentHistory = new TContentHistory(false);
1479 return m_contentHistory;
1480 }
1481
1482 //-----------------------------------------------------------------------------
1483
getSoundColumns(std::vector<TXshSoundColumn * > & columns)1484 void ToonzScene::getSoundColumns(std::vector<TXshSoundColumn *> &columns) {
1485 ToonzScene *scene = this;
1486 std::set<TXsheet *> visited, toVisit;
1487 TXsheet *xsh = scene->getChildStack()->getTopXsheet();
1488 visited.insert(xsh);
1489 toVisit.insert(xsh);
1490 while (!toVisit.empty()) {
1491 xsh = *toVisit.begin();
1492 toVisit.erase(xsh);
1493 for (int i = 0; i < xsh->getColumnCount(); i++) {
1494 TXshColumn *column = xsh->getColumn(i);
1495 if (!column) continue;
1496 if (TXshSoundColumn *sc = column->getSoundColumn())
1497 columns.push_back(sc);
1498 else if (TXshCellColumn *cc = column->getCellColumn()) {
1499 int r0 = 0, r1 = -1;
1500 cc->getRange(r0, r1);
1501 if (!cc->isEmpty() && r0 <= r1) {
1502 for (int r = r0; r <= r1; r++) {
1503 TXshCell cell = cc->getCell(r);
1504 if (cell.m_level && cell.m_level->getChildLevel()) {
1505 TXsheet *subxsh = cell.m_level->getChildLevel()->getXsheet();
1506 if (visited.find(subxsh) == visited.end()) {
1507 visited.insert(subxsh);
1508 toVisit.insert(subxsh);
1509 }
1510 }
1511 }
1512 }
1513 }
1514 }
1515 }
1516 }
1517
1518 //-----------------------------------------------------------------------------
1519
updateSoundColumnFrameRate()1520 void ToonzScene::updateSoundColumnFrameRate() {
1521 std::vector<TXshSoundColumn *> soundColumns;
1522 getSoundColumns(soundColumns);
1523
1524 TSceneProperties *properties = getProperties();
1525 if (!properties) return;
1526
1527 TOutputProperties *outputProperties = properties->getOutputProperties();
1528 if (!outputProperties) return;
1529
1530 double frameRate = outputProperties->getFrameRate();
1531
1532 int i;
1533 for (i = 0; i < (int)soundColumns.size(); i++)
1534 soundColumns[i]->updateFrameRate(frameRate);
1535 }
1536
1537 //-----------------------------------------------------------------------------
1538
getIconPath(const TFilePath & scenePath)1539 TFilePath ToonzScene::getIconPath(const TFilePath &scenePath) {
1540 return scenePath.getParentDir() + "sceneIcons" +
1541 (scenePath.getWideName() + L" .png");
1542 }
1543
1544 //-----------------------------------------------------------------------------
1545
1546 // se la scena sta in +scenes/pippo.tnz => pippo
1547 // se la scena sta in +scenes/pluto/pippo.tnz => pluto/pippo
1548 // se la scena e' untitledxxx.tnz => untitledxxx
1549
getSavePath() const1550 TFilePath ToonzScene::getSavePath() const {
1551 std::string sceneName = getScenePath().getName();
1552 if (isUntitled()) return TFilePath(sceneName);
1553 TFilePath sceneRoot = decodeFilePath(TFilePath("+" + TProject::Scenes));
1554 TFilePath scenePath = getScenePath().withType("");
1555 TFilePath savePath(sceneName);
1556 if (sceneRoot.isAncestorOf(scenePath)) savePath = scenePath - sceneRoot;
1557 return savePath;
1558 }
1559
1560 //---------------------------------------------------------------------------
1561
shiftCameraX(double val)1562 double ToonzScene::shiftCameraX(double val) {
1563 TStageObjectTree *tree = getXsheet()->getStageObjectTree();
1564
1565 TStageObject *stageObject = tree->getStageObject(tree->getCurrentCameraId());
1566 TPointD ret = stageObject->getOffset();
1567 stageObject->setOffset(TPointD(ret.x + val, ret.y));
1568 return ret.x;
1569 }
1570
1571 //----------------------------------------------------------------------------
1572 // if the option is set in the preferences,
1573 // remove the scene numbers("c####_") from the file name
1574
getLevelNameWithoutSceneNumber(std::wstring orgName)1575 std::wstring ToonzScene::getLevelNameWithoutSceneNumber(std::wstring orgName) {
1576 if (!Preferences::instance()->isRemoveSceneNumberFromLoadedLevelNameEnabled())
1577 return orgName;
1578
1579 QString orgNameQstr = QString::fromStdWString(orgName);
1580
1581 // do nothing if the file name has less than 7 letters
1582 if (orgNameQstr.size() <= 6) return orgName;
1583
1584 QString sceneName = QString::fromStdWString(getSceneName()).left(5);
1585
1586 // check if the first 5 letters of the level name is the same as the scene
1587 // name.
1588 // note that we must consider following both cases; "c0001_hogehoge.tif" and
1589 // "c0001A_####.tif"
1590 if (!orgNameQstr.startsWith(sceneName)) return orgName;
1591
1592 if (!orgNameQstr.contains("_")) return orgName;
1593
1594 return orgNameQstr.right(orgNameQstr.size() - orgNameQstr.indexOf("_") - 1)
1595 .toStdWString();
1596 }
1597