1 #include "toonz/txshsimplelevel.h"
2 #include "imagebuilders.h"
3
4 // TnzLib includes
5 #include "toonz/txshleveltypes.h"
6 #include "toonz/imagemanager.h"
7 #include "toonz/studiopalette.h"
8 #include "toonz/hook.h"
9 #include "toonz/toonzscene.h"
10 #include "toonz/levelproperties.h"
11 #include "toonz/levelupdater.h"
12 #include "toonz/fullcolorpalette.h"
13 #include "toonz/preferences.h"
14 #include "toonz/stage.h"
15 #include "toonz/textureutils.h"
16 #include "toonz/levelset.h"
17 #include "toonz/tcamera.h"
18
19 // TnzBase includes
20 #include "tenv.h"
21
22 // TnzCore includes
23 #include "trasterimage.h"
24 #include "tvectorimage.h"
25 #include "tmeshimage.h"
26 #include "timagecache.h"
27 #include "tofflinegl.h"
28 #include "tvectorgl.h"
29 #include "tvectorrenderdata.h"
30 #include "tropcm.h"
31 #include "tpixelutils.h"
32 #include "timageinfo.h"
33 #include "tlogger.h"
34 #include "tstream.h"
35 #include "tsystem.h"
36 #include "tcontenthistory.h"
37
38 // Qt includes
39 #include <QDir>
40 #include <QRegExp>
41 #include <QMessageBox>
42 #include <QtCore>
43
44 #include "../common/psdlib/psd.h"
45
46 //******************************************************************************************
47 // Global stuff
48 //******************************************************************************************
49
50 DEFINE_CLASS_CODE(TXshSimpleLevel, 20)
51 PERSIST_IDENTIFIER(TXshSimpleLevel, "level")
52
53 //******************************************************************************************
54 // Local namespace stuff
55 //******************************************************************************************
56
57 namespace {
58
59 int idBaseCode = 1;
60
61 //-----------------------------------------------------------------------------
62
63 struct CompatibilityStruct {
64 int writeMask, neededMask, forbiddenMask;
65 };
66
67 CompatibilityStruct compatibility = {
68 0x00F1, // mask written. Note: Student main must be 0x00F2
69 // Note: the 0x00F0 part is currently not used.
70 0x0000, // mandatory mask: loaded levels MUST have a mask with these bits
71 // set
72 // Note: if mandatory mask != 0 then no old level (without
73 // mask)
74 // can be loaded
75 // Note: this mask is currently not used.
76 0x000E // forbidden mask: loaded levels MUST NOT have a mask with these
77 // bits set
78 //
79 };
80
81 //-----------------------------------------------------------------------------
82
rasterized(std::string id)83 inline std::string rasterized(std::string id) { return id + "_rasterized"; }
filled(std::string id)84 inline std::string filled(std::string id) { return id + "_filled"; }
85
86 //-----------------------------------------------------------------------------
87
getCreatorString()88 QString getCreatorString() {
89 QString creator = QString::fromStdString(TEnv::getApplicationName()) + " " +
90 QString::fromStdString(TEnv::getApplicationVersion()) +
91 " CM(" + QString::number(compatibility.writeMask, 16) + ")";
92 return creator;
93 }
94
95 //-----------------------------------------------------------------------------
96
checkCreatorString(const QString & creator)97 bool checkCreatorString(const QString &creator) {
98 int mask = 0;
99 if (creator != "") {
100 QRegExp rx("CM\\([0-9A-Fa-f]*\\)");
101 int pos = rx.indexIn(creator);
102 int len = rx.matchedLength();
103 if (pos >= 0 && len >= 4) {
104 QString v;
105 if (len > 4) v = creator.mid(pos + 3, len - 4);
106 bool ok = true;
107 mask = v.toInt(&ok, 16);
108 }
109 }
110 return (mask & compatibility.neededMask) == compatibility.neededMask &&
111 (mask & compatibility.forbiddenMask) == 0;
112 }
113
114 //-----------------------------------------------------------------------------
115
isAreadOnlyLevel(const TFilePath & path)116 bool isAreadOnlyLevel(const TFilePath &path) {
117 if (path.isEmpty() || !path.isAbsolute()) return false;
118 if (path.getDots() == "." ||
119 (path.getDots() == ".." &&
120 (path.getType() == "tlv" || path.getType() == "tpl"))) {
121 if (path.getType() == "psd" || path.getType() == "gif" ||
122 path.getType() == "mp4" || path.getType() == "webm")
123 return true;
124 if (!TSystem::doesExistFileOrLevel(path)) return false;
125 TFileStatus fs(path);
126 return !fs.isWritable();
127 }
128 /*- 処理が重くなるので、連番ファイルは全てfalseを返す -*/
129 /*
130 else if(path.getDots() == "..")
131 {
132 TFilePath dir = path.getParentDir();
133 QDir qDir(QString::fromStdWString(dir.getWideString()));
134 QString levelName =
135 QRegExp::escape(QString::fromStdWString(path.getWideName()));
136 QString levelType = QString::fromStdString(path.getType());
137 QString exp(levelName+".[0-9]{1,4}."+levelType);
138 QRegExp regExp(exp);
139 QStringList list = qDir.entryList(QDir::Files);
140 QStringList livelFrames = list.filter(regExp);
141
142 bool isReadOnly=false;
143 int i;
144 for(i=0; i<livelFrames.size() && !isReadOnly; i++)
145 {
146 TFilePath frame = dir+TFilePath(livelFrames[i].toStdWString());
147 if(frame.isEmpty() || !frame.isAbsolute()) continue;
148 TFileStatus fs(frame);
149 isReadOnly = !fs.isWritable();
150 }
151 return isReadOnly;
152 }
153 */
154 else
155 return false;
156 }
157
158 //-----------------------------------------------------------------------------
159
getIndexesRangefromFids(TXshSimpleLevel * level,const std::set<TFrameId> & fids,int & fromIndex,int & toIndex)160 void getIndexesRangefromFids(TXshSimpleLevel *level,
161 const std::set<TFrameId> &fids, int &fromIndex,
162 int &toIndex) {
163 if (fids.empty()) {
164 fromIndex = toIndex = -1;
165 return;
166 }
167
168 toIndex = 0;
169 fromIndex = level->getFrameCount() - 1;
170
171 std::set<TFrameId>::const_iterator it;
172 for (it = fids.begin(); it != fids.end(); ++it) {
173 int index = level->guessIndex(*it);
174 if (index > toIndex) toIndex = index;
175 if (index < fromIndex) fromIndex = index;
176 }
177 }
178
179 } // namespace
180
181 //******************************************************************************************
182 // TXshSimpleLevel implementation
183 //******************************************************************************************
184
185 bool TXshSimpleLevel::m_rasterizePli = false;
186 bool TXshSimpleLevel::m_fillFullColorRaster = false;
187
188 //-----------------------------------------------------------------------------
189
TXshSimpleLevel(const std::wstring & name)190 TXshSimpleLevel::TXshSimpleLevel(const std::wstring &name)
191 : TXshLevel(m_classCode, name)
192 , m_properties(new LevelProperties)
193 , m_palette(0)
194 , m_idBase(std::to_string(idBaseCode++))
195 , m_editableRangeUserInfo(L"")
196 , m_isSubsequence(false)
197 , m_16BitChannelLevel(false)
198 , m_isReadOnly(false)
199 , m_temporaryHookMerged(false) {}
200
201 //-----------------------------------------------------------------------------
202
~TXshSimpleLevel()203 TXshSimpleLevel::~TXshSimpleLevel() {
204 clearFrames();
205
206 if (m_palette) m_palette->release();
207 }
208
209 //-----------------------------------------------------------------------------
210
setEditableRange(unsigned int from,unsigned int to,const std::wstring & userName)211 void TXshSimpleLevel::setEditableRange(unsigned int from, unsigned int to,
212 const std::wstring &userName) {
213 assert(from <= to && to < (unsigned int)getFrameCount());
214 unsigned int i;
215 for (i = from; i <= to; i++) m_editableRange.insert(index2fid(i));
216
217 QString hostName = TSystem::getHostName();
218 m_editableRangeUserInfo = userName + L"_" + hostName.toStdWString();
219
220 std::wstring fileName = getEditableFileName();
221 TFilePath dstPath = getScene()->decodeFilePath(m_path);
222 dstPath = dstPath.withName(fileName).withType(dstPath.getType());
223
224 // Load temporary level file (for pli and tlv types only)
225 if (getType() != OVL_XSHLEVEL && TSystem::doesExistFileOrLevel(dstPath)) {
226 TLevelReaderP lr(dstPath);
227 TLevelP level = lr->loadInfo();
228 setPalette(level->getPalette());
229 for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
230 TImageP img = lr->getFrameReader(it->first)->load();
231 setFrame(it->first, img);
232 }
233 }
234
235 // Merge temporary hook file with current hookset
236 const TFilePath &hookFile = getHookPath(dstPath);
237 mergeTemporaryHookFile(from, to, hookFile);
238 }
239
240 //-----------------------------------------------------------------------------
241
mergeTemporaryHookFile(unsigned int from,unsigned int to,const TFilePath & hookFile)242 void TXshSimpleLevel::mergeTemporaryHookFile(unsigned int from, unsigned int to,
243 const TFilePath &hookFile) {
244 if (!TFileStatus(hookFile).doesExist()) return;
245
246 HookSet *tempHookSet = new HookSet;
247 TIStream is(hookFile);
248 std::string tagName;
249 try {
250 if (is.matchTag(tagName) && tagName == "hooks") tempHookSet->loadData(is);
251 } catch (...) {
252 }
253
254 HookSet *hookSet = getHookSet();
255 int tempHookCount = tempHookSet->getHookCount();
256
257 if (tempHookCount == 0) {
258 for (unsigned int f = from; f <= to; f++) {
259 TFrameId fid = index2fid(f);
260 hookSet->eraseFrame(fid);
261 }
262 } else {
263 for (int i = 0; i < tempHookCount; i++) {
264 Hook *hook = tempHookSet->getHook(i);
265 Hook *newHook = hookSet->touchHook(hook->getId());
266 newHook->setTrackerObjectId(hook->getTrackerObjectId());
267 newHook->setTrackerRegionHeight(hook->getTrackerRegionHeight());
268 newHook->setTrackerRegionWidth(hook->getTrackerRegionWidth());
269 for (unsigned int f = from; f <= to; f++) {
270 TFrameId fid = index2fid(f);
271 newHook->setAPos(fid, hook->getAPos(fid));
272 newHook->setBPos(fid, hook->getBPos(fid));
273 }
274 }
275 }
276
277 m_temporaryHookMerged = true;
278 }
279
280 //-----------------------------------------------------------------------------
281
clearEditableRange()282 void TXshSimpleLevel::clearEditableRange() {
283 m_editableRange.clear();
284 m_editableRangeUserInfo = L"";
285 }
286
287 //-----------------------------------------------------------------------------
288
getEditableFileName()289 std::wstring TXshSimpleLevel::getEditableFileName() {
290 #ifdef MACOSX
291 std::wstring fileName = L"." + m_path.getWideName();
292 #else
293 std::wstring fileName = m_path.getWideName();
294 #endif
295 fileName += L"_" + m_editableRangeUserInfo;
296 int from, to;
297 getIndexesRangefromFids(this, m_editableRange, from, to);
298 if (from == -1 && to == -1) return L"";
299 fileName += L"_" + std::to_wstring(from + 1) + L"-" + std::to_wstring(to + 1);
300 return fileName;
301 }
302
303 //-----------------------------------------------------------------------------
304
getEditableRange()305 std::set<TFrameId> TXshSimpleLevel::getEditableRange() {
306 return m_editableRange;
307 }
308
309 //-----------------------------------------------------------------------------
310
setRenumberTable()311 void TXshSimpleLevel::setRenumberTable() {
312 m_renumberTable.clear();
313
314 FramesSet::iterator ft, fEnd = m_frames.end();
315 for (ft = m_frames.begin(); ft != fEnd; ++ft) m_renumberTable[*ft] = *ft;
316 }
317
318 //-----------------------------------------------------------------------------
319
setDirtyFlag(bool on)320 void TXshSimpleLevel::setDirtyFlag(bool on) { m_properties->setDirtyFlag(on); }
321
322 //-----------------------------------------------------------------------------
323
getDirtyFlag() const324 bool TXshSimpleLevel::getDirtyFlag() const {
325 return m_properties->getDirtyFlag();
326 }
327
328 //-----------------------------------------------------------------------------
329
touchFrame(const TFrameId & fid)330 void TXshSimpleLevel::touchFrame(const TFrameId &fid) {
331 m_properties->setDirtyFlag(true);
332 TContentHistory *ch = getContentHistory();
333 if (!ch) {
334 ch = new TContentHistory(true);
335 setContentHistory(ch);
336 }
337 ch->frameModifiedNow(fid);
338
339 if (getType() == PLI_XSHLEVEL) {
340 std::string id = rasterized(getImageId(fid));
341 ImageManager::instance()->invalidate(id);
342 }
343 if (getType() & FULLCOLOR_TYPE) {
344 std::string id = filled(getImageId(fid));
345 ImageManager::instance()->invalidate(id);
346 }
347 }
348
349 //-----------------------------------------------------------------------------
350
onPaletteChanged()351 void TXshSimpleLevel::onPaletteChanged() {
352 FramesSet::iterator ft, fEnd = m_frames.end();
353 for (ft = m_frames.begin(); ft != fEnd; ++ft) {
354 const TFrameId &fid = *ft;
355
356 if (getType() == PLI_XSHLEVEL) {
357 std::string id = rasterized(getImageId(fid));
358 ImageManager::instance()->invalidate(id);
359 }
360 if (getType() & FULLCOLOR_TYPE) {
361 std::string id = filled(getImageId(fid));
362 ImageManager::instance()->invalidate(id);
363 }
364
365 texture_utils::invalidateTexture(this, fid);
366 }
367 }
368
369 //-----------------------------------------------------------------------------
370
setScannedPath(const TFilePath & fp)371 void TXshSimpleLevel::setScannedPath(const TFilePath &fp) {
372 m_scannedPath = fp;
373 }
374
375 //-----------------------------------------------------------------------------
376
setPath(const TFilePath & fp,bool keepFrames)377 void TXshSimpleLevel::setPath(const TFilePath &fp, bool keepFrames) {
378 m_path = fp;
379 if (!keepFrames) {
380 clearFrames();
381 assert(getScene());
382 try {
383 load();
384 } catch (...) {
385 }
386 }
387
388 if (getType() != PLI_XSHLEVEL) {
389 if (!m_frames.empty()) {
390 std::string imageId = getImageId(getFirstFid());
391 const TImageInfo *imageInfo =
392 ImageManager::instance()->getInfo(imageId, ImageManager::none, 0);
393 if (imageInfo) {
394 TDimension imageRes(0, 0);
395 TPointD imageDpi;
396 imageRes.lx = imageInfo->m_lx;
397 imageRes.ly = imageInfo->m_ly;
398 imageDpi.x = imageInfo->m_dpix;
399 imageDpi.y = imageInfo->m_dpiy;
400 m_properties->setImageDpi(imageDpi);
401 m_properties->setImageRes(imageRes);
402 m_properties->setBpp(imageInfo->m_bitsPerSample *
403 imageInfo->m_samplePerPixel);
404 }
405 }
406 }
407 }
408
409 //-----------------------------------------------------------------------------
410
clonePropertiesFrom(const TXshSimpleLevel * oldSl)411 void TXshSimpleLevel::clonePropertiesFrom(const TXshSimpleLevel *oldSl) {
412 m_properties->setImageDpi(
413 oldSl->m_properties
414 ->getImageDpi()); // Watch out - may change dpi policy!
415 m_properties->setDpi(oldSl->m_properties->getDpi());
416 m_properties->setDpiPolicy(oldSl->m_properties->getDpiPolicy());
417 m_properties->setImageRes(oldSl->m_properties->getImageRes());
418 m_properties->setBpp(oldSl->m_properties->getBpp());
419 m_properties->setSubsampling(oldSl->m_properties->getSubsampling());
420 }
421
422 //-----------------------------------------------------------------------------
423
getPalette() const424 TPalette *TXshSimpleLevel::getPalette() const { return m_palette; }
425
426 //-----------------------------------------------------------------------------
427
setPalette(TPalette * palette)428 void TXshSimpleLevel::setPalette(TPalette *palette) {
429 if (m_palette != palette) {
430 if (m_palette) m_palette->release();
431
432 m_palette = palette;
433 if (m_palette) {
434 m_palette->addRef();
435 if (!(getType() & FULLCOLOR_TYPE)) m_palette->setPaletteName(getName());
436 }
437 }
438 }
439
440 //-----------------------------------------------------------------------------
441
getFids(std::vector<TFrameId> & fids) const442 void TXshSimpleLevel::getFids(std::vector<TFrameId> &fids) const {
443 fids.assign(m_frames.begin(), m_frames.end());
444 }
445
446 //-----------------------------------------------------------------------------
447
getFids() const448 std::vector<TFrameId> TXshSimpleLevel::getFids() const {
449 return std::vector<TFrameId>(m_frames.begin(), m_frames.end());
450 }
451
452 //-----------------------------------------------------------------------------
453
isFid(const TFrameId & fid) const454 bool TXshSimpleLevel::isFid(const TFrameId &fid) const {
455 return m_frames.count(fid);
456 }
457
458 //-----------------------------------------------------------------------------
459
getFrameId(int index) const460 const TFrameId &TXshSimpleLevel::getFrameId(int index) const {
461 return *(m_frames.begin() += index);
462 }
463
464 //-----------------------------------------------------------------------------
465
getFirstFid() const466 TFrameId TXshSimpleLevel::getFirstFid() const {
467 return !isEmpty() ? *m_frames.begin() : TFrameId(TFrameId::NO_FRAME);
468 }
469
470 //-----------------------------------------------------------------------------
471
getLastFid() const472 TFrameId TXshSimpleLevel::getLastFid() const {
473 return !isEmpty() ? *m_frames.rbegin() : TFrameId(TFrameId::NO_FRAME);
474 }
475
476 //-----------------------------------------------------------------------------
477
guessStep() const478 int TXshSimpleLevel::guessStep() const {
479 int frameCount = m_frames.size();
480 if (frameCount < 2)
481 return 1; // un livello con zero o un frame ha per def. step=1
482
483 FramesSet::const_iterator ft = m_frames.begin();
484
485 TFrameId firstFid = *ft++, secondFid = *ft++;
486
487 if (firstFid.getLetter() != 0 || secondFid.getLetter() != 0) return 1;
488
489 int step = secondFid.getNumber() - firstFid.getNumber();
490 if (step == 1) return 1;
491
492 // controllo subito se lo step vale per l'ultimo frame
493 // (cerco di limitare il numero di volte in cui devo controllare tutta la
494 // lista)
495 TFrameId lastFid = *m_frames.rbegin();
496 if (lastFid.getLetter() != 0) return 1;
497
498 if (lastFid.getNumber() != firstFid.getNumber() + step * (frameCount - 1))
499 return 1;
500
501 for (int i = 2; ft != m_frames.end(); ++ft, ++i) {
502 const TFrameId &fid = *ft;
503
504 if (fid.getLetter() != 0) return 1;
505
506 if (fid.getNumber() != firstFid.getNumber() + step * i) return 1;
507 }
508
509 return step;
510 }
511
512 //-----------------------------------------------------------------------------
513
fid2index(const TFrameId & fid) const514 int TXshSimpleLevel::fid2index(const TFrameId &fid) const {
515 FramesSet::const_iterator ft = m_frames.find(fid);
516 return (ft != m_frames.end()) ? std::distance(m_frames.begin(), ft)
517 : // Note: flat_set has random access
518 -1; // iterators, so this is FAST
519 }
520
521 //-----------------------------------------------------------------------------
522
guessIndex(const TFrameId & fid) const523 int TXshSimpleLevel::guessIndex(const TFrameId &fid) const {
524 if (m_frames.empty()) return 0; // no frames, return 0 (by definition)
525
526 FramesSet::const_iterator ft = m_frames.lower_bound(fid);
527 if (ft == m_frames.end()) {
528 const TFrameId &maxFid = *m_frames.rbegin();
529 assert(fid > maxFid);
530
531 // fid not in the table, but greater than the last one.
532 // return a suitable index. (e.g. frames are 1,3,5,7; fid2index(11) should
533 // return index=5)
534 int step = guessStep();
535 int i = (fid.getNumber() - maxFid.getNumber()) / step;
536 return m_frames.size() - 1 + i;
537 } else
538 return std::distance(m_frames.begin(), ft);
539 }
540
541 //-----------------------------------------------------------------------------
542
index2fid(int index) const543 TFrameId TXshSimpleLevel::index2fid(int index) const {
544 if (index < 0) return TFrameId(-2);
545
546 int frameCount = m_frames.size();
547 if (frameCount == 0) return TFrameId(1); // o_o?
548
549 if (index < frameCount) {
550 FramesSet::const_iterator ft = m_frames.begin();
551 std::advance(ft, index);
552 return *ft;
553 } else {
554 int step = guessStep();
555 TFrameId maxFid = *m_frames.rbegin();
556 int d = step * (index - frameCount + 1);
557 return TFrameId(maxFid.getNumber() + d);
558 }
559 }
560
561 //-----------------------------------------------------------------------------
562
getFrame(const TFrameId & fid,UCHAR imFlags,int subsampling) const563 TImageP TXshSimpleLevel::getFrame(const TFrameId &fid, UCHAR imFlags,
564 int subsampling) const {
565 assert(m_type != UNKNOWN_XSHLEVEL);
566
567 // If the required frame is not in range, quit
568 if (m_frames.count(fid) == 0) return TImageP();
569
570 const std::string &imgId = getImageId(fid);
571
572 ImageLoader::BuildExtData extData(this, fid, subsampling);
573 TImageP img = ImageManager::instance()->getImage(imgId, imFlags, &extData);
574
575 if (imFlags & ImageManager::toBeModified) {
576 // The image will be modified. Perform any related invalidation.
577 texture_utils::invalidateTexture(
578 this, fid); // We must rebuild associated textures
579 }
580
581 return img;
582 }
583
584 //-----------------------------------------------------------------------------
585
getFrameInfo(const TFrameId & fid,bool toBeModified)586 TImageInfo *TXshSimpleLevel::getFrameInfo(const TFrameId &fid,
587 bool toBeModified) {
588 assert(m_type != UNKNOWN_XSHLEVEL);
589
590 // If the required frame is not in range, quit
591 if (m_frames.count(fid) == 0) return 0;
592
593 const std::string &imgId = getImageId(fid);
594
595 TImageInfo *info = ImageManager::instance()->getInfo(
596 imgId, toBeModified ? ImageManager::toBeModified : ImageManager::none, 0);
597
598 return info;
599 }
600
601 //-----------------------------------------------------------------------------
602
getFrameIcon(const TFrameId & fid) const603 TImageP TXshSimpleLevel::getFrameIcon(const TFrameId &fid) const {
604 assert(m_type != UNKNOWN_XSHLEVEL);
605
606 if (m_frames.count(fid) == 0) return TImageP();
607
608 // NOTE: Icons caching is DISABLED at this stage. It is now responsibility of
609 // ToonzQt's IconGenerator class.
610
611 ImageLoader::BuildExtData extData(this, fid);
612 extData.m_subs = 1, extData.m_icon = true;
613
614 const std::string &imgId = getImageId(fid);
615 TImageP img = ImageManager::instance()->getImage(
616 imgId, ImageManager::dontPutInCache, &extData);
617
618 TToonzImageP timg = (TToonzImageP)img;
619 if (timg && m_palette) timg->setPalette(m_palette);
620
621 return img;
622 }
623
624 //-----------------------------------------------------------------------------
625 // load icon (and image) data of all frames into cache
loadAllIconsAndPutInCache(bool cacheImagesAsWell)626 void TXshSimpleLevel::loadAllIconsAndPutInCache(bool cacheImagesAsWell) {
627 if (m_type != TZP_XSHLEVEL) return;
628
629 std::vector<TFrameId> fids;
630 getFids(fids);
631
632 std::vector<std::string> iconIds;
633
634 for (int i = 0; i < (int)fids.size(); i++) {
635 iconIds.push_back(getIconId(fids[i]));
636 }
637
638 ImageManager::instance()->loadAllTlvIconsAndPutInCache(this, fids, iconIds,
639 cacheImagesAsWell);
640 }
641
642 //-----------------------------------------------------------------------------
643
getFrameToCleanup(const TFrameId & fid) const644 TRasterImageP TXshSimpleLevel::getFrameToCleanup(const TFrameId &fid) const {
645 assert(m_type != UNKNOWN_XSHLEVEL);
646
647 FramesSet::const_iterator ft = m_frames.find(fid);
648 if (ft == m_frames.end()) return TImageP();
649
650 bool flag = (m_scannedPath != TFilePath());
651 std::string imageId = getImageId(fid, flag ? Scanned : 0);
652
653 ImageLoader::BuildExtData extData(this, fid, 1);
654 TRasterImageP img = ImageManager::instance()->getImage(
655 imageId, ImageManager::dontPutInCache, &extData);
656 if (!img) return img;
657
658 double x_dpi, y_dpi;
659 img->getDpi(x_dpi, y_dpi);
660 if (!x_dpi && !y_dpi) {
661 TPointD dpi = m_properties->getDpi();
662 img->setDpi(dpi.x, dpi.y);
663 }
664
665 return img;
666 }
667
668 //-----------------------------------------------------------------------------
669
getFullsampledFrame(const TFrameId & fid,UCHAR imFlags) const670 TImageP TXshSimpleLevel::getFullsampledFrame(const TFrameId &fid,
671 UCHAR imFlags) const {
672 assert(m_type != UNKNOWN_XSHLEVEL);
673
674 FramesSet::const_iterator it = m_frames.find(fid);
675 if (it == m_frames.end()) return TRasterImageP();
676
677 std::string imageId = getImageId(fid);
678
679 ImageLoader::BuildExtData extData(this, fid, 1);
680 TImageP img = ImageManager::instance()->getImage(imageId, imFlags, &extData);
681
682 if (imFlags & ImageManager::toBeModified) {
683 // The image will be modified. Perform any related invalidation.
684 texture_utils::invalidateTexture(
685 this, fid); // We must rebuild associated textures
686 }
687
688 return img;
689 }
690
691 //-----------------------------------------------------------------------------
692
getIconId(const TFrameId & fid,int frameStatus) const693 std::string TXshSimpleLevel::getIconId(const TFrameId &fid,
694 int frameStatus) const {
695 return "icon:" + getImageId(fid, frameStatus);
696 }
697
698 //-----------------------------------------------------------------------------
699
getIconId(const TFrameId & fid,const TDimension & size) const700 std::string TXshSimpleLevel::getIconId(const TFrameId &fid,
701 const TDimension &size) const {
702 return getImageId(fid) + ":" + std::to_string(size.lx) + "x" +
703 std::to_string(size.ly);
704 }
705
706 //-----------------------------------------------------------------------------
707
708 namespace {
709
getAffine(const TDimension & srcSize,const TDimension & dstSize)710 TAffine getAffine(const TDimension &srcSize, const TDimension &dstSize) {
711 double scx = 1 * dstSize.lx / (double)srcSize.lx;
712 double scy = 1 * dstSize.ly / (double)srcSize.ly;
713 double sc = std::min(scx, scy);
714 double dx = (dstSize.lx - srcSize.lx * sc) * 0.5;
715 double dy = (dstSize.ly - srcSize.ly * sc) * 0.5;
716 return TScale(sc) *
717 TTranslation(0.5 * TPointD(srcSize.lx, srcSize.ly) + TPointD(dx, dy));
718 }
719
720 //-----------------------------------------------------------------------------
721
722 /*!Costruisce l'icona di dimesione \b size dell'immagine \b img.*/
buildIcon(const TImageP & img,const TDimension & size)723 TImageP buildIcon(const TImageP &img, const TDimension &size) {
724 TRaster32P raster(size);
725 if (TVectorImageP vi = img) {
726 TOfflineGL *glContext = new TOfflineGL(size);
727 // TDimension cameraSize(768, 576);
728 TDimension cameraSize(1920, 1080);
729 TPalette *vPalette = img->getPalette();
730 assert(vPalette);
731 const TVectorRenderData rd(getAffine(cameraSize, size), TRect(), vPalette,
732 0, false);
733 glContext->clear(TPixel32::White);
734 glContext->draw(vi, rd);
735 raster->copy(glContext->getRaster());
736 delete glContext;
737 } else if (TToonzImageP ti = img) {
738 raster->fill(TPixel32(255, 255, 255, 255));
739 TRasterCM32P rasCM32 = ti->getRaster();
740 TRect bbox;
741 bbox = ti->getSavebox();
742 if (!bbox.isEmpty()) {
743 rasCM32 = rasCM32->extractT(bbox);
744 double sx = raster->getLx() / (double)rasCM32->getLx();
745 double sy = raster->getLy() / (double)rasCM32->getLy();
746 double sc = std::min(sx, sy);
747 TAffine aff =
748 TScale(sc).place(rasCM32->getCenterD(), raster->getCenterD());
749 TRop::resample(raster, rasCM32, ti->getPalette(), aff);
750 raster->lock();
751 for (int y = 0; y < raster->getLy(); y++) {
752 TPixel32 *pix = raster->pixels(y);
753 TPixel32 *endPix = pix + raster->getLx();
754 while (pix < endPix) {
755 *pix = overPix(TPixel32::White, *pix);
756 pix++;
757 }
758 }
759 raster->unlock();
760 }
761 } else {
762 TRasterImageP ri = img;
763 if (ri) {
764 ri->makeIcon(raster);
765 TRop::addBackground(raster, TPixel32::White);
766 } else
767 raster->fill(TPixel32(127, 50, 20));
768 }
769
770 return TRasterImageP(raster);
771 }
772
773 } // anonymous namespace
774
775 //-----------------------------------------------------------------------------
776
setFrame(const TFrameId & fid,const TImageP & img)777 void TXshSimpleLevel::setFrame(const TFrameId &fid, const TImageP &img) {
778 assert(m_type != UNKNOWN_XSHLEVEL);
779
780 if (img) img->setPalette(getPalette());
781
782 m_frames.insert(fid);
783
784 TFilePath path = m_path;
785
786 int frameStatus = getFrameStatus(fid);
787 static const int SCANNED_OR_CLEANUPPED = (Scanned | Cleanupped);
788
789 if ((frameStatus & SCANNED_OR_CLEANUPPED) == Scanned) path = m_scannedPath;
790
791 // Deal with the ImageManger: ensure the identifiers are bound, and the
792 // associated image is either modified to img or (if !img) invalidated.
793 const std::string &imageId = getImageId(fid);
794
795 if (!ImageManager::instance()->isBound(imageId)) {
796 const TFilePath &decodedPath = getScene()->decodeFilePath(path);
797 ImageManager::instance()->bind(imageId, new ImageLoader(decodedPath, fid));
798 }
799
800 ImageManager::instance()->setImage(imageId, img); // Invalidates if !img
801
802 if (frameStatus == Normal) {
803 // Only a normal frame can have these. Justified since:
804 // PLIs have nothing to share with cleanup stuff
805
806 if (m_type == PLI_XSHLEVEL) {
807 const std::string &imageId2 = rasterized(imageId);
808 if (!ImageManager::instance()->isBound(imageId2))
809 ImageManager::instance()->bind(imageId2, new ImageRasterizer);
810 else
811 ImageManager::instance()->invalidate(imageId2);
812 }
813
814 if (m_type == OVL_XSHLEVEL || m_type == TZI_XSHLEVEL) {
815 const std::string &imageId2 = filled(imageId);
816 if (!ImageManager::instance()->isBound(imageId2))
817 ImageManager::instance()->bind(imageId2, new ImageFiller);
818 else
819 ImageManager::instance()->invalidate(imageId2);
820 }
821 }
822 }
823
824 //-----------------------------------------------------------------------------
825
eraseFrame(const TFrameId & fid)826 void TXshSimpleLevel::eraseFrame(const TFrameId &fid) {
827 FramesSet::iterator ft = m_frames.find(fid);
828 if (ft == m_frames.end()) return;
829
830 // Erase the corresponding entry in the renumber table
831 std::map<TFrameId, TFrameId>::iterator rt, rEnd(m_renumberTable.end());
832 for (rt = m_renumberTable.begin(); rt != rEnd; ++rt) {
833 if (rt->second == fid) {
834 m_renumberTable.erase(rt->first);
835 break;
836 }
837 }
838
839 m_frames.erase(ft);
840 getHookSet()->eraseFrame(fid);
841
842 ImageManager *im = ImageManager::instance();
843 TImageCache *ic = TImageCache::instance();
844 {
845 im->unbind(getImageId(fid, Normal));
846 im->unbind(getImageId(fid, Scanned));
847 im->unbind(getImageId(fid, CleanupPreview));
848 // remove icon cache as well
849 ic->remove(getIconId(fid, Normal));
850 ic->remove(getIconId(fid, Scanned));
851 ic->remove(getIconId(fid, CleanupPreview));
852
853 if (m_type == PLI_XSHLEVEL) im->unbind(rasterized(getImageId(fid)));
854
855 if (m_type == OVL_XSHLEVEL || m_type == TZI_XSHLEVEL)
856 im->unbind(filled(getImageId(fid)));
857
858 texture_utils::invalidateTexture(this, fid);
859 }
860 }
861
862 //-----------------------------------------------------------------------------
863
clearFrames()864 void TXshSimpleLevel::clearFrames() {
865 ImageManager *im = ImageManager::instance();
866 TImageCache *ic = TImageCache::instance();
867 // Unbind frames
868 FramesSet::iterator ft, fEnd = m_frames.end();
869 for (ft = m_frames.begin(); ft != fEnd; ++ft) {
870 im->unbind(getImageId(*ft, Scanned));
871 im->unbind(getImageId(*ft, Cleanupped));
872 im->unbind(getImageId(*ft, CleanupPreview));
873 // remove icon cache as well
874 ic->remove(getIconId(*ft, Normal));
875 ic->remove(getIconId(*ft, Scanned));
876 ic->remove(getIconId(*ft, CleanupPreview));
877
878 if (m_type == PLI_XSHLEVEL) im->unbind(rasterized(getImageId(*ft)));
879
880 if (m_type == OVL_XSHLEVEL || m_type == TZI_XSHLEVEL)
881 im->unbind(filled(getImageId(*ft)));
882
883 texture_utils::invalidateTexture(this, *ft);
884 }
885
886 // Clear level
887 m_frames.clear();
888 m_editableRange.clear();
889 m_editableRangeUserInfo.clear();
890 m_renumberTable.clear();
891 m_framesStatus.clear();
892 }
893
894 //-----------------------------------------------------------------------------
895
loadData(TIStream & is)896 void TXshSimpleLevel::loadData(TIStream &is) {
897 std::string tagName;
898 bool flag = false;
899
900 int type = UNKNOWN_XSHLEVEL;
901
902 for (;;) {
903 if (is.matchTag(tagName)) {
904 if (tagName == "path") {
905 is >> m_path;
906 is.matchEndTag();
907 } else if (tagName == "scannedPath") {
908 is >> m_scannedPath;
909 is.matchEndTag();
910 } else if (tagName == "info") {
911 std::string v;
912 double xdpi = 0, ydpi = 0;
913 int subsampling = 1;
914 int doPremultiply = 0;
915 int whiteTransp = 0;
916 int antialiasSoftness = 0;
917 int isStopMotionLevel = 0;
918 LevelProperties::DpiPolicy dpiPolicy = LevelProperties::DP_ImageDpi;
919 if (is.getTagParam("dpix", v)) xdpi = std::stod(v);
920 if (is.getTagParam("dpiy", v)) ydpi = std::stod(v);
921 if (xdpi != 0 && ydpi != 0) dpiPolicy = LevelProperties::DP_CustomDpi;
922 std::string dpiType = is.getTagAttribute("dpiType");
923 if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi;
924 if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL;
925 if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v);
926 if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v);
927 if (is.getTagParam("antialias", v)) antialiasSoftness = std::stoi(v);
928 if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v);
929 if (is.getTagParam("isStopMotionLevel", v))
930 isStopMotionLevel = std::stoi(v);
931
932 m_properties->setDpiPolicy(dpiPolicy);
933 m_properties->setDpi(TPointD(xdpi, ydpi));
934 m_properties->setSubsampling(subsampling);
935 m_properties->setDoPremultiply(doPremultiply);
936 m_properties->setDoAntialias(antialiasSoftness);
937 m_properties->setWhiteTransp(whiteTransp);
938 m_properties->setIsStopMotion(isStopMotionLevel);
939 if (isStopMotionLevel == 1) setIsReadOnly(true);
940 } else
941 throw TException("unexpected tag " + tagName);
942 } else {
943 if (flag) break; // ci puo' essere un solo nome
944 flag = true;
945 std::wstring token;
946 is >> token;
947 if (token == L"__empty") {
948 // empty = true;
949 is >> token;
950 }
951
952 if (token == L"_raster") // obsoleto (Tab2.2)
953 {
954 double xdpi = 1, ydpi = 1;
955 is >> xdpi >> ydpi >> m_name;
956 setName(m_name);
957 type = OVL_XSHLEVEL;
958 m_properties->setDpi(TPointD(xdpi, ydpi));
959 setType(type);
960 setPath(
961 TFilePath("+drawings/") + (getName() + L"." + ::to_wstring("bmp")),
962 true);
963 } else if (token == L"__raster") // obsoleto (Tab2.2)
964 {
965 double xdpi = 1, ydpi = 1;
966 std::string extension;
967 is >> xdpi >> ydpi >> m_name >> extension;
968 setName(m_name);
969 type = OVL_XSHLEVEL;
970 m_properties->setDpi(TPointD(xdpi, ydpi));
971 setType(type);
972 setPath(TFilePath("+drawings/") +
973 (getName() + L"." + ::to_wstring(extension)),
974 true);
975 } else {
976 m_name = token;
977 setName(m_name);
978 }
979 }
980 }
981 if (type == UNKNOWN_XSHLEVEL) {
982 std::string ext = m_path.getType();
983 if (ext == "pli" || ext == "svg")
984 type = PLI_XSHLEVEL;
985 else if (ext == "tlv" || ext == "tzu" || ext == "tzp" || ext == "tzl")
986 type = TZP_XSHLEVEL;
987 else if (ext == "tzi")
988 type = TZI_XSHLEVEL;
989 else if (ext == "mesh")
990 type = MESH_XSHLEVEL;
991 else
992 type = OVL_XSHLEVEL;
993 }
994 setType(type);
995 }
996
997 //-----------------------------------------------------------------------------
998
999 namespace {
1000 class LoadingLevelRange {
1001 public:
1002 TFrameId m_fromFid, m_toFid;
LoadingLevelRange()1003 LoadingLevelRange() : m_fromFid(1), m_toFid(0) {}
1004
match(const TFrameId & fid) const1005 bool match(const TFrameId &fid) const {
1006 /*-- ↓SubSequent範囲内にある条件 ↓全部ロードする場合 --*/
1007 return ((m_fromFid <= fid && fid <= m_toFid) || m_fromFid > m_toFid);
1008 }
isEnabled() const1009 bool isEnabled() const { return m_fromFid <= m_toFid; }
reset()1010 void reset() {
1011 m_fromFid = TFrameId(1);
1012 m_toFid = TFrameId(0);
1013 }
1014
1015 } loadingLevelRange;
1016
1017 //-----------------------------------------------------------------------------
1018 } // namespace
1019 //-----------------------------------------------------------------------------
1020
setLoadingLevelRange(const TFrameId & fromFid,const TFrameId & toFid)1021 void setLoadingLevelRange(const TFrameId &fromFid, const TFrameId &toFid) {
1022 loadingLevelRange.m_fromFid = fromFid;
1023 loadingLevelRange.m_toFid = toFid;
1024 }
1025
getLoadingLevelRange(TFrameId & fromFid,TFrameId & toFid)1026 void getLoadingLevelRange(TFrameId &fromFid, TFrameId &toFid) {
1027 fromFid = loadingLevelRange.m_fromFid;
1028 toFid = loadingLevelRange.m_toFid;
1029 }
1030
getLevelPathAndSetNameWithPsdLevelName(TXshSimpleLevel * xshLevel)1031 static TFilePath getLevelPathAndSetNameWithPsdLevelName(
1032 TXshSimpleLevel *xshLevel) {
1033 TFilePath retfp = xshLevel->getPath();
1034
1035 QString name = QString::fromStdWString(retfp.getWideName());
1036 bool removeFileName = name.contains("##");
1037 if (removeFileName) {
1038 retfp = TFilePath(
1039 QString::fromStdWString(retfp.getWideString()).replace("##", "#"));
1040 }
1041 QStringList list = name.split("#", QString::SkipEmptyParts);
1042
1043 if (list.size() >= 2 && list.at(1) != "frames") {
1044 bool hasLayerId;
1045 int layid = list.at(1).toInt(&hasLayerId);
1046 QTextCodec *layerNameCodec = QTextCodec::codecForName(
1047 Preferences::instance()->getLayerNameEncoding().c_str());
1048
1049 if (hasLayerId) {
1050 // An explicit photoshop layer id must be converted to the associated
1051 // level name
1052 TPSDParser psdparser(xshLevel->getScene()->decodeFilePath(retfp));
1053 std::string levelName = psdparser.getLevelNameWithCounter(
1054 layid); // o_o what about UNICODE names??
1055
1056 list[1] = layerNameCodec->toUnicode(levelName.c_str());
1057 std::wstring wLevelName = list.join("#").toStdWString();
1058 retfp = retfp.withName(wLevelName);
1059
1060 if (removeFileName) wLevelName = list[1].toStdWString();
1061
1062 TLevelSet *levelSet = xshLevel->getScene()->getLevelSet();
1063 if (levelSet && levelSet->hasLevel(
1064 wLevelName)) // levelSet should be asserted instead
1065 levelSet->renameLevel(xshLevel, wLevelName);
1066
1067 xshLevel->setName(wLevelName);
1068 }
1069 }
1070
1071 return retfp;
1072 }
1073 //-----------------------------------------------------------------------------
1074
1075 // Nota: load() NON fa clearFrames(). si limita ad aggiungere le informazioni
1076 // relative ai frames su disco
load()1077 void TXshSimpleLevel::load() {
1078 getProperties()->setCreator("");
1079 QString creator;
1080
1081 assert(getScene());
1082 if (!getScene()) return;
1083
1084 m_isSubsequence = loadingLevelRange.isEnabled();
1085
1086 TFilePath checkpath = getScene()->decodeFilePath(m_path);
1087 std::string type = checkpath.getType();
1088
1089 if (m_scannedPath != TFilePath()) {
1090 getProperties()->setDirtyFlag(
1091 false); // Level is now supposedly loaded from disk
1092
1093 static const int ScannedCleanuppedMask = Scanned | Cleanupped;
1094 TFilePath path = getScene()->decodeFilePath(m_scannedPath);
1095 if (TSystem::doesExistFileOrLevel(path)) {
1096 TLevelReaderP lr(path);
1097 assert(lr);
1098 TLevelP level = lr->loadInfo();
1099 if (!checkCreatorString(creator = lr->getCreator()))
1100 getProperties()->setIsForbidden(true);
1101 else
1102 for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
1103 TFrameId fid = it->first;
1104 if (!loadingLevelRange.match(fid)) continue;
1105 setFrameStatus(
1106 fid, (getFrameStatus(fid) & ~ScannedCleanuppedMask) | Scanned);
1107 setFrame(fid, TImageP());
1108 }
1109 }
1110
1111 path = getScene()->decodeFilePath(m_path);
1112 if (TSystem::doesExistFileOrLevel(path)) {
1113 TLevelReaderP lr(path);
1114 assert(lr);
1115 TLevelP level = lr->loadInfo();
1116 if (getType() & FULLCOLOR_TYPE)
1117 setPalette(FullColorPalette::instance()->getPalette(getScene()));
1118 else
1119 setPalette(level->getPalette());
1120 if (!checkCreatorString(creator = lr->getCreator()))
1121 getProperties()->setIsForbidden(true);
1122 else
1123 for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
1124 TFrameId fid = it->first;
1125 if (!loadingLevelRange.match(fid)) continue;
1126 setFrameStatus(fid, getFrameStatus(fid) | Cleanupped);
1127 setFrame(fid, TImageP());
1128 }
1129 setContentHistory(
1130 lr->getContentHistory() ? lr->getContentHistory()->clone() : 0);
1131 }
1132
1133 } else {
1134 // Not a scan + cleanup level
1135
1136 if (m_path.getType() == "psd" &&
1137 this->getScene()->getVersionNumber().first < 71)
1138 m_path = getLevelPathAndSetNameWithPsdLevelName(this);
1139
1140 TFilePath path = getScene()->decodeFilePath(m_path);
1141
1142 getProperties()->setDirtyFlag(
1143 false); // Level is now supposedly loaded from disk
1144
1145 TLevelReaderP lr(path); // May throw
1146 assert(lr);
1147
1148 TLevelP level = lr->loadInfo();
1149 if (level->getFrameCount() > 0) {
1150 const TImageInfo *info = lr->getImageInfo(level->begin()->first);
1151
1152 if (info && info->m_samplePerPixel >= 5) {
1153 QString msg = QString(
1154 "Failed to open %1.\nSamples per pixel is more than "
1155 "4. It may contain more than one alpha channel.")
1156 .arg(QString::fromStdWString(m_path.getWideString()));
1157 QMessageBox::warning(0, "Image format not supported", msg);
1158 return;
1159 }
1160
1161 if (info) set16BitChannelLevel(info->m_bitsPerSample == 16);
1162 }
1163 if ((getType() & FULLCOLOR_TYPE) && !is16BitChannelLevel())
1164 setPalette(FullColorPalette::instance()->getPalette(getScene()));
1165 else
1166 setPalette(level->getPalette());
1167
1168 if (!checkCreatorString(creator = lr->getCreator()))
1169 getProperties()->setIsForbidden(true);
1170 else
1171 for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
1172 m_renumberTable[it->first] = it->first; // Voglio che la tabella
1173 // contenga anche i frame che
1174 // non vengono caricati
1175 if (!loadingLevelRange.match(it->first)) continue;
1176 setFrame(it->first, TImageP());
1177 }
1178
1179 setContentHistory(lr->getContentHistory() ? lr->getContentHistory()->clone()
1180 : 0);
1181 }
1182 getProperties()->setCreator(creator.toStdString());
1183
1184 loadingLevelRange.reset();
1185 if (getType() != PLI_XSHLEVEL) {
1186 if (m_properties->getImageDpi() == TPointD() && !m_frames.empty()) {
1187 TDimension imageRes(0, 0);
1188 TPointD imageDpi;
1189
1190 const TFrameId &firstFid = getFirstFid();
1191 std::string imageId = getImageId(firstFid);
1192
1193 const TImageInfo *imageInfo =
1194 ImageManager::instance()->getInfo(imageId, ImageManager::none, 0);
1195 if (imageInfo) {
1196 imageRes.lx = imageInfo->m_lx;
1197 imageRes.ly = imageInfo->m_ly;
1198 imageDpi.x = imageInfo->m_dpix;
1199 imageDpi.y = imageInfo->m_dpiy;
1200 m_properties->setImageDpi(imageDpi);
1201 m_properties->setImageRes(imageRes);
1202 m_properties->setBpp(imageInfo->m_bitsPerSample *
1203 imageInfo->m_samplePerPixel);
1204 }
1205 }
1206 setRenumberTable();
1207 }
1208
1209 if (getPalette() && StudioPalette::isEnabled())
1210 StudioPalette::instance()->updateLinkedColors(getPalette());
1211
1212 TFilePath refImgName;
1213 if (m_palette) {
1214 refImgName = m_palette->getRefImgPath();
1215 TFilePath refImgPath = refImgName;
1216 if (refImgName != TFilePath() && TFileStatus(refImgPath).doesExist()) {
1217 TLevelReaderP lr(refImgPath);
1218 if (lr) {
1219 TLevelP level = lr->loadInfo();
1220 if (level->getFrameCount() > 0) {
1221 TImageP img = lr->getFrameReader(level->begin()->first)->load();
1222 if (img && getPalette()) {
1223 img->setPalette(0);
1224 getPalette()->setRefImg(img);
1225 std::vector<TFrameId> fids = getPalette()->getRefLevelFids();
1226 // in case the fids are specified by user
1227 if (fids.size() > 0) {
1228 // check existence of each fid
1229 auto itr = fids.begin();
1230 while (itr != fids.end()) {
1231 bool found = false;
1232 for (TLevel::Iterator it = level->begin(); it != level->end();
1233 ++it) {
1234 if (itr->getNumber() == it->first.getNumber()) {
1235 found = true;
1236 break;
1237 }
1238 }
1239 if (!found) // remove the fid if it does not exist in the level
1240 itr = fids.erase(itr);
1241 else
1242 itr++;
1243 }
1244 }
1245 // in case the fids are not specified, or all specified fids are
1246 // absent
1247 if (fids.size() == 0) {
1248 for (TLevel::Iterator it = level->begin(); it != level->end();
1249 ++it)
1250 fids.push_back(it->first);
1251 getPalette()->setRefLevelFids(fids, false);
1252 } else if (fids.size() != getPalette()->getRefLevelFids().size())
1253 getPalette()->setRefLevelFids(fids, true);
1254 }
1255 }
1256 }
1257 }
1258 }
1259
1260 // Load hooks
1261 HookSet *hookSet = getHookSet();
1262 hookSet->clearHooks();
1263
1264 const TFilePath &hookFile =
1265 TXshSimpleLevel::getExistingHookFile(getScene()->decodeFilePath(m_path));
1266
1267 if (!hookFile.isEmpty()) {
1268 TIStream is(hookFile);
1269 std::string tagName;
1270 try {
1271 if (is.matchTag(tagName) && tagName == "hooks") hookSet->loadData(is);
1272 } catch (...) {
1273 }
1274 }
1275 updateReadOnly();
1276 }
1277
1278 //-----------------------------------------------------------------------------
1279
load(const std::vector<TFrameId> & fIds)1280 void TXshSimpleLevel::load(const std::vector<TFrameId> &fIds) {
1281 getProperties()->setCreator("");
1282 QString creator;
1283 assert(getScene());
1284 getProperties()->setDirtyFlag(false);
1285
1286 m_isSubsequence = loadingLevelRange.isEnabled();
1287
1288 // non e' un livello scan+cleanup
1289 TFilePath path = getScene()->decodeFilePath(m_path);
1290
1291 TLevelReaderP lr(path);
1292 assert(lr);
1293
1294 if (!checkCreatorString(creator = lr->getCreator()))
1295 getProperties()->setIsForbidden(true);
1296 else {
1297 if (fIds.size() != 0) {
1298 for (int i = 0; i < (int)fIds.size(); i++) {
1299 m_renumberTable[fIds[i]] = fIds[i];
1300 if (!loadingLevelRange.match(fIds[i])) continue;
1301 setFrame(fIds[i], TImageP());
1302 }
1303 const TImageInfo *info = lr->getImageInfo(fIds[0]);
1304 if (info) set16BitChannelLevel(info->m_bitsPerSample == 16);
1305 } else {
1306 TLevelP level = lr->loadInfo();
1307 for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
1308 m_renumberTable[it->first] = it->first;
1309 if (!loadingLevelRange.match(it->first)) continue;
1310 setFrame(it->first, TImageP());
1311 }
1312 const TImageInfo *info = lr->getImageInfo(level->begin()->first);
1313 if (info) set16BitChannelLevel(info->m_bitsPerSample == 16);
1314 }
1315
1316 if ((getType() & FULLCOLOR_TYPE) && !is16BitChannelLevel())
1317 setPalette(FullColorPalette::instance()->getPalette(getScene()));
1318 }
1319
1320 setContentHistory(lr->getContentHistory() ? lr->getContentHistory()->clone()
1321 : 0);
1322
1323 getProperties()->setCreator(creator.toStdString());
1324
1325 loadingLevelRange.reset();
1326
1327 if (getType() != PLI_XSHLEVEL) {
1328 if (m_properties->getImageDpi() == TPointD() && !m_frames.empty()) {
1329 TDimension imageRes(0, 0);
1330 TPointD imageDpi;
1331 std::string imageId = getImageId(getFirstFid());
1332 const TImageInfo *imageInfo =
1333 ImageManager::instance()->getInfo(imageId, ImageManager::none, 0);
1334 if (imageInfo) {
1335 imageRes.lx = imageInfo->m_lx;
1336 imageRes.ly = imageInfo->m_ly;
1337 imageDpi.x = imageInfo->m_dpix;
1338 imageDpi.y = imageInfo->m_dpiy;
1339 m_properties->setImageDpi(imageDpi);
1340 m_properties->setImageRes(imageRes);
1341 }
1342 }
1343 setRenumberTable();
1344 }
1345 }
1346
1347 //-----------------------------------------------------------------------------
1348
updateReadOnly()1349 void TXshSimpleLevel::updateReadOnly() {
1350 TFilePath path = getScene()->decodeFilePath(m_path);
1351 m_isReadOnly = isAreadOnlyLevel(path);
1352 }
1353
1354 //-----------------------------------------------------------------------------
1355
saveData(TOStream & os)1356 void TXshSimpleLevel::saveData(TOStream &os) {
1357 os << m_name;
1358
1359 std::map<std::string, std::string> attr;
1360 if (getProperties()->getDpiPolicy() == LevelProperties::DP_CustomDpi) {
1361 TPointD dpi = getProperties()->getDpi();
1362 if (dpi.x != 0 && dpi.y != 0) {
1363 attr["dpix"] = std::to_string(dpi.x);
1364 attr["dpiy"] = std::to_string(dpi.y);
1365 }
1366 } else {
1367 attr["dpiType"] = "image";
1368 }
1369
1370 if (getProperties()->getSubsampling() != 1) {
1371 attr["subsampling"] = std::to_string(getProperties()->getSubsampling());
1372 }
1373 if (getProperties()->antialiasSoftness() > 0) {
1374 attr["antialias"] = std::to_string(getProperties()->antialiasSoftness());
1375 }
1376 if (getProperties()->doPremultiply()) {
1377 attr["premultiply"] = std::to_string(getProperties()->doPremultiply());
1378 } else if (getProperties()->whiteTransp()) {
1379 attr["whiteTransp"] = std::to_string(getProperties()->whiteTransp());
1380 } else if (getProperties()->isStopMotionLevel()) {
1381 attr["isStopMotionLevel"] =
1382 std::to_string(getProperties()->isStopMotionLevel());
1383 }
1384
1385 if (m_type == TZI_XSHLEVEL) attr["type"] = "s";
1386
1387 os.openCloseChild("info", attr);
1388
1389 os.child("path") << m_path; // fp;
1390 if (m_scannedPath != TFilePath())
1391 os.child("scannedPath") << m_scannedPath; // fp;
1392 }
1393
1394 //-----------------------------------------------------------------------------
1395
save()1396 void TXshSimpleLevel::save() {
1397 assert(getScene());
1398 TFilePath path = getScene()->decodeFilePath(m_path);
1399 TSystem::outputDebug("save() : " + ::to_string(m_path) + " = " +
1400 ::to_string(path) + "\n");
1401
1402 if (getProperties()->getDirtyFlag() == false &&
1403 getPalette()->getDirtyFlag() == false &&
1404 TSystem::doesExistFileOrLevel(path))
1405 return;
1406
1407 if (!TFileStatus(path.getParentDir()).doesExist()) {
1408 try {
1409 TSystem::mkDir(path.getParentDir());
1410 } catch (...) {
1411 }
1412 }
1413 save(path);
1414 }
1415
1416 //-----------------------------------------------------------------------------
1417
saveBackup(TFilePath path)1418 static void saveBackup(TFilePath path) {
1419 // The additional .bak extension keeps it from being detected as a sequence.
1420 // If the original path is a sequence, find the individual files and back it
1421 // up individually
1422 if (path.isLevelName()) {
1423 TFilePathSet files =
1424 TSystem::readDirectory(path.getParentDir(), false, true);
1425 for (TFilePathSet::iterator file = files.begin(); file != files.end();
1426 file++) {
1427 if (file->getLevelName() == path.getLevelName()) saveBackup(*file);
1428 }
1429 return;
1430 }
1431
1432 int totalBackups = Preferences::instance()->getBackupKeepCount();
1433 totalBackups -= 1;
1434 TFilePath backup = path.withType(path.getType() + ".bak");
1435 TFilePath prevBackup =
1436 path.withType(path.getType() + ".bak" + std::to_string(totalBackups));
1437 while (--totalBackups >= 0) {
1438 std::string bakExt =
1439 ".bak" + (totalBackups > 0 ? std::to_string(totalBackups) : "");
1440 backup = path.withType(path.getType() + bakExt);
1441 if (TSystem::doesExistFileOrLevel(backup)) {
1442 try {
1443 TSystem::copyFileOrLevel_throw(prevBackup, backup);
1444 } catch (...) {
1445 }
1446 }
1447 prevBackup = backup;
1448 }
1449
1450 try {
1451 if (TSystem::doesExistFileOrLevel(backup))
1452 TSystem::removeFileOrLevel_throw(backup);
1453 TSystem::copyFileOrLevel_throw(backup, path);
1454 } catch (...) {
1455 }
1456 }
1457
1458 //-----------------------------------------------------------------------------
1459
save(const TFilePath & fp,const TFilePath & oldFp,bool overwritePalette)1460 void TXshSimpleLevel::save(const TFilePath &fp, const TFilePath &oldFp,
1461 bool overwritePalette) {
1462 TFilePath dOldPath =
1463 (!oldFp.isEmpty()) ? oldFp : getScene()->decodeFilePath(m_path);
1464
1465 TFilePath dDstPath = getScene()->decodeFilePath(fp);
1466 if (!TSystem::touchParentDir(dDstPath))
1467 throw TSystemException(
1468 dDstPath,
1469 "The level cannot be saved: failed to access the target folder.");
1470
1471 // backup
1472 if (Preferences::instance()->isBackupEnabled() && dOldPath == dDstPath &&
1473 TSystem::doesExistFileOrLevel(dDstPath) &&
1474 !getProperties()->isStopMotionLevel())
1475 saveBackup(dDstPath);
1476
1477 if (isAreadOnlyLevel(dDstPath)) {
1478 if (m_editableRange.empty() &&
1479 !m_temporaryHookMerged) // file internally locked
1480 throw TSystemException(
1481 dDstPath, "The level cannot be saved: it is a read only level.");
1482 else if (getType() != OVL_XSHLEVEL) {
1483 // file partially unlocked
1484 std::wstring fileName = getEditableFileName();
1485 assert(!fileName.empty());
1486
1487 TFilePath app = dDstPath.withName(fileName).withType(dDstPath.getType());
1488
1489 // removes old files
1490 if (TSystem::doesExistFileOrLevel(app)) TSystem::removeFileOrLevel(app);
1491
1492 TFilePathSet oldFilePaths;
1493 getFiles(app, oldFilePaths);
1494
1495 TFilePathSet::iterator it;
1496 for (it = oldFilePaths.begin(); it != oldFilePaths.end(); ++it) {
1497 if (TSystem::doesExistFileOrLevel(*it)) TSystem::removeFileOrLevel(*it);
1498 }
1499
1500 // save new files
1501 TXshSimpleLevel *sl = new TXshSimpleLevel;
1502 sl->setScene(getScene());
1503 sl->setPalette(getPalette());
1504 sl->setPath(getScene()->codeFilePath(app));
1505 sl->setType(getType());
1506 sl->setDirtyFlag(getDirtyFlag());
1507 sl->addRef(); // Needed so levelUpdater doesn't destroy it right away
1508 // when its done writing
1509
1510 std::set<TFrameId>::iterator eft, efEnd = m_editableRange.end();
1511 for (eft = m_editableRange.begin(); eft != efEnd; ++eft) {
1512 const TFrameId &fid = *eft;
1513 sl->setFrame(fid, getFrame(fid, false));
1514 }
1515
1516 // Copy hooks
1517 HookSet *hookSet = sl->getHookSet();
1518 *hookSet = *getHookSet();
1519
1520 FramesSet::iterator ft, fEnd = m_frames.end();
1521 for (ft = m_frames.begin(); ft != fEnd; ++ft) {
1522 const TFrameId &fid = *ft;
1523
1524 if (m_editableRange.find(fid) == m_editableRange.end())
1525 hookSet->eraseFrame(fid);
1526 }
1527
1528 sl->setRenumberTable();
1529
1530 // Copy mesh level
1531 sl->save(app);
1532
1533 #ifdef _WIN32
1534
1535 // hides files
1536 oldFilePaths.clear();
1537
1538 if (TSystem::doesExistFileOrLevel(app)) TSystem::hideFileOrLevel(app);
1539
1540 getFiles(app, oldFilePaths);
1541
1542 for (it = oldFilePaths.begin(); it != oldFilePaths.end(); ++it) {
1543 if (TSystem::doesExistFileOrLevel(*it)) TSystem::hideFileOrLevel(*it);
1544 }
1545 #endif
1546 return;
1547 }
1548 }
1549
1550 if (dOldPath != dDstPath && m_path != TFilePath()) {
1551 const TFilePath &dSrcPath = dOldPath;
1552
1553 try {
1554 if (TSystem::doesExistFileOrLevel(dSrcPath)) {
1555 if (TSystem::doesExistFileOrLevel(dDstPath))
1556 TSystem::removeFileOrLevel(dDstPath);
1557
1558 copyFiles(dDstPath, dSrcPath);
1559 }
1560 } catch (...) {
1561 }
1562 }
1563 // when saving the level palette with global name
1564 if (overwritePalette && getType() == TZP_XSHLEVEL && getPalette() &&
1565 getPalette()->getGlobalName() != L"") {
1566 overwritePalette = false;
1567 TFilePath palettePath = dDstPath.withNoFrame().withType("tpl");
1568 StudioPalette::instance()->save(palettePath, getPalette());
1569 getPalette()->setDirtyFlag(false);
1570 }
1571
1572 saveSimpleLevel(dDstPath, overwritePalette);
1573 }
1574
1575 //-----------------------------------------------------------------------------
1576
saveSimpleLevel(const TFilePath & decodedFp,bool overwritePalette)1577 void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
1578 bool overwritePalette) {
1579 /* Precondition: Destination level with path decodedFp is supposed to already
1580 store those image frames not tagged as 'modified' in this level
1581 instance. */
1582
1583 TFilePath oldPath = m_path;
1584 TFilePath dOldPath = getScene()->decodeFilePath(oldPath);
1585
1586 // Substitute m_path with decodedFp until the function quits.
1587 struct CopyOnExit {
1588 TFilePath &m_dstPath, &m_srcPath;
1589 ~CopyOnExit() { m_dstPath = m_srcPath; }
1590 } copyOnExit = {m_path = decodedFp,
1591 oldPath}; // m_path substituted here until function quits
1592
1593 bool savingOriginal = (decodedFp == dOldPath), paletteNotSaved = false;
1594
1595 int imFlags = savingOriginal
1596 ? ImageManager::dontPutInCache | ImageManager::toBeSaved
1597 : ImageManager::dontPutInCache;
1598
1599 std::vector<TFrameId> fids;
1600 getFids(fids);
1601
1602 bool isLevelModified = getProperties()->getDirtyFlag();
1603 bool isPaletteModified = false;
1604 if (getPalette()) isPaletteModified = getPalette()->getDirtyFlag();
1605
1606 if (isLevelModified || isPaletteModified) {
1607 // gmt (8/8/08. provo a risolvere il pasticcio della scrittura dei tlv.
1608 // Dobbiamo
1609 // ripensarci con piu' calma. Per ora cerco di fare meno danno possibile).
1610 TDimension oldRes(0, 0);
1611
1612 if (TSystem::doesExistFileOrLevel(decodedFp)) {
1613 TLevelReaderP lr(decodedFp);
1614 lr->doReadPalette(false);
1615 const TImageInfo *imageInfo = m_frames.empty()
1616 ? lr->getImageInfo()
1617 : lr->getImageInfo(*(m_frames.begin()));
1618
1619 if (imageInfo) {
1620 oldRes.lx = imageInfo->m_lx;
1621 oldRes.ly = imageInfo->m_ly;
1622 lr = TLevelReaderP();
1623 if (getProperties()->getImageRes() != oldRes) {
1624 // Il comando canvas size cambia le dimensioni del livello!!!
1625 // Se il file già esiste, nel level writer vengono risettate le
1626 // dimesnioni del file esistente
1627 // e salva male
1628 TSystem::removeFileOrLevel(decodedFp);
1629 }
1630 }
1631 }
1632 // overwrite tlv
1633 if (decodedFp.getType() == "tlv" &&
1634 TSystem::doesExistFileOrLevel(decodedFp)) {
1635 if (isLevelModified) {
1636 // in questo caso dovrei scrivere solo i frame modificati.
1637 // certamente NON DEVO scrivere quelli che non ho (e che dovrei
1638 // rileggere dallo stesso file che sto scrivendo
1639
1640 int oldSubs = getProperties()->getSubsampling();
1641
1642 TLevelWriterP lw;
1643 try {
1644 lw = TLevelWriterP(decodedFp);
1645 } catch (...) {
1646 // revert subsampling
1647 m_properties->setSubsampling(oldSubs);
1648 m_path = oldPath;
1649 throw TSystemException(decodedFp,
1650 "Can't open file.\nAccess may be denied or \n"
1651 "someone else may be saving the same file.\n"
1652 "Please wait and try again.");
1653 }
1654
1655 lw->setOverwritePaletteFlag(overwritePalette);
1656
1657 lw->setCreator(getCreatorString());
1658 lw->setPalette(getPalette());
1659
1660 // Filter out of the renumber table all non-tlv frames (could happen if
1661 // the level
1662 // is a scan-cleanup mix). This is fine even on the temporarily
1663 // substituted m_path.
1664 std::map<TFrameId, TFrameId> renumberTable;
1665
1666 for (auto it = m_renumberTable.rbegin(); it != m_renumberTable.rend();
1667 ++it) {
1668 TFrameId id = (*it).first;
1669 if ((getFrameStatus(id) != Scanned) &&
1670 (getFrameStatus(id) != CleanupPreview)) {
1671 renumberTable[id] = (*it).second;
1672 }
1673 }
1674
1675 m_renumberTable.clear();
1676 m_renumberTable = renumberTable;
1677
1678 lw->setIconSize(Preferences::instance()->getIconSize());
1679 if (!isSubsequence()) lw->renumberFids(m_renumberTable);
1680
1681 if (getContentHistory())
1682 lw->setContentHistory(getContentHistory()->clone());
1683
1684 ImageLoader::BuildExtData extData(this, TFrameId());
1685
1686 for (auto const &fid : fids) {
1687 std::string imageId = getImageId(
1688 fid, Normal); // Retrieve the actual level frames ("L_whatever")
1689 if (!ImageManager::instance()->isModified(imageId)) continue;
1690
1691 extData.m_fid = fid;
1692 TImageP img =
1693 ImageManager::instance()->getImage(imageId, imFlags, &extData);
1694
1695 assert(img);
1696 if (!img) continue;
1697
1698 int subs = 1;
1699 if (TToonzImageP ti = img)
1700 subs = ti->getSubsampling();
1701 else if (TRasterImageP ri = img)
1702 subs = ri->getSubsampling();
1703
1704 assert(subs == 1);
1705 if (subs != 1) continue;
1706
1707 if (TToonzImageP ti = img) {
1708 /*-
1709 * SaveBoxを塗り漏れ防止に使用している場合、実際の画像範囲とSaveBoxのサイズが異なるため、ここで更新しておく-*/
1710 TRect saveBox;
1711 TRop::computeBBox(ti->getRaster(), saveBox);
1712 ti->setSavebox(saveBox);
1713 }
1714
1715 lw->getFrameWriter(fid)->save(img);
1716 }
1717
1718 lw = TLevelWriterP(); // TLevelWriterP's destructor saves the palette
1719 } else if (isPaletteModified && overwritePalette) {
1720 TFilePath palettePath = decodedFp.withNoFrame().withType("tpl");
1721 if (Preferences::instance()->isBackupEnabled() &&
1722 TSystem::doesExistFileOrLevel(palettePath))
1723 saveBackup(palettePath);
1724 TOStream os(palettePath);
1725 if (os.checkStatus())
1726 os << getPalette();
1727 else
1728 paletteNotSaved = true;
1729 }
1730 } else {
1731 // per ora faccio quello che facevo prima, ma dobbiamo rivedere tutta la
1732 // strategia
1733 LevelUpdater updater(this);
1734 updater.getLevelWriter()->setCreator(getCreatorString());
1735 if (updater.getImageInfo())
1736 updater.getLevelWriter()->setFrameRate(
1737 updater.getImageInfo()->m_frameRate);
1738
1739 if (isLevelModified) {
1740 // Apply the level's renumber table, before saving other files.
1741 // NOTE: This is currently NOT under LevelUpdater's responsibility, as
1742 // renumber tables
1743 // are set/manipulated heavily here. The approach should be re-designed,
1744 // though...
1745 updater.getLevelWriter()->renumberFids(m_renumberTable);
1746
1747 if (!m_editableRange.empty())
1748 fids = std::vector<TFrameId>(m_editableRange.begin(),
1749 m_editableRange.end());
1750
1751 ImageLoader::BuildExtData extData(this, TFrameId());
1752
1753 for (auto const &fid : fids) {
1754 std::string imageId = getImageId(
1755 fid, Normal); // Retrieve the actual level frames ("L_whatever")
1756 if (!ImageManager::instance()->isModified(imageId)) continue;
1757
1758 extData.m_fid = fid;
1759 TImageP img =
1760 ImageManager::instance()->getImage(imageId, imFlags, &extData);
1761
1762 assert(img);
1763 if (!img) continue;
1764
1765 int subs = 1;
1766 if (TToonzImageP ti = img)
1767 subs = ti->getSubsampling();
1768 else if (TRasterImageP ri = img)
1769 subs = ri->getSubsampling();
1770
1771 assert(subs == 1);
1772 if (subs != 1) continue;
1773
1774 updater.update(fid, img);
1775 }
1776 }
1777 updater.close(); // Needs the original level subs
1778 if ((getType() & FULLCOLOR_TYPE) && isPaletteModified)
1779 FullColorPalette::instance()->savePalette(getScene());
1780 }
1781 }
1782
1783 // Save hooks
1784
1785 TFilePath hookFile;
1786 HookSet *hookSet = 0;
1787
1788 // Save the hookSet in a temporary hook file
1789 if (getType() == OVL_XSHLEVEL && !m_editableRange.empty()) {
1790 hookSet = new HookSet(*getHookSet());
1791
1792 FramesSet::const_iterator it;
1793 for (it = m_frames.begin(); it != m_frames.end(); ++it) {
1794 TFrameId fid = *it;
1795 if (m_editableRange.find(fid) == m_editableRange.end())
1796 hookSet->eraseFrame(fid);
1797 }
1798
1799 // file partially unlocked
1800 std::wstring fileName = getEditableFileName();
1801 assert(!fileName.empty());
1802 TFilePath app = decodedFp.withName(fileName).withType(decodedFp.getType());
1803 hookFile = getHookPath(app);
1804 } else {
1805 hookFile = getHookPath(decodedFp);
1806 hookSet = getHookSet();
1807 }
1808
1809 #ifdef _WIN32
1810 // Remove the hidden attribute (since TOStream's fopen fails on hidden files)
1811 if (getType() == OVL_XSHLEVEL && !m_editableRange.empty())
1812 SetFileAttributesW(hookFile.getWideString().c_str(), FILE_ATTRIBUTE_NORMAL);
1813 #endif
1814
1815 if (hookSet && hookSet->getHookCount() > 0) {
1816 TOStream os(hookFile);
1817 os.openChild("hooks");
1818 hookSet->saveData(os);
1819 os.closeChild();
1820 } else if (TFileStatus(hookFile).doesExist()) {
1821 try {
1822 TSystem::deleteFile(hookFile);
1823 } catch (...) {
1824 }
1825 }
1826
1827 #ifdef _WIN32
1828 if (getType() == OVL_XSHLEVEL && !m_editableRange.empty())
1829 TSystem::hideFileOrLevel(hookFile);
1830 #endif
1831
1832 if (savingOriginal) {
1833 setRenumberTable(); // Since the renumber table refers to the
1834 // 'original' frames saved on disk
1835 if (m_properties) m_properties->setDirtyFlag(false);
1836
1837 if (getPalette() && overwritePalette) getPalette()->setDirtyFlag(false);
1838 }
1839
1840 if (paletteNotSaved)
1841 throw TSystemException(m_path,
1842 "The palette of the level could not be saved.");
1843 }
1844
1845 //-----------------------------------------------------------------------------
1846
getImageId(const TFrameId & fid,int frameStatus) const1847 std::string TXshSimpleLevel::getImageId(const TFrameId &fid,
1848 int frameStatus) const {
1849 if (frameStatus < 0) frameStatus = getFrameStatus(fid);
1850 std::string prefix = "L";
1851 if (frameStatus & CleanupPreview)
1852 prefix = "P";
1853 else if ((frameStatus & (Scanned | Cleanupped)) == Scanned)
1854 prefix = "S";
1855 std::string imageId = m_idBase + "_" + prefix + fid.expand();
1856 return imageId;
1857 }
1858
1859 //-----------------------------------------------------------------------------
1860
getFrameStatus(const TFrameId & fid) const1861 int TXshSimpleLevel::getFrameStatus(const TFrameId &fid) const {
1862 std::map<TFrameId, int>::const_iterator it = m_framesStatus.find(fid);
1863 return (it != m_framesStatus.end()) ? it->second : Normal;
1864 }
1865
1866 //-----------------------------------------------------------------------------
1867
setFrameStatus(const TFrameId & fid,int status)1868 void TXshSimpleLevel::setFrameStatus(const TFrameId &fid, int status) {
1869 assert((status & ~(Scanned | Cleanupped | CleanupPreview)) == 0);
1870 m_framesStatus[fid] = status;
1871 }
1872
1873 //-----------------------------------------------------------------------------
1874 /*- CleanupPopup::setCurrentLevel / TCleanupper で使用 -*/
makeTlv(const TFilePath & tlvPath)1875 void TXshSimpleLevel::makeTlv(const TFilePath &tlvPath) {
1876 int ltype = getType();
1877
1878 if (!(ltype & FULLCOLOR_TYPE)) {
1879 assert(ltype & FULLCOLOR_TYPE);
1880 return;
1881 }
1882
1883 setType(TZP_XSHLEVEL);
1884
1885 m_scannedPath = m_path;
1886
1887 assert(tlvPath.getType() == "tlv");
1888 m_path = tlvPath;
1889
1890 FramesSet::const_iterator it;
1891 for (it = m_frames.begin(); it != m_frames.end(); ++it) {
1892 TFrameId fid = *it;
1893 setFrameStatus(fid, Scanned);
1894 ImageManager::instance()->rebind(getImageId(fid, Scanned),
1895 getImageId(fid, 0));
1896 ImageManager::instance()->rebind(getIconId(fid, Scanned),
1897 getIconId(fid, 0));
1898 }
1899 }
1900
1901 //-----------------------------------------------------------------------------
1902
invalidateFrames()1903 void TXshSimpleLevel::invalidateFrames() {
1904 FramesSet::iterator ft, fEnd = m_frames.end();
1905 for (ft = m_frames.begin(); ft != fEnd; ++ft)
1906 ImageManager::instance()->invalidate(getImageId(*ft));
1907 }
1908
1909 //-----------------------------------------------------------------------------
1910 /*- 指定したFIdのみInvalidateする -*/
invalidateFrame(const TFrameId & fid)1911 void TXshSimpleLevel::invalidateFrame(const TFrameId &fid) {
1912 std::string id = getImageId(fid);
1913 ImageManager::instance()->invalidate(id);
1914 }
1915
1916 //-----------------------------------------------------------------------------
1917 // note that the palette will always be replaced by the new one.
initializePalette()1918 void TXshSimpleLevel::initializePalette() {
1919 assert(getScene());
1920 int type = getType();
1921 if (type == TZP_XSHLEVEL || type == PLI_XSHLEVEL) setPalette(new TPalette());
1922 if (type == OVL_XSHLEVEL)
1923 setPalette(FullColorPalette::instance()->getPalette(getScene()));
1924 TPalette *palette = getPalette();
1925 if (palette && type != OVL_XSHLEVEL) {
1926 palette->setPaletteName(getName());
1927 palette->setDirtyFlag(true);
1928 }
1929 }
1930
1931 //-----------------------------------------------------------------------------
1932
initializeResolutionAndDpi(const TDimension & dim,double dpi)1933 void TXshSimpleLevel::initializeResolutionAndDpi(const TDimension &dim,
1934 double dpi) {
1935 assert(getScene());
1936 if (getProperties()->getImageRes() != TDimension() &&
1937 getProperties()->getDpi() != TPointD())
1938 return;
1939
1940 double dpiY = dpi;
1941 getProperties()->setDpiPolicy(LevelProperties::DP_ImageDpi);
1942 if (dim == TDimension()) {
1943 double w, h;
1944 Preferences *pref = Preferences::instance();
1945 if (pref->isNewLevelSizeToCameraSizeEnabled()) {
1946 TDimensionD camSize = getScene()->getCurrentCamera()->getSize();
1947 w = camSize.lx;
1948 h = camSize.ly;
1949 getProperties()->setDpiPolicy(LevelProperties::DP_CustomDpi);
1950 dpi = getScene()->getCurrentCamera()->getDpi().x;
1951 dpiY = getScene()->getCurrentCamera()->getDpi().y;
1952 } else {
1953 w = pref->getDefLevelWidth();
1954 h = pref->getDefLevelHeight();
1955 dpi = pref->getDefLevelDpi();
1956 dpiY = dpi;
1957 }
1958
1959 getProperties()->setImageRes(TDimension(tround(w * dpi), tround(h * dpiY)));
1960 } else
1961 getProperties()->setImageRes(dim);
1962
1963 getProperties()->setImageDpi(TPointD(dpi, dpiY));
1964 getProperties()->setDpi(dpi);
1965 }
1966
1967 //-----------------------------------------------------------------------------
1968
1969 // crea un frame con tipo, dimensioni, dpi, ecc. compatibili con il livello
createEmptyFrame()1970 TImageP TXshSimpleLevel::createEmptyFrame() {
1971 // In case this is the first frame to be created in this level (i.e. the level
1972 // file was missing when loading resources) initialize the level in the same
1973 // manner as createNewLevel() in order to avoid crash. This can be happened if
1974 // the level was not saved after creating and being placed in the xsheet.
1975 if (isEmpty()) {
1976 if (!getPalette()) initializePalette();
1977 initializeResolutionAndDpi();
1978 }
1979
1980 TImageP result;
1981
1982 switch (m_type) {
1983 case PLI_XSHLEVEL:
1984 result = new TVectorImage;
1985 break;
1986
1987 case MESH_XSHLEVEL:
1988 assert(false); // Not implemented yet
1989 break;
1990
1991 default: {
1992 // normally the image must have the level->getProperties()->getImageDpi().
1993 // if this value is missing (for some reason - can this happen, ever?) then
1994 // we use the getDpi() (that is the current dpi, e.g. cameraDpi or
1995 // customDpi).
1996
1997 TPointD dpi = getProperties()->getImageDpi();
1998 /*--
1999 tgaからtlvにconvertしたものをInsert Pasteしたとき、
2000 ペーストしたフレームにのみDPIが付いてしまうので、この処理は省く
2001 --*/
2002 // if(dpi.x==0.0 || dpi.y==0.0)
2003 // dpi = getProperties()->getDpi();
2004
2005 TDimension res = getProperties()->getImageRes();
2006
2007 if (m_type == TZP_XSHLEVEL) {
2008 TRasterCM32P raster(res);
2009 raster->fill(TPixelCM32());
2010 TToonzImageP ti(raster, TRect());
2011 ti->setDpi(dpi.x, dpi.y);
2012 ti->setSavebox(TRect(0, 0, res.lx - 1, res.ly - 1));
2013
2014 result = ti;
2015 } else {
2016 TRaster32P raster(res);
2017 raster->fill(TPixel32(0, 0, 0, 0));
2018 TRasterImageP ri(raster);
2019 ri->setDpi(dpi.x, dpi.y);
2020
2021 result = ri;
2022 }
2023
2024 break;
2025 }
2026 }
2027
2028 return result;
2029 }
2030
2031 //-----------------------------------------------------------------------------
2032
2033 // ritorna la risoluzione dei frames del livello (se il livello non e'
2034 // vettoriale)
getResolution()2035 TDimension TXshSimpleLevel::getResolution() {
2036 if (isEmpty() || getType() == PLI_XSHLEVEL) return TDimension();
2037 return m_properties->getImageRes();
2038 }
2039
2040 //-----------------------------------------------------------------------------
2041
2042 // ritorna il dpi letto da file
getImageDpi(const TFrameId & fid,int frameStatus)2043 TPointD TXshSimpleLevel::getImageDpi(const TFrameId &fid, int frameStatus) {
2044 if (isEmpty() || getType() == PLI_XSHLEVEL) return TPointD();
2045
2046 const TFrameId &theFid =
2047 (fid == TFrameId::NO_FRAME || !isFid(fid)) ? getFirstFid() : fid;
2048 const std::string &imageId = getImageId(theFid, frameStatus);
2049
2050 const TImageInfo *imageInfo =
2051 ImageManager::instance()->getInfo(imageId, ImageManager::none, 0);
2052
2053 if (!imageInfo) return TPointD();
2054
2055 return TPointD(imageInfo->m_dpix, imageInfo->m_dpiy);
2056 }
2057
2058 //-----------------------------------------------------------------------------
2059
getImageSubsampling(const TFrameId & fid) const2060 int TXshSimpleLevel::getImageSubsampling(const TFrameId &fid) const {
2061 if (isEmpty() || getType() == PLI_XSHLEVEL) return 1;
2062 TImageP img = TImageCache::instance()->get(getImageId(fid), false);
2063 if (!img) return 1;
2064 if (TRasterImageP ri = img) return ri->getSubsampling();
2065 if (TToonzImageP ti = img) return ti->getSubsampling();
2066 return 1;
2067 }
2068
2069 //-----------------------------------------------------------------------------
2070
2071 // ritorna il dpi corrente del livello
getDpi(const TFrameId & fid,int frameStatus)2072 TPointD TXshSimpleLevel::getDpi(const TFrameId &fid, int frameStatus) {
2073 TPointD dpi;
2074 if (m_properties->getDpiPolicy() == LevelProperties::DP_ImageDpi)
2075 dpi = getImageDpi(fid, frameStatus);
2076 else
2077 dpi = m_properties->getDpi();
2078 return dpi;
2079 }
2080
2081 //-----------------------------------------------------------------------------
2082
renumber(const std::vector<TFrameId> & fids)2083 void TXshSimpleLevel::renumber(const std::vector<TFrameId> &fids) {
2084 assert(fids.size() == m_frames.size());
2085 int n = fids.size();
2086
2087 int i = 0;
2088 std::vector<TFrameId> oldFids;
2089 getFids(oldFids);
2090 std::map<TFrameId, TFrameId> table;
2091 std::map<TFrameId, TFrameId> newRenumberTable;
2092 for (std::vector<TFrameId>::iterator it = oldFids.begin();
2093 it != oldFids.end(); ++it) {
2094 TFrameId oldFrameId = *it;
2095 TFrameId newFrameId = fids[i++];
2096 table[oldFrameId] = newFrameId;
2097 for (auto const &renumber : m_renumberTable) {
2098 if (renumber.second == oldFrameId) {
2099 newRenumberTable[renumber.first] = newFrameId;
2100 break;
2101 }
2102 }
2103 }
2104 for (auto const &renumber : newRenumberTable) {
2105 m_renumberTable[renumber.first] = renumber.second;
2106 }
2107
2108 m_frames.clear();
2109 for (i = 0; i < n; ++i) {
2110 TFrameId fid(fids[i]);
2111 assert(m_frames.count(fid) == 0);
2112 m_frames.insert(fid);
2113 }
2114
2115 ImageManager *im = ImageManager::instance();
2116 TImageCache *ic = TImageCache::instance();
2117
2118 std::map<TFrameId, TFrameId>::iterator jt;
2119
2120 {
2121 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2122 std::string Id = getImageId(jt->first);
2123 ImageLoader::BuildExtData extData(this, jt->first);
2124 TImageP img = im->getImage(Id, ImageManager::none, &extData);
2125 ic->add(getIconId(jt->first), img, false);
2126 im->rebind(Id, "^" + std::to_string(i));
2127 ic->remap("^icon:" + std::to_string(i), getIconId(jt->first));
2128 }
2129
2130 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2131 std::string Id = getImageId(jt->second);
2132 im->rebind("^" + std::to_string(i), Id);
2133 ic->remap(getIconId(jt->second), "^icon:" + std::to_string(i));
2134 im->renumber(Id, jt->second);
2135 }
2136 }
2137
2138 if (getType() == PLI_XSHLEVEL) {
2139 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2140 const std::string &id = rasterized(getImageId(jt->first));
2141 if (im->isBound(id)) im->rebind(id, rasterized("^" + std::to_string(i)));
2142 }
2143 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2144 const std::string &id = rasterized("^" + std::to_string(i));
2145 if (im->isBound(id)) im->rebind(id, rasterized(getImageId(jt->second)));
2146 }
2147 }
2148
2149 if (getType() & FULLCOLOR_TYPE) {
2150 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2151 const std::string &id = filled(getImageId(jt->first));
2152 if (im->isBound(id)) im->rebind(id, filled("^" + std::to_string(i)));
2153 }
2154 for (i = 0, jt = table.begin(); jt != table.end(); ++jt, ++i) {
2155 const std::string &id = filled("^" + std::to_string(i));
2156 if (im->isBound(id)) im->rebind(id, filled(getImageId(jt->second)));
2157 }
2158 }
2159
2160 m_properties->setDirtyFlag(true);
2161
2162 if (getHookSet()) getHookSet()->renumber(table);
2163 }
2164
2165 //-----------------------------------------------------------------------------
2166
copyFiles(const TFilePath & dst,const TFilePath & src)2167 void TXshSimpleLevel::copyFiles(const TFilePath &dst, const TFilePath &src) {
2168 if (dst == src) return;
2169 TSystem::touchParentDir(dst);
2170 TSystem::copyFileOrLevel_throw(dst, src);
2171 if (dst.getType() == "tlv") {
2172 // Copio la palette del livello
2173 TFilePath srcPltPath =
2174 src.getParentDir() + TFilePath(src.getWideName() + L".tpl");
2175 if (TFileStatus(srcPltPath).doesExist())
2176 TSystem::copyFile(
2177 dst.getParentDir() + TFilePath(dst.getWideName() + L".tpl"),
2178 srcPltPath, true);
2179 }
2180 if (dst.getType() == "tzp" || dst.getType() == "tzu") {
2181 // Copio la palette del livello
2182 TFilePath srcPltPath =
2183 src.getParentDir() + TFilePath(src.getWideName() + L".plt");
2184 if (TFileStatus(srcPltPath).doesExist())
2185 TSystem::copyFile(
2186 dst.getParentDir() + TFilePath(dst.getWideName() + L".plt"),
2187 srcPltPath, true);
2188 }
2189
2190 const TFilePath &srcHookFile = TXshSimpleLevel::getExistingHookFile(src);
2191 if (!srcHookFile.isEmpty()) {
2192 const TFilePath &dstHookFile = getHookPath(dst);
2193 TSystem::copyFile(dstHookFile, srcHookFile, true);
2194 }
2195 TFilePath files = src.getParentDir() + (src.getName() + "_files");
2196 if (TFileStatus(files).doesExist() && TFileStatus(files).isDirectory())
2197 TSystem::copyDir(dst.getParentDir() + (src.getName() + "_files"), files);
2198 }
2199
2200 //-----------------------------------------------------------------------------
2201
renameFiles(const TFilePath & dst,const TFilePath & src)2202 void TXshSimpleLevel::renameFiles(const TFilePath &dst, const TFilePath &src) {
2203 if (dst == src) return;
2204 TSystem::touchParentDir(dst);
2205 if (TSystem::doesExistFileOrLevel(dst)) TXshSimpleLevel::removeFiles(dst);
2206 TSystem::renameFileOrLevel_throw(dst, src);
2207 if (dst.getType() == "tlv")
2208 TSystem::renameFile(dst.withType("tpl"), src.withType("tpl"));
2209
2210 const TFilePath &srcHookFile = TXshSimpleLevel::getExistingHookFile(src);
2211 if (!srcHookFile.isEmpty()) {
2212 const TFilePath &dstHookFile = getHookPath(dst);
2213 TSystem::renameFile(dstHookFile, srcHookFile);
2214 }
2215
2216 TFilePath files = src.getParentDir() + (src.getName() + "_files");
2217 if (TFileStatus(files).doesExist() && TFileStatus(files).isDirectory())
2218 TSystem::renameFile(dst.getParentDir() + (dst.getName() + "_files"), files);
2219 }
2220
2221 //-----------------------------------------------------------------------------
2222
removeFiles(const TFilePath & fp)2223 void TXshSimpleLevel::removeFiles(const TFilePath &fp) {
2224 TSystem::moveFileOrLevelToRecycleBin(fp);
2225 if (fp.getType() == "tlv") {
2226 TFilePath tpl = fp.withType("tpl");
2227 if (TFileStatus(tpl).doesExist()) TSystem::moveFileToRecycleBin(tpl);
2228 }
2229
2230 // Delete ALL hook files (ie from every Toonz version)
2231 const QStringList &hookFiles = TXshSimpleLevel::getHookFiles(fp);
2232
2233 int f, fCount = hookFiles.size();
2234 for (f = 0; f != fCount; ++f)
2235 TSystem::moveFileToRecycleBin(TFilePath(hookFiles[f].toStdWString()));
2236
2237 TFilePath files = fp.getParentDir() + (fp.getName() + "_files");
2238 if (TFileStatus(files).doesExist() && TFileStatus(files).isDirectory())
2239 TSystem::rmDirTree(files);
2240 }
2241
2242 //-----------------------------------------------------------------------------
2243
getFiles(const TFilePath & fp,TFilePathSet & fpset)2244 void TXshSimpleLevel::getFiles(const TFilePath &fp, TFilePathSet &fpset) {
2245 if (fp.getType() == "tlv") {
2246 TFilePath tpl = fp.withType("tpl");
2247 if (TFileStatus(tpl).doesExist()) fpset.push_back(tpl);
2248 }
2249
2250 // Store the hooks file if any (NOTE: there could be more than one hooks
2251 // file. I'm retaining the behavior I've seen, but was this really intended?
2252 // Shouldn't we return ALL the hooks files?)
2253 const TFilePath &hookFile = getExistingHookFile(fp);
2254 if (!hookFile.isEmpty()) fpset.push_back(hookFile);
2255
2256 // Needed for TAB Manga & Kids and not used in Toonz
2257
2258 // TFilePath files = fp.getParentDir() + (fp.getName() + "_files");
2259 // if(TFileStatus(files).doesExist() && TFileStatus(files).isDirectory())
2260 // TSystem::rmDirTree(files);
2261 }
2262
2263 //-----------------------------------------------------------------------------
2264
setContentHistory(TContentHistory * contentHistory)2265 void TXshSimpleLevel::setContentHistory(TContentHistory *contentHistory) {
2266 if (contentHistory != m_contentHistory.get())
2267 m_contentHistory.reset(contentHistory);
2268 }
2269
2270 //-----------------------------------------------------------------------------
2271
setCompatibilityMasks(int writeMask,int neededMask,int forbiddenMask)2272 void TXshSimpleLevel::setCompatibilityMasks(int writeMask, int neededMask,
2273 int forbiddenMask) {
2274 compatibility.writeMask = writeMask;
2275 compatibility.neededMask = neededMask;
2276 compatibility.forbiddenMask = forbiddenMask;
2277 }
2278
2279 //-----------------------------------------------------------------------------
2280
getHookPath(const TFilePath & path)2281 TFilePath TXshSimpleLevel::getHookPath(const TFilePath &path) {
2282 // Translates: levelName..ext into levelName_hooks..ext.xml
2283 // levelName.ext into levelName_hooks.ext.xml
2284
2285 // Observe that retaining the original level extension IS IMPORTANT, as it
2286 // ensures
2287 // the UNICITY of the association between a level path and its hook path (ie
2288 // levels test..png and test..tif have separate hook files).
2289
2290 return TFilePath(path.withName(path.getName() + "_hooks").getWideString() +
2291 L".xml");
2292 }
2293
2294 //-----------------------------------------------------------------------------
2295
getHookFiles(const TFilePath & decodedLevelPath)2296 QStringList TXshSimpleLevel::getHookFiles(const TFilePath &decodedLevelPath) {
2297 const TFilePath &dirPath = decodedLevelPath.getParentDir();
2298 QDir levelDir(QString::fromStdWString(dirPath.getWideString()));
2299
2300 QStringList hookFileFilter(
2301 QString::fromStdWString( // We have to scan for files of the
2302 decodedLevelPath.getWideName() +
2303 L"_hooks*.xml")); // form levelName_hooks*.xml to
2304 // retain backward compatibility
2305 return levelDir.entryList( //
2306 hookFileFilter,
2307 QDir::Files | QDir::NoDotAndDotDot, // Observe that we cleverly sort by
2308 QDir::Time); // mod date :)
2309 }
2310
2311 //-----------------------------------------------------------------------------
2312
getExistingHookFile(const TFilePath & decodedLevelPath)2313 TFilePath TXshSimpleLevel::getExistingHookFile(
2314 const TFilePath &decodedLevelPath) {
2315 static const int pCount = 3;
2316 static const QRegExp pattern[pCount] = {
2317 // Prioritized in this order
2318 QRegExp(".*\\.\\.?.+\\.xml$"), // whatever.(.)ext.xml
2319 QRegExp(".*\\.xml$"), // whatever.xml
2320 QRegExp(".*\\.\\.?xml$") // whatever.(.)xml
2321 };
2322
2323 struct locals {
2324 static inline int getPattern(const QString &fp) {
2325 for (int p = 0; p != pCount; ++p)
2326 if (pattern[p].exactMatch(fp)) return p;
2327 return -1;
2328 }
2329 }; // locals
2330
2331 const QStringList &hookFiles = getHookFiles(decodedLevelPath);
2332 if (hookFiles.empty()) return TFilePath();
2333
2334 // Return the hook file with the most recent (smallest) identified
2335 // regexp pattern
2336 int fPattern, p = pCount, h = -1;
2337
2338 int f, fCount = hookFiles.size();
2339 for (f = 0; f != fCount; ++f) {
2340 fPattern = locals::getPattern(hookFiles[f]);
2341 if (fPattern < p) p = fPattern, h = f;
2342 }
2343
2344 assert(h >= 0);
2345 return (h < 0) ? TFilePath()
2346 : decodedLevelPath.getParentDir() +
2347 TFilePath(hookFiles[h].toStdWString());
2348 }
2349
2350 //-----------------------------------------------------------------------------
2351
getBBox(const TFrameId & fid) const2352 TRectD TXshSimpleLevel::getBBox(const TFrameId &fid) const {
2353 TRectD bbox;
2354 double dpiX = Stage::inch, dpiY = dpiX;
2355
2356 // Get the frame bbox in image coordinates
2357 switch (getType()) {
2358 case PLI_XSHLEVEL:
2359 case MESH_XSHLEVEL: {
2360 // Load the image and extract its bbox forcibly
2361 TImageP img = getFrame(fid, false);
2362 if (!img) return TRectD();
2363
2364 bbox = img->getBBox();
2365
2366 if (TMeshImageP mi = img) mi->getDpi(dpiX, dpiY);
2367
2368 break;
2369 }
2370
2371 default: {
2372 // Raster case: retrieve the image info from the ImageManager
2373 const std::string &imageId = getImageId(fid);
2374
2375 const TImageInfo *info =
2376 ImageManager::instance()->getInfo(imageId, ImageManager::none, 0);
2377 if (!info) return TRectD();
2378
2379 bbox = TRectD(TPointD(info->m_x0, info->m_y0),
2380 TPointD(info->m_x1, info->m_y1)) -
2381 0.5 * TPointD(info->m_lx, info->m_ly);
2382
2383 if (info->m_dpix > 0.0 && info->m_dpiy > 0.0)
2384 dpiX = info->m_dpix, dpiY = info->m_dpiy;
2385
2386 break;
2387 }
2388 }
2389
2390 // Get the frame's dpi and traduce the bbox to inch coordinates
2391 return TScale(1.0 / dpiX, 1.0 / dpiY) * bbox;
2392 }
2393
isFrameReadOnly(TFrameId fid)2394 bool TXshSimpleLevel::isFrameReadOnly(TFrameId fid) {
2395 // For Raster and mesh files, check to see if files are marked as read-only at
2396 // the OS level
2397 if (getType() == OVL_XSHLEVEL || getType() == TZI_XSHLEVEL ||
2398 getType() == MESH_XSHLEVEL) {
2399 if (getProperties()->isStopMotionLevel()) return true;
2400 TFilePath fullPath = getScene()->decodeFilePath(m_path);
2401 std::string fileType = fullPath.getType();
2402 if (fileType == "psd" || fileType == "gif" || fileType == "mp4" ||
2403 fileType == "webm")
2404 return true;
2405 TFilePath path =
2406 fullPath.getDots() == ".." ? fullPath.withFrame(fid) : fullPath;
2407 if (!TSystem::doesExistFileOrLevel(path)) return false;
2408 TFileStatus fs(path);
2409 return !fs.isWritable();
2410 }
2411
2412 // If Level is marked read only, check for editable frames
2413 if (m_isReadOnly && !m_editableRange.empty() &&
2414 m_editableRange.count(fid) != 0)
2415 return false;
2416
2417 return m_isReadOnly;
2418 }
2419