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> &regInf) {
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> &regs,
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> &regInf,
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> &regs,
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> &regs) {
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