1
2
3 // TnzQt includes
4 #include "toonzqt/menubarcommand.h"
5 #include "toonzqt/viewcommandids.h"
6 #include "toonzqt/imageutils.h"
7 #include "toonzqt/dvdialog.h"
8 #include "toonzqt/gutil.h"
9
10 // Qt Includes:
11 #include <QScreen>
12 #include <QWindow>
13
14 // TnzLib includes
15 #include "toonz/preferences.h"
16 #include "toonz/namebuilder.h"
17 #include "toonz/toonzscene.h"
18 #include "toonz/tproject.h"
19 #include "toonz/Naa2TlvConverter.h"
20 #include "toonz/toonzimageutils.h"
21
22 #ifdef _WIN32
23 #include "avicodecrestrictions.h"
24 #endif
25
26 // TnzCore includes
27 #include "tsystem.h"
28 #include "tfilepath_io.h"
29 #include "timage_io.h"
30 #include "tlevel.h"
31 #include "tlevel_io.h"
32 #include "tpalette.h"
33 #include "tropcm.h"
34 #include "trasterimage.h"
35 #include "tvectorrenderdata.h"
36 #include "tofflinegl.h"
37 #include "tconvert.h"
38 #include "trop.h"
39 #include "timageinfo.h"
40 #include "tfiletype.h"
41 #include "tfilepath.h"
42 #include "ttoonzimage.h"
43 #include "tvectorimage.h"
44 #include "tstroke.h"
45
46 // TnzQt includes
47 #include <QApplication>
48 #ifdef _WIN32
49 #include <QtPlatformHeaders/QWindowsWindowFunctions>
50 #endif
51
52 // boost includes
53 #include <boost/iterator/transform_iterator.hpp>
54
55 //**********************************************************************************
56 // Local namespace stuff
57 //**********************************************************************************
58
59 namespace {
60
addOverlappedRegions(TRegion * reg,std::vector<TFilledRegionInf> & regInf)61 void addOverlappedRegions(TRegion *reg, std::vector<TFilledRegionInf> ®Inf) {
62 regInf.push_back(TFilledRegionInf(reg->getId(), reg->getStyle()));
63 UINT regNum = reg->getSubregionCount();
64 for (UINT i = 0; i < regNum; i++)
65 addOverlappedRegions(reg->getSubregion(i), regInf);
66 }
67
68 //--------------------------------------------------------------------
69
addRegionsInArea(TRegion * reg,std::vector<TFilledRegionInf> & regs,const TRectD & area)70 void addRegionsInArea(TRegion *reg, std::vector<TFilledRegionInf> ®s,
71 const TRectD &area) {
72 if (area.contains(reg->getBBox()))
73 regs.push_back(TFilledRegionInf(reg->getId(), reg->getStyle()));
74
75 if (reg->getBBox().overlaps(area)) {
76 UINT regNum = reg->getSubregionCount();
77 for (UINT i = 0; i < regNum; i++)
78 addRegionsInArea(reg->getSubregion(i), regs, area);
79 }
80 }
81
82 //--------------------------------------------------------------------
83
getFrameIds(TFrameId from,TFrameId to,const TLevelP & level,std::vector<TFrameId> & frames)84 void getFrameIds(TFrameId from, TFrameId to, const TLevelP &level,
85 std::vector<TFrameId> &frames) {
86 struct locals {
87 static inline TFrameId getFrame(const TLevel::Table::value_type &pair) {
88 return pair.first;
89 }
90 }; // locals
91
92 if (from.isEmptyFrame()) from = TFrameId(-(std::numeric_limits<int>::max)());
93
94 if (to.isEmptyFrame()) to = TFrameId((std::numeric_limits<int>::max)());
95
96 if (from > to) std::swap(from, to);
97
98 const TLevel::Table &table = *level->getTable();
99
100 TLevel::Table::const_iterator lBegin = table.lower_bound(from),
101 lEnd = table.upper_bound(to);
102
103 assert(frames.empty());
104 frames.insert(frames.end(),
105 boost::make_transform_iterator(lBegin, locals::getFrame),
106 boost::make_transform_iterator(lEnd, locals::getFrame));
107 }
108
109 } // namespace
110
111 //**********************************************************************************
112 // ImageUtils namespace stuff
113 //**********************************************************************************
114
115 namespace ImageUtils {
116
117 // todo: spostare da qualche altra parte. rendere accessibile a tutti.
118 // utilizzare
119 // anche nella libreria dovunque si usi direttamente "_files"
getResourceFolder(const TFilePath & scenePath)120 static TFilePath getResourceFolder(const TFilePath &scenePath) {
121 return scenePath.getParentDir() + (scenePath.getName() + "_files");
122 }
123
124 //-----------------------------------------------------------------------------
125
copyScene(const TFilePath & dst,const TFilePath & src)126 static void copyScene(const TFilePath &dst, const TFilePath &src) {
127 TSystem::copyFile(dst, src);
128 if (TProjectManager::instance()->isTabModeEnabled())
129 TSystem::copyDir(getResourceFolder(dst), getResourceFolder(src));
130 TFilePath srcIcon = ToonzScene::getIconPath(src);
131 if (TFileStatus(srcIcon).doesExist()) {
132 TFilePath dstIcon = ToonzScene::getIconPath(dst);
133 TSystem::copyFile(dstIcon, srcIcon);
134 }
135 }
136
137 //-----------------------------------------------------------------------------
138
duplicate(const TFilePath & levelPath)139 TFilePath duplicate(const TFilePath &levelPath) {
140 if (levelPath == TFilePath()) return TFilePath();
141
142 if (!TSystem::doesExistFileOrLevel(levelPath)) {
143 DVGui::warning(
144 QObject::tr("It is not possible to find the %1 level.")
145 .arg(QString::fromStdWString(levelPath.getWideString())));
146 return TFilePath();
147 }
148
149 NameBuilder *nameBuilder =
150 NameBuilder::getBuilder(::to_wstring(levelPath.getName()));
151 std::wstring levelNameOut;
152 do
153 levelNameOut = nameBuilder->getNext();
154 while (TSystem::doesExistFileOrLevel(levelPath.withName(levelNameOut)));
155
156 TFilePath levelPathOut = levelPath.withName(levelNameOut);
157
158 try {
159 if (levelPath.getType() == "tnz")
160 copyScene(levelPathOut, levelPath);
161 else {
162 TSystem::copyFileOrLevel_throw(levelPathOut, levelPath);
163 if (levelPath.getType() == "tlv") {
164 TFilePath pltPath = levelPath.withType("tpl");
165 if (TSystem::doesExistFileOrLevel(pltPath))
166 TSystem::copyFileOrLevel_throw(levelPathOut.withType("tpl"), pltPath);
167 }
168 }
169 } catch (...) {
170 QString msg =
171 QObject::tr("There was an error copying %1").arg(toQString(levelPath));
172 DVGui::error(msg);
173 return TFilePath();
174 }
175
176 // QString msg = QString("Copied ") +
177 // QString::fromStdWString(levelPath.withoutParentDir().getWideString())
178 // +
179 // QString(" to " +
180 // QString::fromStdWString(levelPathOut.withoutParentDir().getWideString()));
181 // DVGui::info(msg);
182
183 return levelPathOut;
184 }
185
186 //-----------------------------------------------------------------------------
187
premultiply(const TFilePath & levelPath)188 void premultiply(const TFilePath &levelPath) {
189 if (levelPath == TFilePath()) return;
190
191 if (!TSystem::doesExistFileOrLevel(levelPath)) {
192 DVGui::warning(
193 QObject::tr("It is not possible to find the level %1")
194 .arg(QString::fromStdWString(levelPath.getWideString())));
195 return;
196 }
197
198 TFileType::Type type = TFileType::getInfo(levelPath);
199 if (type == TFileType::CMAPPED_LEVEL) {
200 DVGui::warning(QObject::tr("Cannot premultiply the selected file."));
201 return;
202 }
203 if (type == TFileType::VECTOR_LEVEL || type == TFileType::VECTOR_IMAGE) {
204 DVGui::warning(QObject::tr("Cannot premultiply a vector-based level."));
205 return;
206 }
207 if (type != TFileType::RASTER_LEVEL && type != TFileType::RASTER_IMAGE) {
208 DVGui::info(QObject::tr("Cannot premultiply the selected file."));
209 return;
210 }
211
212 try {
213 TLevelReaderP lr = TLevelReaderP(levelPath);
214 if (!lr) return;
215 TLevelP level = lr->loadInfo();
216 if (!level || level->getFrameCount() == 0) return;
217
218 bool isMovie = type == TFileType::RASTER_LEVEL;
219 /*
220 if (!isMovie && lr->getImageInfo()->m_samplePerPixel!=4)
221 {
222 QMessageBox::information(0, QString("ERROR"), QString("Only rgba images can be
223 premultiplied!"));
224 return;
225 }
226 */
227
228 TLevelWriterP lw;
229 if (!isMovie) {
230 lw = TLevelWriterP(levelPath);
231 if (!lw) return;
232 }
233
234 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
235 qApp->processEvents();
236
237 int count = 0;
238 TLevel::Iterator it = level->begin();
239
240 for (; it != level->end(); ++it) {
241 TImageReaderP ir = lr->getFrameReader(it->first);
242
243 TRasterImageP rimg = (TRasterImageP)ir->load();
244 if (!rimg) continue;
245
246 TRop::premultiply(rimg->getRaster());
247 ir = 0;
248
249 if (isMovie)
250 level->setFrame(it->first, rimg);
251 else {
252 TImageWriterP iw = lw->getFrameWriter(it->first);
253 iw->save(levelPath.withFrame(it->first), rimg);
254 iw = 0;
255 }
256 }
257 lr = TLevelReaderP();
258
259 if (isMovie) {
260 TSystem::deleteFile(levelPath);
261 lw = TLevelWriterP(levelPath);
262 if (!lw) {
263 QApplication::restoreOverrideCursor();
264 return;
265 }
266 lw->save(level);
267 }
268 } catch (...) {
269 DVGui::warning(QObject::tr("Cannot premultiply the selected file."));
270 QApplication::restoreOverrideCursor();
271 return;
272 }
273
274 QApplication::restoreOverrideCursor();
275 QString msg = QObject::tr("Level %1 premultiplied.")
276 .arg(QString::fromStdString(levelPath.getLevelName()));
277 DVGui::info(msg);
278 }
279
280 //-----------------------------------------------------------------------------
281
getFillingInformationOverlappingArea(const TVectorImageP & vi,std::vector<TFilledRegionInf> & regInf,const TRectD & area1,const TRectD & area2)282 void getFillingInformationOverlappingArea(const TVectorImageP &vi,
283 std::vector<TFilledRegionInf> ®Inf,
284 const TRectD &area1,
285 const TRectD &area2) {
286 if (!vi->isComputedRegionAlmostOnce()) return;
287
288 assert(area1 != TRectD() || area2 != TRectD());
289
290 vi->findRegions();
291 UINT regNum = vi->getRegionCount();
292 TRegion *reg;
293
294 for (UINT i = 0; i < regNum; i++) {
295 reg = vi->getRegion(i);
296 if (reg->getBBox().overlaps(area1) || reg->getBBox().overlaps(area2))
297 addOverlappedRegions(reg, regInf);
298 }
299 }
300
301 //-----------------------------------------------------------------------------
302
getFillingInformationInArea(const TVectorImageP & vi,std::vector<TFilledRegionInf> & regs,const TRectD & area)303 void getFillingInformationInArea(const TVectorImageP &vi,
304 std::vector<TFilledRegionInf> ®s,
305 const TRectD &area) {
306 if (!vi->isComputedRegionAlmostOnce()) return;
307 vi->findRegions();
308 UINT regNum = vi->getRegionCount();
309
310 for (UINT i = 0; i < regNum; i++)
311 addRegionsInArea(vi->getRegion(i), regs, area);
312 }
313
314 //--------------------------------------------------------------------
315
assignFillingInformation(TVectorImage & vi,const std::vector<TFilledRegionInf> & regs)316 void assignFillingInformation(TVectorImage &vi,
317 const std::vector<TFilledRegionInf> ®s) {
318 vi.findRegions();
319
320 UINT r, rCount = UINT(regs.size());
321 for (r = 0; r != rCount; ++r) {
322 const TFilledRegionInf &rInfo = regs[r];
323
324 if (TRegion *region = vi.getRegion(rInfo.m_regionId))
325 region->setStyle(rInfo.m_styleId);
326 }
327 }
328
329 //--------------------------------------------------------------------
330
getStrokeStyleInformationInArea(const TVectorImageP & vi,std::vector<std::pair<int,int>> & strokesInfo,const TRectD & area)331 void getStrokeStyleInformationInArea(
332 const TVectorImageP &vi, std::vector<std::pair<int, int>> &strokesInfo,
333 const TRectD &area) {
334 if (!vi->isComputedRegionAlmostOnce()) return;
335 vi->findRegions();
336 UINT regNum = vi->getStrokeCount();
337
338 for (UINT i = 0; i < vi->getStrokeCount(); i++) {
339 if (!vi->inCurrentGroup(i)) continue;
340 if (area.contains(vi->getStroke(i)->getBBox()))
341 strokesInfo.push_back(
342 std::pair<int, int>(i, vi->getStroke(i)->getStyle()));
343 }
344 }
345
346 //--------------------------------------------------------------------
convertFromCM(const TLevelReaderP & lr,const TPaletteP & plt,const TLevelWriterP & lw,const std::vector<TFrameId> & frames,const TAffine & aff,const TRop::ResampleFilterType & resType,FrameTaskNotifier * frameNotifier,const TPixel & bgColor,bool removeDotBeforeFrameNumber=false)347 static void convertFromCM(
348 const TLevelReaderP &lr, const TPaletteP &plt, const TLevelWriterP &lw,
349 const std::vector<TFrameId> &frames, const TAffine &aff,
350 const TRop::ResampleFilterType &resType, FrameTaskNotifier *frameNotifier,
351 const TPixel &bgColor, bool removeDotBeforeFrameNumber = false) {
352 for (int i = 0; i < (int)frames.size(); i++) {
353 if (frameNotifier->abortTask()) break;
354 try {
355 TImageReaderP ir = lr->getFrameReader(frames[i]);
356 TImageP img = ir->load();
357 TToonzImageP toonzImage(img);
358 double xdpi, ydpi;
359 toonzImage->getDpi(xdpi, ydpi);
360 assert(toonzImage);
361 if (toonzImage) {
362 TRasterCM32P rasCMImage = toonzImage->getRaster();
363 TRaster32P ras(
364 convert(aff * convert(rasCMImage->getBounds())).getSize());
365 if (!aff.isIdentity())
366 TRop::resample(ras, rasCMImage, plt, aff, resType);
367 else
368 TRop::convert(ras, rasCMImage, plt);
369 if (bgColor != TPixel::Transparent) TRop::addBackground(ras, bgColor);
370
371 TRasterImageP rasImage(ras);
372 rasImage->setDpi(xdpi, ydpi);
373
374 /*---
375 ConvertPopup
376 での指定に合わせて、[レベル名].[フレーム番号].[拡張子]
377 のうち、[レベル名]と[フレーム番号]の間のドットを消す。
378 例:A.0001.tga → A0001.tga になる。
379 ---*/
380 if (removeDotBeforeFrameNumber) {
381 TFilePath outPath = lw->getFilePath().withFrame(frames[i]);
382 /*--- フレーム番号と拡張子の文字数の合計。大抵8 ---*/
383 int index = 4 + 1 + outPath.getType().length();
384 std::wstring renamedStr = outPath.getWideString();
385 if (renamedStr[renamedStr.length() - index - 1] == L'.')
386 renamedStr = renamedStr.substr(0, renamedStr.length() - index - 1) +
387 renamedStr.substr(renamedStr.length() - index, index);
388
389 const TFilePath fp(renamedStr);
390 TImageWriterP writer(fp);
391 writer->setProperties(lw->getProperties());
392 writer->save(rasImage);
393 } else {
394 TImageWriterP iw = lw->getFrameWriter(frames[i]);
395 iw->save(rasImage);
396 }
397 }
398 } catch (...) {
399 // QString msg=QObject::tr("Frame %1 : conversion
400 // failed!").arg(QString::number(i+1));
401 // DVGui::info(msg);
402 }
403 /*--
404 * これはプログレスバーを進めるものなので、動画番号ではなく、完了したフレームの枚数を投げる
405 * --*/
406 frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
407 }
408 }
409
410 //--------------------------------------------------------------------
411
convertFromVI(const TLevelReaderP & lr,const TPaletteP & plt,const TLevelWriterP & lw,const std::vector<TFrameId> & frames,const TRop::ResampleFilterType & resType,int width,FrameTaskNotifier * frameNotifier)412 static void convertFromVI(const TLevelReaderP &lr, const TPaletteP &plt,
413 const TLevelWriterP &lw,
414 const std::vector<TFrameId> &frames,
415 const TRop::ResampleFilterType &resType, int width,
416 FrameTaskNotifier *frameNotifier) {
417 QString msg;
418 int i;
419 std::vector<TVectorImageP> images;
420 TRectD maxBbox;
421 for (i = 0; i < (int)frames.size();
422 i++) { // trovo la bbox che possa contenere tutte le immagini
423 try {
424 TImageReaderP ir = lr->getFrameReader(frames[i]);
425 TVectorImageP img = ir->load();
426 images.push_back(img);
427 maxBbox += img->getBBox();
428 } catch (...) {
429 msg = QObject::tr("Frame %1 : conversion failed!")
430 .arg(QString::fromStdString(frames[i].expand()));
431 DVGui::info(msg);
432 }
433 }
434 maxBbox = maxBbox.enlarge(2);
435 TAffine aff;
436 if (width) // calcolo l'affine
437 aff = TScale((double)width / maxBbox.getLx());
438 maxBbox = aff * maxBbox;
439
440 for (i = 0; i < (int)images.size(); i++) {
441 if (frameNotifier->abortTask()) break;
442 try {
443 TVectorImageP vectorImage = images[i];
444 assert(vectorImage);
445 if (vectorImage) {
446 // faccio il render dell'immagine
447 vectorImage->transform(aff, true);
448 const TVectorRenderData rd(TTranslation(-maxBbox.getP00()), TRect(),
449 plt.getPointer(), 0, true, true);
450 TOfflineGL *glContext = new TOfflineGL(convert(maxBbox).getSize());
451 glContext->clear(TPixel32::Transparent);
452 glContext->draw(vectorImage, rd);
453 TRaster32P rasImage = (glContext->getRaster());
454 TImageWriterP iw = lw->getFrameWriter(TFrameId(i + 1));
455 iw->save(TRasterImageP(rasImage));
456 }
457 } catch (...) {
458 msg = QObject::tr("Frame %1 : conversion failed!")
459 .arg(QString::fromStdString(frames[i].expand()));
460 DVGui::info(msg);
461 }
462 frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
463 }
464 }
465
466 //-----------------------------------------------------------------------
467
convertFromFullRaster(const TLevelReaderP & lr,const TLevelWriterP & lw,const std::vector<TFrameId> & _frames,const TAffine & aff,const TRop::ResampleFilterType & resType,FrameTaskNotifier * frameNotifier,const TPixel & bgColor,bool removeDotBeforeFrameNumber=false)468 static void convertFromFullRaster(
469 const TLevelReaderP &lr, const TLevelWriterP &lw,
470 const std::vector<TFrameId> &_frames, const TAffine &aff,
471 const TRop::ResampleFilterType &resType, FrameTaskNotifier *frameNotifier,
472 const TPixel &bgColor, bool removeDotBeforeFrameNumber = false) {
473 std::vector<TFrameId> frames = _frames;
474 if (frames.empty() && lr->loadInfo()->getFrameCount() ==
475 1) // e' una immagine singola, non un livello
476 frames.push_back(TFrameId());
477
478 for (int i = 0; i < (int)frames.size(); i++) {
479 if (frameNotifier->abortTask()) break;
480 try {
481 TRasterImageP img;
482 if (frames[i] == TFrameId()) // immagine singola, non livello
483 {
484 assert(frames.size() == 1);
485 TImageP _img;
486 if (!TImageReader::load(lr->getFilePath(), _img)) continue;
487 img = _img;
488 } else {
489 TImageReaderP ir = lr->getFrameReader(frames[i]);
490 img = ir->load();
491 }
492 TRaster32P raster(convert(aff * img->getBBox()).getSize());
493 if (!aff.isIdentity())
494 TRop::resample(raster, img->getRaster(), aff, resType);
495 else {
496 if ((TRaster32P)img->getRaster())
497 raster = img->getRaster();
498 else
499 TRop::convert(raster, img->getRaster());
500 }
501 if (bgColor != TPixel::Transparent) TRop::addBackground(raster, bgColor);
502
503 TRasterImageP newImg(raster);
504 double xDpi, yDpi;
505 img->getDpi(xDpi, yDpi);
506 if (xDpi == 0 && yDpi == 0)
507 xDpi = yDpi = Preferences::instance()->getDefLevelDpi();
508 newImg->setDpi(xDpi, yDpi);
509 if (frames[i] == TFrameId())
510 TImageWriter::save(lw->getFilePath(), newImg);
511 else {
512 /*---
513 ConvertPopup での指定に合わせて、[レベル名].[フレーム番号].[拡張子]
514 のうち、[レベル名]と[フレーム番号]の間のドットを消す。
515 例:A.0001.tga → A0001.tga になる。
516 ---*/
517 if (removeDotBeforeFrameNumber) {
518 TFilePath outPath = lw->getFilePath().withFrame(frames[i]);
519 /*--- フレーム番号と拡張子の文字数の合計。大抵8 ---*/
520 int index = 4 + 1 + outPath.getType().length();
521 std::wstring renamedStr = outPath.getWideString();
522 if (renamedStr[renamedStr.length() - index - 1] == L'.')
523 renamedStr = renamedStr.substr(0, renamedStr.length() - index - 1) +
524 renamedStr.substr(renamedStr.length() - index, index);
525 const TFilePath fp(renamedStr);
526 TImageWriterP writer(fp);
527 writer->setProperties(lw->getProperties());
528 writer->save(newImg);
529 } else {
530 TImageWriterP iw = lw->getFrameWriter(frames[i]);
531 iw->save(newImg);
532 }
533 }
534 } catch (...) {
535 // QString msg=QObject::tr("Frame %1 : conversion
536 // failed!").arg(QString::number(i+1));
537 // DVGui::info(msg);
538 }
539 /*--
540 * これはプログレスバーを進めるものなので、動画番号ではなく、完了したフレームの枚数を投げる
541 * --*/
542 frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
543 }
544 }
545
546 //-----------------------------------------------------------------------
547
convertFromVector(const TLevelReaderP & lr,const TLevelWriterP & lw,const std::vector<TFrameId> & _frames,FrameTaskNotifier * frameNotifier)548 static void convertFromVector(const TLevelReaderP &lr, const TLevelWriterP &lw,
549 const std::vector<TFrameId> &_frames,
550 FrameTaskNotifier *frameNotifier) {
551 std::vector<TFrameId> frames = _frames;
552 TLevelP lv = lr->loadInfo();
553 if (frames.empty() &&
554 lv->getFrameCount() == 1) // e' una immagine singola, non un livello
555 frames.push_back(TFrameId());
556
557 for (int i = 0; i < (int)frames.size(); i++) {
558 if (frameNotifier->abortTask()) break;
559 try {
560 TVectorImageP img;
561 if (frames[i] == TFrameId()) // immagine singola, non livello
562 {
563 assert(frames.size() == 1);
564 TImageP _img;
565 if (!TImageReader::load(lr->getFilePath(), _img)) continue;
566 img = _img;
567
568 } else {
569 TImageReaderP ir = lr->getFrameReader(frames[i]);
570 img = ir->load();
571 }
572
573 img->setPalette(lv->getPalette());
574
575 if (frames[i] == TFrameId())
576 TImageWriter::save(lw->getFilePath(), img);
577 else {
578 TImageWriterP iw = lw->getFrameWriter(frames[i]);
579 iw->save(img);
580 }
581 } catch (...) {
582 // QString msg=QObject::tr("Frame %1 : conversion
583 // failed!").arg(QString::number(i+1));
584 // DVGui::info(msg);
585 }
586 frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
587 }
588 }
589
590 //-----------------------------------------------------------------------
591
convert(const TFilePath & source,const TFilePath & dest,const TFrameId & from,const TFrameId & to,double framerate,TPropertyGroup * prop,FrameTaskNotifier * frameNotifier,const TPixel & bgColor,bool removeDotBeforeFrameNumber)592 void convert(const TFilePath &source, const TFilePath &dest,
593 const TFrameId &from, const TFrameId &to, double framerate,
594 TPropertyGroup *prop, FrameTaskNotifier *frameNotifier,
595 const TPixel &bgColor, bool removeDotBeforeFrameNumber) {
596 std::string dstExt = dest.getType(), srcExt = source.getType();
597
598 // Load source level structure
599 TLevelReaderP lr(source);
600 TLevelP level = lr->loadInfo();
601
602 #ifdef _WIN32
603 if (dstExt == "avi") {
604 TDimension res(0, 0);
605
606 const TImageInfo *info = lr->getImageInfo(level->begin()->first);
607 res.lx = info->m_lx;
608 res.ly = info->m_ly;
609
610 std::string codecName = prop->getProperty(0)->getValueAsString();
611 if (!AviCodecRestrictions::canWriteMovie(::to_wstring(codecName), res)) {
612 return;
613 }
614 }
615 #endif
616
617 // Get the frames available in level inside the [from, to] range
618 std::vector<TFrameId> frames;
619 getFrameIds(from, to, level, frames);
620
621 // Write the destination level
622 TLevelWriterP lw(dest, prop);
623 lw->setFrameRate(framerate);
624
625 if (srcExt == "tlv")
626 convertFromCM(lr, level->getPalette(), lw, frames, TAffine(),
627 TRop::Triangle, frameNotifier, bgColor,
628 removeDotBeforeFrameNumber);
629 else if (srcExt == "pli")
630 // assert(!"Conversion from pli files is currently diabled");
631 convertFromVector(lr, lw, frames, frameNotifier);
632 else
633 convertFromFullRaster(lr, lw, frames, TAffine(), TRop::Triangle,
634 frameNotifier, bgColor, removeDotBeforeFrameNumber);
635 }
636
637 //=============================================================================
638
convertNaa2Tlv(const TFilePath & source,const TFilePath & dest,const TFrameId & from,const TFrameId & to,FrameTaskNotifier * frameNotifier,TPalette * palette,bool removeUnusedStyles,double dpi)639 void convertNaa2Tlv(const TFilePath &source, const TFilePath &dest,
640 const TFrameId &from, const TFrameId &to,
641 FrameTaskNotifier *frameNotifier, TPalette *palette,
642 bool removeUnusedStyles, double dpi) {
643 std::string dstExt = dest.getType(), srcExt = source.getType();
644
645 // Load source level structure
646 TLevelReaderP lr(source);
647 TLevelP level = lr->loadInfo();
648
649 // Get the frames available in level inside the [from, to] range
650 std::vector<TFrameId> frames;
651 getFrameIds(from, to, level, frames);
652
653 if (frames.empty()) return;
654
655 // Write the destination level
656 TLevelWriterP lw; // Initialized just before a sure write
657
658 Naa2TlvConverter converter;
659 converter.setPalette(palette);
660
661 QList<int> usedStyleIds({0});
662
663 int f, fCount = int(frames.size());
664 for (f = 0; f != fCount; ++f) {
665 if (frameNotifier->abortTask()) break;
666
667 try {
668 TImageReaderP ir = lr->getFrameReader(frames[f]);
669 TImageP img = ir->load();
670
671 if (!img) continue;
672
673 TRasterImageP ri = img;
674 if (!ri) continue;
675
676 TRaster32P raster =
677 ri->getRaster(); // Converts only 32-bit images, it seems...
678 if (!raster) //
679 continue; //
680
681 converter.process(raster);
682
683 if (TToonzImageP dstImg = converter.makeTlv(
684 false, usedStyleIds, dpi)) // Opaque synthetic inks
685 {
686 if (converter.getPalette() == 0)
687 converter.setPalette(dstImg->getPalette());
688
689 if (!lw) // Initialize output file only on a sure write
690 lw = TLevelWriterP(dest); //
691
692 TImageWriterP iw = lw->getFrameWriter(frames[f]);
693 iw->save(dstImg);
694 } else {
695 DVGui::warning(QObject::tr(
696 "The source image seems not suitable for this kind of conversion"));
697 frameNotifier->notifyError();
698 }
699 } catch (...) {
700 }
701
702 frameNotifier->notifyFrameCompleted(100 * (f + 1) / frames.size());
703 }
704
705 if (removeUnusedStyles) converter.removeUnusedStyles(usedStyleIds);
706 }
707
708 //=============================================================================
709
convertOldLevel2Tlv(const TFilePath & source,const TFilePath & dest,const TFrameId & from,const TFrameId & to,FrameTaskNotifier * frameNotifier)710 void convertOldLevel2Tlv(const TFilePath &source, const TFilePath &dest,
711 const TFrameId &from, const TFrameId &to,
712 FrameTaskNotifier *frameNotifier) {
713 TFilePath destPltPath = TFilePath(dest.getParentDir().getWideString() +
714 L"\\" + dest.getWideName() + L".tpl");
715 if (TSystem::doesExistFileOrLevel(destPltPath))
716 TSystem::removeFileOrLevel(destPltPath);
717
718 TLevelWriterP lw(dest);
719 lw->setIconSize(Preferences::instance()->getIconSize());
720 TPaletteP palette =
721 ToonzImageUtils::loadTzPalette(source.withType("plt").withNoFrame());
722 TLevelReaderP lr(source);
723 if (!lr) {
724 DVGui::warning(QObject::tr(
725 "The source image seems not suitable for this kind of conversion"));
726 frameNotifier->notifyError();
727 return;
728 }
729 TLevelP inLevel = lr->loadInfo();
730 if (!inLevel || inLevel->getFrameCount() == 0) {
731 DVGui::warning(QObject::tr(
732 "The source image seems not suitable for this kind of conversion"));
733 frameNotifier->notifyError();
734 return;
735 }
736 // Get the frames available in level inside the [from, to] range
737 std::vector<TFrameId> frames;
738 getFrameIds(from, to, inLevel, frames);
739 if (frames.empty()) return;
740
741 TLevelP outLevel;
742 outLevel->setPalette(palette.getPointer());
743 try {
744 int f, fCount = int(frames.size());
745 for (f = 0; f != fCount; ++f) {
746 if (frameNotifier->abortTask()) break;
747 TToonzImageP img = lr->getFrameReader(frames[f])->load();
748 if (!img) continue;
749 img->setPalette(palette.getPointer());
750 lw->getFrameWriter(frames[f])->save(img);
751
752 frameNotifier->notifyFrameCompleted(100 * (f + 1) / frames.size());
753 }
754 } catch (TException &e) {
755 QString msg = QString::fromStdWString(e.getMessage());
756 DVGui::warning(msg);
757 lw = TLevelWriterP();
758 if (TSystem::doesExistFileOrLevel(dest)) TSystem::removeFileOrLevel(dest);
759 frameNotifier->notifyError();
760 return;
761 }
762 lw = TLevelWriterP();
763 }
764
765 //=============================================================================
766
767 #define ZOOMLEVELS 13
768 #define NOZOOMINDEX 6
769 double ZoomFactors[ZOOMLEVELS] = {
770 0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64};
771
getQuantizedZoomFactor(double zf,bool forward)772 double getQuantizedZoomFactor(double zf, bool forward) {
773 if (forward && (zf > ZoomFactors[ZOOMLEVELS - 1] ||
774 areAlmostEqual(zf, ZoomFactors[ZOOMLEVELS - 1], 1e-5)))
775 return zf;
776 else if (!forward &&
777 (zf < ZoomFactors[0] || areAlmostEqual(zf, ZoomFactors[0], 1e-5)))
778 return zf;
779
780 assert((!forward && zf > ZoomFactors[0]) ||
781 (forward && zf < ZoomFactors[ZOOMLEVELS - 1]));
782 int i = 0;
783 for (i = 0; i <= ZOOMLEVELS - 1; i++)
784 if (areAlmostEqual(zf, ZoomFactors[i], 1e-5)) zf = ZoomFactors[i];
785
786 if (forward && zf < ZoomFactors[0])
787 return ZoomFactors[0];
788 else if (!forward && zf > ZoomFactors[ZOOMLEVELS - 1])
789 return ZoomFactors[ZOOMLEVELS - 1];
790
791 for (i = 0; i < ZOOMLEVELS - 1; i++)
792 if (ZoomFactors[i + 1] - zf >= 0 && zf - ZoomFactors[i] >= 0) {
793 if (forward && ZoomFactors[i + 1] == zf)
794 return ZoomFactors[i + 2];
795 else if (!forward && ZoomFactors[i] == zf)
796 return ZoomFactors[i - 1];
797 else
798 return forward ? ZoomFactors[i + 1] : ZoomFactors[i];
799 }
800 return ZoomFactors[NOZOOMINDEX];
801 }
802
803 } // namespace ImageUtils
804
805 namespace {
806
getViewerShortcuts(int & zoomIn,int & zoomOut,int & viewReset,int & zoomFit,int & showHideFullScreen,int & actualPixelSize,int & flipX,int & flipY,int & zoomReset,int & rotateReset,int & positionReset)807 void getViewerShortcuts(int &zoomIn, int &zoomOut, int &viewReset, int &zoomFit,
808 int &showHideFullScreen, int &actualPixelSize,
809 int &flipX, int &flipY, int &zoomReset,
810 int &rotateReset, int &positionReset) {
811 CommandManager *cManager = CommandManager::instance();
812
813 zoomIn = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomIn));
814 zoomOut =
815 cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomOut));
816 viewReset =
817 cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ViewReset));
818 zoomFit =
819 cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomFit));
820 showHideFullScreen = cManager->getKeyFromShortcut(
821 cManager->getShortcutFromId(V_ShowHideFullScreen));
822 actualPixelSize = cManager->getKeyFromShortcut(
823 cManager->getShortcutFromId(V_ActualPixelSize));
824 flipX = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_FlipX));
825 flipY = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_FlipY));
826 zoomReset =
827 cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomReset));
828 rotateReset =
829 cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_RotateReset));
830 positionReset = cManager->getKeyFromShortcut(
831 cManager->getShortcutFromId(V_PositionReset));
832 }
833
834 } // namespace
835
836 //--------------------------------------------------------------------------
837
838 namespace ImageUtils {
839
ShortcutZoomer(QWidget * zoomingWidget)840 ShortcutZoomer::ShortcutZoomer(QWidget *zoomingWidget)
841 : m_widget(zoomingWidget) {}
842
843 //--------------------------------------------------------------------------
844
exec(QKeyEvent * event)845 bool ShortcutZoomer::exec(QKeyEvent *event) {
846 int zoomInKey, zoomOutKey, viewResetKey, zoomFitKey, showHideFullScreenKey,
847 actualPixelSize, flipX, flipY, zoomReset, rotateReset, positionReset;
848 getViewerShortcuts(zoomInKey, zoomOutKey, viewResetKey, zoomFitKey,
849 showHideFullScreenKey, actualPixelSize, flipX, flipY,
850 zoomReset, rotateReset, positionReset);
851
852 int key = event->key();
853 if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Alt)
854 return false;
855
856 key = key | event->modifiers() &
857 (~0xf0000000); // Ignore if the key is a numpad key
858
859 return (key == showHideFullScreenKey)
860 ? toggleFullScreen()
861 : (key == Qt::Key_Escape)
862 ? toggleFullScreen(true)
863 : (key == actualPixelSize)
864 ? setActualPixelSize()
865 : (key == zoomFitKey)
866 ? fit()
867 : (key == zoomInKey || key == zoomOutKey ||
868 key == viewResetKey)
869 ? zoom(key == zoomInKey,
870 key == viewResetKey)
871 : (key == flipX)
872 ? setFlipX()
873 : (key == flipY)
874 ? setFlipY()
875 : (key == zoomReset)
876 ? resetZoom()
877 : (key == rotateReset)
878 ? resetRotation()
879 : (key ==
880 positionReset)
881 ? resetPosition()
882 : false;
883 ;
884 }
885
886 //*********************************************************************************************
887 // FullScreenWidget implementation
888 //*********************************************************************************************
889
FullScreenWidget(QWidget * parent)890 FullScreenWidget::FullScreenWidget(QWidget *parent) : QWidget(parent) {
891 QHBoxLayout *layout = new QHBoxLayout(this);
892 layout->setMargin(0);
893 layout->setSpacing(0);
894
895 setLayout(layout);
896 }
897
898 //---------------------------------------------------------------------------------
899
setWidget(QWidget * widget)900 void FullScreenWidget::setWidget(QWidget *widget) {
901 QLayout *layout = this->layout();
902
903 delete layout->takeAt(0);
904 if ((m_widget = widget)) layout->addWidget(m_widget);
905 }
906
907 //=============================================================
toggleFullScreen(const bool kfApplicationQuitInProgress)908 bool FullScreenWidget::toggleFullScreen(
909 //=============================================================
910
911 const bool kfApplicationQuitInProgress) // Indicates whether the
912 // application is quiting.
913
914 /*
915 * DESCRIPTION:
916 *
917 * Sets the size and location of the widget to either full screen or normal.
918 *
919 * Entering full screen has to be done manually in order to avoid having the
920 * window placed on the wrong monitor on systems running X11 with multiple
921 * monitors.
922 */
923 {
924 // Initialize the return value.
925 bool fFullScreenStateToggled = false;
926
927 // Define some constants for setting and clearing window flags.
928 const Qt::WindowFlags kwfFullScreenWidgetFlags =
929 Qt::Window | // <-- Make the widget become a window.
930 Qt::FramelessWindowHint; // <-- Full screen windows have no border.
931
932 const Qt::WindowFlags kwfFullScreenWidgetExcludedFlagsMask =
933 (Qt::WindowFlags)~Qt::WindowTitleHint; // <-- Full screen windows have no
934 // titlebar.
935
936 // Determine whether to enter or leave full screen mode
937 if (this->windowState() & Qt::WindowFullScreen) {
938 this->hide();
939
940 this->setWindowFlags(this->windowFlags() & ~kwfFullScreenWidgetFlags);
941
942 this->showNormal();
943
944 this->m_widget->setFocus();
945
946 // Set the return value to indicate that the full screen mode has been
947 // changed.
948 fFullScreenStateToggled = true;
949 } else {
950 // There's no point to switching into full screen if the
951 // application is in the process of quiting.
952 if (!kfApplicationQuitInProgress) {
953 //==============================================================
954 //
955 // NOTE:
956 //
957 // This new way of going into full screen mode does things manually in
958 // order to bypass Qt's incorrect policy of always relocating a widget
959 // to desktop coordinates (0,0) when the widget becomes a window via
960 // setting the Qt::Window flag.
961 //
962 // This makes full screen mode work MUCH better on X11 systems with
963 // multiple displays.
964 //
965 //==============================================================
966 //
967 // STRATEGY:
968 //
969 // 1. Obtain the rectangle of the screen that the widgets host
970 // window is on. This has to be done first, otherwise the CORRECT
971 // screen info can potentially become unavailable in the following
972 // steps.
973 //
974 // 2. Manually set all the necessary flags for the full screen
975 // window attributes and state. Qt WILL hide the widget/window when
976 // this is done.
977 //
978 // 3. Set the window geometry/rect to be the same as the screen from
979 // Step 1.
980 //
981 // 4. Make the window visible again.
982 //
983 //---------------------------------------------------
984 // STEP 1:
985
986 // Get the window widget that contains this widget.
987 QWidget *ptrWindowWidget = this->window();
988 if (ptrWindowWidget) {
989 // Get the access to the QWindow object of the containing window.
990 QWindow *ptrContainingQWindow = ptrWindowWidget->windowHandle();
991 if (ptrContainingQWindow) {
992 // Get access to the screen the window is on.
993 QScreen *ptrScreenThisWindowIsOn = ptrContainingQWindow->screen();
994 if (ptrScreenThisWindowIsOn) {
995 #if !defined(_WIN32)
996 // Get the geometry rect for the correct screen.
997 QRect qrcScreen = ptrScreenThisWindowIsOn->geometry();
998
999 //---------------------------------------------------
1000 // STEP 2:
1001
1002 // Set the window flags to be frameless and with no titlebar.
1003 //
1004 // This call will turn the widget into a "window", and HIDE it. This
1005 // is because Qt always hides a widget when it transforms a widget
1006 // into a window, or turns a window into a widget.
1007 //
1008 this->setWindowFlags(
1009 (this->windowFlags() & kwfFullScreenWidgetExcludedFlagsMask) |
1010 kwfFullScreenWidgetFlags);
1011
1012 // Set the window state flag to indicate that it's now in fullscreen
1013 // mode.
1014 // If this state flag isn't set, the test for whether to enter or
1015 // leave full screen mode won't work correctly.
1016 this->setWindowState(Qt::WindowFullScreen);
1017
1018 //---------------------------------------------------
1019 // STEP 3:
1020
1021 // Set the window to the geometry rect of the correct screen.
1022 this->setGeometry(qrcScreen);
1023
1024 //---------------------------------------------------
1025 // STEP 4:
1026
1027 // Make the window visible. This also causes all the changes to the
1028 // widget's flags, state and geometry that was just set to take
1029 // effect.
1030 this->show();
1031 #else
1032 this->setWindowFlags(this->windowFlags() | Qt::Window);
1033 this->window()->windowHandle()->setScreen(ptrScreenThisWindowIsOn);
1034
1035 // http://doc.qt.io/qt-5/windows-issues.html#fullscreen-opengl-based-windows
1036 this->winId();
1037 QWindowsWindowFunctions::setHasBorderInFullScreen(
1038 this->windowHandle(), true);
1039
1040 this->showFullScreen();
1041 #endif
1042 }
1043 }
1044 }
1045
1046 // Set the return value to indicate that the full screen mode has been
1047 // changed.
1048 fFullScreenStateToggled = true;
1049 }
1050 }
1051
1052 return (fFullScreenStateToggled);
1053 }
1054
1055 } // namespace ImageUtils
1056