1 
2 
3 // TnzCore includes
4 #include "tfilepath.h"
5 #include "tfiletype.h"
6 #include "tstream.h"
7 #include "tsystem.h"
8 #include "timagecache.h"
9 #include "tpixelutils.h"
10 #include "tropcm.h"
11 #include "timageinfo.h"
12 #include "timage_io.h"
13 #include "tlevel_io.h"
14 #include "tofflinegl.h"
15 #include "tgl.h"
16 #include "tvectorrenderdata.h"
17 #include "tstroke.h"
18 #include "tthreadmessage.h"
19 #include "tpalette.h"
20 #include "trasterimage.h"
21 #include "tvectorimage.h"
22 #include "ttoonzimage.h"
23 #include "tmeshimage.h"
24 
25 // TnzExt includes
26 #include "ext/meshutils.h"
27 
28 // TnzLib includes
29 #include "toonz/toonzscene.h"
30 #include "toonz/sceneproperties.h"
31 #include "toonz/txsheet.h"
32 #include "toonz/tscenehandle.h"
33 #include "toonz/txshlevel.h"
34 #include "toonz/txshleveltypes.h"
35 #include "toonz/txshsimplelevel.h"
36 #include "toonz/txshchildlevel.h"
37 #include "toonz/tstageobjectspline.h"
38 #include "toonz/preferences.h"
39 #include "toonz/sceneresources.h"
40 #include "toonz/stage2.h"
41 
42 // TnzQt includes
43 #include "toonzqt/gutil.h"
44 
45 #include "toonzqt/icongenerator.h"
46 
47 //=============================================================================
48 
49 //===================================
50 //
51 //    Local namespace
52 //
53 //-----------------------------------
54 
55 namespace {
56 const TDimension IconSize(80, 60);
57 TDimension FilmstripIconSize(0, 0);
58 
59 // Access name-based storage
60 std::set<std::string> iconsMap;
61 typedef std::set<std::string>::iterator IconIterator;
62 
63 //-----------------------------------------------------------------------------
64 
65 // Returns true if the image request was already submitted.
getIcon(const std::string & iconName,QPixmap & pix,TXshSimpleLevel * simpleLevel=0,TDimension standardSize=TDimension (0,0))66 bool getIcon(const std::string &iconName, QPixmap &pix,
67              TXshSimpleLevel *simpleLevel = 0,
68              TDimension standardSize      = TDimension(0, 0)) {
69   IconIterator it;
70   it = iconsMap.find(iconName);
71 
72   if (it != iconsMap.end()) {
73     TImageP im         = TImageCache::instance()->get(iconName, false);
74     TToonzImage *timgp = dynamic_cast<TToonzImage *>(im.getPointer());
75 
76     if (simpleLevel && timgp) {
77       IconGenerator::Settings settings =
78           IconGenerator::instance()->getSettings();
79 
80       TRaster32P icon(timgp->getSize());
81       icon->clear();
82       icon->fill((settings.m_blackBgCheck) ? TPixel::Black : TPixel::White);
83       if (settings.m_transparencyCheck || settings.m_inkIndex != -1 ||
84           settings.m_paintIndex != -1) {
85         TRop::CmappedQuickputSettings s;
86         s.m_globalColorScale  = TPixel32::Black;
87         s.m_inksOnly          = false;
88         s.m_transparencyCheck = settings.m_transparencyCheck;
89         s.m_blackBgCheck      = settings.m_blackBgCheck;
90         s.m_inkIndex          = settings.m_inkIndex;
91         s.m_paintIndex        = settings.m_paintIndex;
92         Preferences::instance()->getTranspCheckData(
93             s.m_transpCheckBg, s.m_transpCheckInk, s.m_transpCheckPaint);
94 
95         TRop::quickPut(icon, timgp->getRaster(), simpleLevel->getPalette(),
96                        TAffine(), s);
97       } else
98         TRop::quickPut(icon, timgp->getRaster(), simpleLevel->getPalette(),
99                        TAffine());
100       pix = rasterToQPixmap(icon, false);
101       return true;
102     }
103     TRasterImageP img = static_cast<TRasterImageP>(im);
104 
105     if (!img) {
106       pix = QPixmap();
107       return true;
108     }
109     assert(!(TRasterGR8P)img->getRaster());
110     const TRaster32P &ras = img->getRaster();
111     bool isHighDpi        = false;
112     // If the icon raster obtained in higher resolution than the standard
113     // icon size, it may be icon displayed in high dpi monitors.
114     // In such case set the device pixel ratio to the pixmap.
115     // Note that the humbnails of regular levels are standardsize even if
116     // they are displayed in high dpi monitors for now.
117     if (standardSize != TDimension(0, 0) &&
118         ras->getSize().lx > standardSize.lx &&
119         ras->getSize().ly > standardSize.ly)
120       isHighDpi = true;
121     pix         = rasterToQPixmap(ras, false, isHighDpi);
122     return true;
123   }
124 
125   return false;
126 }
127 
128 //-----------------------------------------------------------------------------
129 
setIcon(const std::string & iconName,const TRaster32P & icon)130 void setIcon(const std::string &iconName, const TRaster32P &icon) {
131   if (iconsMap.find(iconName) != iconsMap.end())
132     TImageCache::instance()->add(iconName, TRasterImageP(icon), true);
133 }
134 
135 //-----------------------------------------------------------------------------
136 /*! Cache icon data in TToonzImage format if ToonzImageIconRenderer generates
137  * them
138  */
setIcon_TnzImg(const std::string & iconName,const TRasterCM32P & icon)139 void setIcon_TnzImg(const std::string &iconName, const TRasterCM32P &icon) {
140   if (iconsMap.find(iconName) != iconsMap.end())
141     TImageCache::instance()->add(
142         iconName, TToonzImageP(icon, TRect(icon->getSize())), true);
143 }
144 
145 //-----------------------------------------------------------------------------
146 
removeIcon(const std::string & iconName)147 void removeIcon(const std::string &iconName) {
148   IconIterator it;
149   it = iconsMap.find(iconName);
150   if (it != iconsMap.end()) {
151     TImageCache::instance()->remove(iconName);
152   }
153   iconsMap.erase(iconName);
154 }
155 
156 //-----------------------------------------------------------------------------
157 
isUnpremultiplied(const TRaster32P & r)158 bool isUnpremultiplied(const TRaster32P &r) {
159   int lx = r->getLx();
160   int y  = r->getLy();
161   r->lock();
162   while (--y >= 0) {
163     TPixel32 *pix    = r->pixels(y);
164     TPixel32 *endPix = pix + lx;
165     while (pix < endPix) {
166       if (pix->r > pix->m || pix->g > pix->m || pix->b > pix->m) {
167         r->unlock();
168         return true;
169       }
170       ++pix;
171     }
172   }
173   r->unlock();
174   return false;
175 }
176 
177 //-----------------------------------------------------------------------------
178 
makeChessBackground(const TRaster32P & ras)179 void makeChessBackground(const TRaster32P &ras) {
180   ras->lock();
181 
182   const TPixel32 gray1(230, 230, 230, 255), gray2(180, 180, 180, 255);
183 
184   int lx = ras->getLx(), ly = ras->getLy();
185   for (int y = 0; y != ly; ++y) {
186     TPixel32 *pix = ras->pixels(y), *lineEnd = pix + lx;
187 
188     int yCol = (y & 4);
189 
190     for (int x                = 0; pix != lineEnd; ++x, ++pix)
191       if (pix->m != 255) *pix = overPix((x & 4) == yCol ? gray1 : gray2, *pix);
192   }
193 
194   ras->unlock();
195 }
196 
197 }  // namespace
198 
199 //=============================================================================
200 
201 //==========================================
202 //
203 //    Image-to-Icon convertion methods
204 //
205 //------------------------------------------
206 
207 namespace {
convertToIcon(TVectorImageP vimage,int frame,const TDimension & iconSize,const IconGenerator::Settings & settings)208 TRaster32P convertToIcon(TVectorImageP vimage, int frame,
209                          const TDimension &iconSize,
210                          const IconGenerator::Settings &settings) {
211   if (!vimage) return TRaster32P();
212 
213   TPalette *plt = vimage->getPalette()->clone();
214   if (!plt) return TRaster32P();
215   plt->setFrame(frame);
216 
217   TOfflineGL *glContext = IconGenerator::instance()->getOfflineGLContext();
218 
219   // The image and contained within Imagebox
220   // (add a small margin also to prevent problems with empty images)
221   TRectD imageBox;
222   {
223     QMutexLocker sl(vimage->getMutex());
224     imageBox = vimage->getBBox().enlarge(.1);
225   }
226   TPointD imageCenter = (imageBox.getP00() + imageBox.getP11()) * 0.5;
227 
228   // Calculate a transformation matrix that moves the image inside the icon.
229   // The scale factor is chosen so that the image is entirely
230   // contained in the icon (with a margin of 'margin' pixels)
231   const int margin = 10;
232   double scx       = (iconSize.lx - margin) / imageBox.getLx();
233   double scy       = (iconSize.ly - margin) / imageBox.getLy();
234   double sc        = std::min(scx, scy);
235   // Add the translation: the center point of the image is at the point
236   // middle of the pixmap.
237   TPointD iconCenter(iconSize.lx * 0.5, iconSize.ly * 0.5);
238   TAffine aff = TScale(sc).place(imageCenter, iconCenter);
239 
240   // RenderData
241   TVectorRenderData rd(aff, TRect(iconSize), plt, 0, true);
242 
243   rd.m_tcheckEnabled     = settings.m_transparencyCheck;
244   rd.m_blackBgEnabled    = settings.m_blackBgCheck;
245   rd.m_drawRegions       = !settings.m_inksOnly;
246   rd.m_inkCheckEnabled   = settings.m_inkIndex != -1;
247   rd.m_paintCheckEnabled = settings.m_paintIndex != -1;
248   rd.m_colorCheckIndex =
249       rd.m_inkCheckEnabled ? settings.m_inkIndex : settings.m_paintIndex;
250   rd.m_isIcon = true;
251 
252   // Draw the image.
253   glContext->makeCurrent();
254   glContext->clear(rd.m_blackBgEnabled ? TPixel::Black : TPixel32::White);
255   glContext->draw(vimage, rd);
256 
257   TRaster32P ras(iconSize);
258   glContext->getRaster(ras);
259 
260   glContext->doneCurrent();
261 
262   delete plt;
263 
264   return ras;
265 }
266 
267 //-------------------------------------------------------------------------
268 
convertToIcon(TToonzImageP timage,int frame,const TDimension & iconSize,const IconGenerator::Settings & settings)269 TRaster32P convertToIcon(TToonzImageP timage, int frame,
270                          const TDimension &iconSize,
271                          const IconGenerator::Settings &settings) {
272   if (!timage) return TRaster32P();
273 
274   TPalette *plt = timage->getPalette();
275   if (!plt) return TRaster32P();
276 
277   plt->setFrame(frame);
278 
279   TRasterCM32P rasCM32 = timage->getRaster();
280   if (!rasCM32.getPointer()) return TRaster32P();
281 
282   int lx     = rasCM32->getSize().lx;
283   int ly     = rasCM32->getSize().ly;
284   int iconLx = iconSize.lx, iconLy = iconSize.ly;
285   if (std::max(double(lx) / iconSize.lx, double(ly) / iconSize.ly) ==
286       double(ly) / iconSize.ly)
287     iconLx = tround((double(lx) * iconSize.ly) / ly);
288   else
289     iconLy = tround((double(ly) * iconSize.lx) / lx);
290 
291   // icon size with correct aspect ratio
292   TDimension iconSize2 = TDimension(iconLx, iconLy);
293 
294   TRaster32P icon(iconSize2);
295   icon->clear();
296   icon->fill(settings.m_blackBgCheck ? TPixel::Black : TPixel::White);
297 
298   TDimension dim = rasCM32->getSize();
299   if (dim != iconSize2) {
300     TRasterCM32P auxCM32(icon->getSize());
301     auxCM32->clear();
302     TRop::makeIcon(auxCM32, rasCM32);
303     rasCM32 = auxCM32;
304   }
305 
306   if (settings.m_transparencyCheck || settings.m_inksOnly ||
307       settings.m_inkIndex != -1 || settings.m_paintIndex != -1) {
308     TRop::CmappedQuickputSettings s;
309     s.m_globalColorScale  = TPixel32::Black;
310     s.m_inksOnly          = settings.m_inksOnly;
311     s.m_transparencyCheck = settings.m_transparencyCheck;
312     s.m_blackBgCheck      = settings.m_blackBgCheck;
313     s.m_inkIndex          = settings.m_inkIndex;
314     s.m_paintIndex        = settings.m_paintIndex;
315     Preferences::instance()->getTranspCheckData(
316         s.m_transpCheckBg, s.m_transpCheckInk, s.m_transpCheckPaint);
317 
318     TRop::quickPut(icon, rasCM32, timage->getPalette(), TAffine(), s);
319   } else
320     TRop::quickPut(icon, rasCM32, timage->getPalette(), TAffine());
321 
322   assert(iconSize2.lx <= iconSize.lx && iconSize2.ly <= iconSize.ly);
323   TRaster32P outIcon(iconSize);
324   outIcon->clear();
325   int dx = (outIcon->getLx() - icon->getLx()) / 2;
326   int dy = (outIcon->getLy() - icon->getLy()) / 2;
327   assert(dx >= 0 && dy >= 0);
328   TRect box = outIcon->getBounds().enlarge(-dx, -dy);
329   TRop::copy(outIcon->extract(box), icon);
330 
331   return outIcon;
332 }
333 
334 //-------------------------------------------------------------------------
335 
convertToIcon(TRasterImageP rimage,const TDimension & iconSize)336 TRaster32P convertToIcon(TRasterImageP rimage, const TDimension &iconSize) {
337   if (!rimage) return TRaster32P();
338 
339   TRasterP ras = rimage->getRaster();
340 
341   if (!(TRaster32P)ras && !(TRasterGR8P)ras) return TRaster32P();
342 
343   if (ras->getSize() == iconSize) return ras;
344 
345   TRaster32P icon(iconSize);
346   icon->fill(TPixel32(235, 235, 235));
347 
348   double sx = (double)icon->getLx() / ras->getLx();
349   double sy = (double)icon->getLy() / ras->getLy();
350   double sc = sx < sy ? sx : sy;
351 
352   TAffine aff = TScale(sc).place(ras->getCenterD(), icon->getCenterD());
353   TRop::resample(icon, ras, aff, TRop::Bilinear);
354   TRop::addBackground(icon, TPixel32::White);
355 
356   return icon;
357 }
358 
359 //-------------------------------------------------------------------------
360 
convertToIcon(TMeshImageP mi,int frame,const TDimension & iconSize,const IconGenerator::Settings & settings)361 TRaster32P convertToIcon(TMeshImageP mi, int frame, const TDimension &iconSize,
362                          const IconGenerator::Settings &settings) {
363   if (!mi) return TRaster32P();
364 
365   TOfflineGL *glContext = IconGenerator::instance()->getOfflineGLContext();
366 
367   // The image and contained within Imagebox
368   // (add a small margin also to prevent problems with empty images)
369   TRectD imageBox;
370   imageBox = mi->getBBox().enlarge(.1);
371 
372   TPointD imageCenter(0.5 * (imageBox.getP00() + imageBox.getP11()));
373 
374   // Calculate a transformation matrix that moves the image inside the icon.
375   // The scale factor is chosen so that the image is entirely
376   // contained in the icon (with a margin of 'margin' pixels)
377   const int margin = 10;
378   double scx       = (iconSize.lx - margin) / imageBox.getLx();
379   double scy       = (iconSize.ly - margin) / imageBox.getLy();
380   double sc        = std::min(scx, scy);
381 
382   // Add the translation: the center point of the image is at the point
383   // middle of the pixmap.
384   TPointD iconCenter(iconSize.lx * 0.5, iconSize.ly * 0.5);
385   TAffine aff = TScale(sc).place(imageCenter, iconCenter);
386 
387   // Draw the image.
388   glContext->makeCurrent();
389   glContext->clear(settings.m_blackBgCheck ? TPixel::Black : TPixel32::White);
390 
391   glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
392   glEnable(GL_BLEND);
393   glEnable(GL_LINE_SMOOTH);
394 
395   glPushMatrix();
396   tglMultMatrix(aff);
397 
398   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
399 
400   glColor4f(0.0f, 1.0f, 0.0f, 0.7f);
401   tglDrawEdges(*mi);
402 
403   glPopMatrix();
404 
405   glPopAttrib();
406 
407   TRaster32P ras(iconSize);
408   glContext->getRaster(ras);
409 
410   glContext->doneCurrent();
411 
412   return ras;
413 }
414 
415 //-------------------------------------------------------------------------
416 
convertToIcon(TImageP image,int frame,const TDimension & iconSize,const IconGenerator::Settings & settings)417 TRaster32P convertToIcon(TImageP image, int frame, const TDimension &iconSize,
418                          const IconGenerator::Settings &settings) {
419   TRasterImageP ri(image);
420   if (ri) return convertToIcon(ri, iconSize);
421 
422   TToonzImageP ti(image);
423   if (ti) return convertToIcon(ti, frame, iconSize, settings);
424 
425   TVectorImageP vi(image);
426   if (vi) return convertToIcon(vi, frame, iconSize, settings);
427 
428   TMeshImageP mi(image);
429   if (mi) return convertToIcon(mi, frame, iconSize, settings);
430 
431   return TRaster32P();
432 }
433 
434 }  // namespace
435 
436 //=============================================================================
437 
438 //============================
439 //
440 //    IconRenderer class
441 //
442 //----------------------------
443 
444 class IconRenderer : public TThread::Runnable {
445   TRaster32P m_icon;
446   TDimension m_iconSize;
447   std::string m_id;
448 
449   bool m_started;
450   bool m_terminated;
451 
452 public:
453   IconRenderer(const std::string &id, const TDimension &iconSize);
454   virtual ~IconRenderer();
455 
456   void run() override = 0;
457 
setIcon(const TRaster32P & icon)458   void setIcon(const TRaster32P &icon) { m_icon = icon; }
getIcon() const459   TRaster32P getIcon() const { return m_icon; }
460 
getIconSize()461   TDimension getIconSize() { return m_iconSize; }
getId() const462   const std::string &getId() const { return m_id; }
463 
hasStarted()464   bool &hasStarted() { return m_started; }
wasTerminated()465   bool &wasTerminated() { return m_terminated; }
466 };
467 
468 //-----------------------------------------------------------------------------
469 
IconRenderer(const std::string & id,const TDimension & iconSize)470 IconRenderer::IconRenderer(const std::string &id, const TDimension &iconSize)
471     : m_icon()
472     , m_iconSize(iconSize)
473     , m_id(id)
474     , m_started(false)
475     , m_terminated(false) {
476   connect(this, SIGNAL(started(TThread::RunnableP)), IconGenerator::instance(),
477           SLOT(onStarted(TThread::RunnableP)));
478   connect(this, SIGNAL(finished(TThread::RunnableP)), IconGenerator::instance(),
479           SLOT(onFinished(TThread::RunnableP)));
480   connect(this, SIGNAL(canceled(TThread::RunnableP)), IconGenerator::instance(),
481           SLOT(onCanceled(TThread::RunnableP)), Qt::QueuedConnection);
482   connect(this, SIGNAL(terminated(TThread::RunnableP)),
483           IconGenerator::instance(), SLOT(onTerminated(TThread::RunnableP)),
484           Qt::QueuedConnection);
485 }
486 
487 //-----------------------------------------------------------------------------
488 
~IconRenderer()489 IconRenderer::~IconRenderer() {}
490 
491 //=============================================================================
492 
493 //===================================
494 //
495 //    Specific icon renderers
496 //
497 //-----------------------------------
498 
499 //=============================================================================
500 
501 //======================================
502 //    VectorImageIconRenderer class
503 //--------------------------------------
504 
505 class VectorImageIconRenderer final : public IconRenderer {
506   TVectorImageP m_vimage;
507   TXshSimpleLevelP m_sl;
508   TFrameId m_fid;
509   IconGenerator::Settings m_settings;
510 
511 public:
VectorImageIconRenderer(const std::string & id,const TDimension & iconSize,TXshSimpleLevelP sl,const TFrameId & fid,const IconGenerator::Settings & settings)512   VectorImageIconRenderer(const std::string &id, const TDimension &iconSize,
513                           TXshSimpleLevelP sl, const TFrameId &fid,
514                           const IconGenerator::Settings &settings)
515       : IconRenderer(id, iconSize)
516       , m_vimage()
517       , m_sl(sl)
518       , m_fid(fid)
519       , m_settings(settings) {}
520 
VectorImageIconRenderer(const std::string & id,const TDimension & iconSize,TVectorImageP vimage,const IconGenerator::Settings & settings)521   VectorImageIconRenderer(const std::string &id, const TDimension &iconSize,
522                           TVectorImageP vimage,
523                           const IconGenerator::Settings &settings)
524       : IconRenderer(id, iconSize)
525       , m_vimage(vimage)
526       , m_sl(0)
527       , m_fid(-1)
528       , m_settings(settings) {}
529 
530   TRaster32P generateRaster(const TDimension &iconSize) const;
531   void run() override;
532 };
533 
534 //-----------------------------------------------------------------------------
535 
generateRaster(const TDimension & iconSize) const536 TRaster32P VectorImageIconRenderer::generateRaster(
537     const TDimension &iconSize) const {
538   TVectorImageP vimage;
539 
540   int frame = 0;
541   if (!m_vimage) {
542     assert(m_sl);
543     if (!m_sl->isFid(m_fid)) return TRaster32P();
544     TImageP image = m_sl->getFrameIcon(m_fid);
545     if (!image) return TRaster32P();
546     vimage = (TVectorImageP)image;
547     if (!vimage) return TRaster32P();
548     frame = m_sl->guessIndex(m_fid);
549   } else
550     vimage = m_vimage;
551   assert(vimage);
552 
553   TRaster32P ras(convertToIcon(vimage, frame, iconSize, m_settings));
554 
555   return ras;
556 }
557 
558 //-----------------------------------------------------------------------------
559 
run()560 void VectorImageIconRenderer::run() {
561   try {
562     TRaster32P ras(generateRaster(getIconSize()));
563 
564     if (ras) setIcon(ras);
565   } catch (...) {
566   }
567 }
568 
569 //=============================================================================
570 
571 //======================================
572 //    SplineImageIconRenderer class
573 //--------------------------------------
574 
575 class SplineIconRenderer final : public IconRenderer {
576   TStageObjectSpline *m_spline;
577 
578 public:
SplineIconRenderer(const std::string & id,const TDimension & iconSize,TStageObjectSpline * spline)579   SplineIconRenderer(const std::string &id, const TDimension &iconSize,
580                      TStageObjectSpline *spline)
581       : IconRenderer(id, iconSize), m_spline(spline) {}
582 
583   TRaster32P generateRaster(const TDimension &iconSize) const;
584   void run() override;
585 };
586 
587 //-----------------------------------------------------------------------------
588 
generateRaster(const TDimension & iconSize) const589 TRaster32P SplineIconRenderer::generateRaster(
590     const TDimension &iconSize) const {
591   // get the glContext
592   TOfflineGL *glContext = IconGenerator::instance()->getOfflineGLContext();
593   glContext->makeCurrent();
594   glContext->clear(TPixel32::White);
595 
596   const TStroke *stroke = m_spline->getStroke();
597   assert(stroke);
598   if (!stroke) {
599     glContext->doneCurrent();
600     return TRaster32P();
601   }
602   TRectD sbbox = stroke->getBBox();
603 
604   glColor3d(0, 0, 0);
605   double scaleX = 1, scaleY = 1;
606   if (sbbox.getLx() > 0.0) scaleX = (double)iconSize.lx / sbbox.getLx();
607   if (sbbox.getLy() > 0.0) scaleY = (double)iconSize.ly / sbbox.getLy();
608   double scale                    = 0.8 * std::min(scaleX, scaleY);
609   TPointD centerStroke            = 0.5 * (sbbox.getP00() + sbbox.getP11());
610   TPointD centerPixmap(iconSize.lx * 0.5, iconSize.ly * 0.5);
611   glPushMatrix();
612   tglMultMatrix(TScale(scale).place(centerStroke, centerPixmap));
613   int n = 50;
614   glBegin(GL_LINE_STRIP);
615   for (int i = 0; i < n; i++)
616     tglVertex(stroke->getPoint((double)i / (double)(n - 1)));
617   glEnd();
618   glPopMatrix();
619 
620   TRaster32P ras(iconSize);
621   glContext->getRaster(ras);
622   glContext->doneCurrent();
623   return ras;
624 }
625 
626 //-----------------------------------------------------------------------------
627 
run()628 void SplineIconRenderer::run() {
629   TRaster32P raster = generateRaster(getIconSize());
630   if (raster) setIcon(raster);
631 }
632 
633 //=============================================================================
634 
635 //======================================
636 //    RasterImageIconRenderer class
637 //--------------------------------------
638 
639 class RasterImageIconRenderer final : public IconRenderer {
640   TXshSimpleLevelP m_sl;
641   TFrameId m_fid;
642 
643 public:
RasterImageIconRenderer(const std::string & id,const TDimension & iconSize,TXshSimpleLevelP sl,const TFrameId & fid)644   RasterImageIconRenderer(const std::string &id, const TDimension &iconSize,
645                           TXshSimpleLevelP sl, const TFrameId &fid)
646       : IconRenderer(id, iconSize), m_sl(sl), m_fid(fid) {}
647 
648   void run() override;
649 };
650 
651 //-----------------------------------------------------------------------------
652 
run()653 void RasterImageIconRenderer::run() {
654   if (!m_sl->isFid(m_fid)) return;
655 
656   TImageP image = m_sl->getFrameIcon(m_fid);
657   if (!image) return;
658 
659   TRasterImageP rimage = (TRasterImageP)image;
660   assert(rimage);
661 
662   TRaster32P icon(convertToIcon(rimage, getIconSize()));
663 
664   if (icon) setIcon(icon);
665 }
666 
667 //=============================================================================
668 
669 //======================================
670 //    ToonzImageIconRenderer class
671 //--------------------------------------
672 
673 class ToonzImageIconRenderer final : public IconRenderer {
674   TXshSimpleLevelP m_sl;
675   TFrameId m_fid;
676   IconGenerator::Settings m_settings;
677 
678   TRasterCM32P m_tnzImgIcon;
679 
680 public:
ToonzImageIconRenderer(const std::string & id,const TDimension & iconSize,TXshSimpleLevelP sl,const TFrameId & fid,const IconGenerator::Settings & settings)681   ToonzImageIconRenderer(const std::string &id, const TDimension &iconSize,
682                          TXshSimpleLevelP sl, const TFrameId &fid,
683                          const IconGenerator::Settings &settings)
684       : IconRenderer(id, iconSize)
685       , m_sl(sl)
686       , m_fid(fid)
687       , m_settings(settings)
688       , m_tnzImgIcon(0) {}
689 
690   void run() override;
691 
setIcon_TnzImg(const TRasterCM32P & timgp)692   void setIcon_TnzImg(const TRasterCM32P &timgp) { m_tnzImgIcon = timgp; }
getIcon_TnzImg() const693   TRasterCM32P getIcon_TnzImg() const { return m_tnzImgIcon; }
694 };
695 
696 //-----------------------------------------------------------------------------
697 
run()698 void ToonzImageIconRenderer::run() {
699   if (!m_sl->isFid(m_fid)) return;
700 
701   TImageP image = m_sl->getFrameIcon(m_fid);
702   if (!image) return;
703 
704   TRasterImageP rimage(image);
705   if (rimage) {
706     TRaster32P icon(convertToIcon(rimage, getIconSize()));
707     if (icon) setIcon(icon);
708 
709     return;
710   }
711 
712   TToonzImageP timage = (TToonzImageP)image;
713 
714   TDimension iconSize(getIconSize());
715   if (!timage) {
716     TRaster32P p(iconSize.lx, iconSize.ly);
717     p->fill(TPixelRGBM32::Yellow);
718     setIcon(p);
719 
720     return;
721   }
722 
723   TRasterCM32P rasCM32 = timage->getRaster();
724   if (!rasCM32.getPointer()) return;
725 
726   int lx     = rasCM32->getSize().lx;
727   int ly     = rasCM32->getSize().ly;
728   int iconLx = iconSize.lx, iconLy = iconSize.ly;
729 
730   TRaster32P icon(iconSize);
731 
732   icon->fill(m_settings.m_blackBgCheck ? TPixel::Black : TPixel::White);
733 
734   if (lx != iconLx && ly != iconLy) {
735     // The icons stored in the tlv file don't have the required size.
736     // Fetch the original and iconize it.
737 
738     image = m_sl->getFrame(m_fid, ImageManager::dontPutInCache,
739                            0);  // 0 uses the level properties' subsampling
740     if (!image) return;
741 
742     timage = (TToonzImageP)image;
743     if (!timage) {
744       TRaster32P p(iconSize.lx, iconSize.ly);
745       p->fill(TPixelRGBM32::Yellow);
746       setIcon(p);
747 
748       return;
749     }
750 
751     rasCM32 = timage->getRaster();
752     if (!rasCM32.getPointer()) return;
753 
754     TRasterCM32P auxCM32(icon->getSize());
755     auxCM32->clear();
756 
757     TRop::makeIcon(auxCM32, rasCM32);
758     rasCM32 = auxCM32;
759   }
760 
761   if (!m_sl->getPalette()) return;
762 
763   TPaletteP plt = m_sl->getPalette()->clone();
764   if (!plt) return;
765 
766   int frame = m_sl->guessIndex(m_fid);
767   plt->setFrame(frame);
768 
769   setIcon_TnzImg(rasCM32);
770 }
771 
772 //=============================================================================
773 
774 //======================================
775 //    MeshImageIconRenderer class
776 //--------------------------------------
777 
778 class MeshImageIconRenderer final : public IconRenderer {
779   TMeshImageP m_image;
780   TXshSimpleLevelP m_sl;
781   TFrameId m_fid;
782   IconGenerator::Settings m_settings;
783 
784 public:
MeshImageIconRenderer(const std::string & id,const TDimension & iconSize,TXshSimpleLevelP sl,const TFrameId & fid,const IconGenerator::Settings & settings)785   MeshImageIconRenderer(const std::string &id, const TDimension &iconSize,
786                         TXshSimpleLevelP sl, const TFrameId &fid,
787                         const IconGenerator::Settings &settings)
788       : IconRenderer(id, iconSize)
789       , m_image()
790       , m_sl(sl)
791       , m_fid(fid)
792       , m_settings(settings) {}
793 
MeshImageIconRenderer(const std::string & id,const TDimension & iconSize,TMeshImageP image,const IconGenerator::Settings & settings)794   MeshImageIconRenderer(const std::string &id, const TDimension &iconSize,
795                         TMeshImageP image,
796                         const IconGenerator::Settings &settings)
797       : IconRenderer(id, iconSize)
798       , m_image(image)
799       , m_sl(0)
800       , m_fid(-1)
801       , m_settings(settings) {}
802 
803   TRaster32P generateRaster(const TDimension &iconSize) const;
804   void run() override;
805 };
806 
807 //-----------------------------------------------------------------------------
808 
generateRaster(const TDimension & iconSize) const809 TRaster32P MeshImageIconRenderer::generateRaster(
810     const TDimension &iconSize) const {
811   TMeshImageP mi;
812 
813   int frame = 0;
814   if (!m_image) {
815     assert(m_sl);
816     if (!m_sl->isFid(m_fid)) return TRaster32P();
817 
818     TImageP image = m_sl->getFrameIcon(m_fid);
819     if (!image) return TRaster32P();
820 
821     mi = (TMeshImageP)image;
822     if (!mi) return TRaster32P();
823 
824     frame = m_sl->guessIndex(m_fid);
825   } else
826     mi = m_image;
827 
828   assert(mi);
829 
830   return convertToIcon(mi, frame, iconSize, m_settings);
831 }
832 
833 //-----------------------------------------------------------------------------
834 
run()835 void MeshImageIconRenderer::run() {
836   try {
837     TRaster32P ras(generateRaster(getIconSize()));
838 
839     if (ras) setIcon(ras);
840   } catch (...) {
841   }
842 }
843 
844 //=============================================================================
845 
846 //==================================
847 //    XsheetIconRenderer class
848 //----------------------------------
849 
850 class XsheetIconRenderer final : public IconRenderer {
851   TXsheet *m_xsheet;
852   int m_row;
853 
854 public:
XsheetIconRenderer(const std::string & id,const TDimension & iconSize,TXsheet * xsheet,int row=0)855   XsheetIconRenderer(const std::string &id, const TDimension &iconSize,
856                      TXsheet *xsheet, int row = 0)
857       : IconRenderer(id, iconSize), m_xsheet(xsheet), m_row(row) {
858     if (m_xsheet) {
859       assert(m_xsheet->getRefCount() > 0);
860       m_xsheet->addRef();
861     }
862   }
863 
~XsheetIconRenderer()864   ~XsheetIconRenderer() {
865     if (m_xsheet) m_xsheet->release();
866   }
867 
getId(TXshChildLevel * level,int row)868   static std::string getId(TXshChildLevel *level, int row) {
869     return "sub:" + ::to_string(level->getName()) + std::to_string(row);
870   }
871 
872   TRaster32P generateRaster(const TDimension &iconSize) const;
873   void run() override;
874 };
875 
876 //-----------------------------------------------------------------------------
877 
generateRaster(const TDimension & iconSize) const878 TRaster32P XsheetIconRenderer::generateRaster(
879     const TDimension &iconSize) const {
880   ToonzScene *scene = m_xsheet->getScene();
881 
882   TRaster32P ras(iconSize);
883 
884   TPixel32 bgColor = scene->getProperties()->getBgColor();
885   bgColor.m        = 255;
886   ras->fill(bgColor);
887 
888   TImageCache::instance()->setEnabled(false);
889   // temporarily disable "Visualize Vector As Raster" option to prevent crash.
890   // (see the issue #2862)
891   bool rasterizePli               = TXshSimpleLevel::m_rasterizePli;
892   TXshSimpleLevel::m_rasterizePli = false;
893 
894   // All checks are disabled
895   scene->renderFrame(ras, m_row, m_xsheet, false);
896 
897   TXshSimpleLevel::m_rasterizePli = rasterizePli;
898   TImageCache::instance()->setEnabled(true);
899 
900   return ras;
901 }
902 
903 //-----------------------------------------------------------------------------
904 
run()905 void XsheetIconRenderer::run() {
906   TRaster32P ras = generateRaster(getIconSize());
907   if (ras) setIcon(ras);
908 }
909 
910 //=============================================================================
911 
912 //================================
913 //    FileIconRenderer class
914 //--------------------------------
915 
916 class FileIconRenderer final : public IconRenderer {
917   TFilePath m_path;
918   TFrameId m_fid;
919 
920 public:
FileIconRenderer(const TDimension & iconSize,const TFilePath & path,const TFrameId & fid)921   FileIconRenderer(const TDimension &iconSize, const TFilePath &path,
922                    const TFrameId &fid)
923       : IconRenderer(getId(path, fid), iconSize), m_path(path), m_fid(fid) {}
924 
925   static std::string getId(const TFilePath &path, const TFrameId &fid);
926 
927   void run() override;
928 };
929 
930 //-----------------------------------------------------------------------------
931 
getId(const TFilePath & path,const TFrameId & fid)932 std::string FileIconRenderer::getId(const TFilePath &path,
933                                     const TFrameId &fid) {
934   std::string type(path.getType());
935 
936   if (type == "tab" || type == "tnz" ||
937       type == "mesh" ||  // meshes are not currently viewable
938       TFileType::isViewable(TFileType::getInfo(path))) {
939     std::string fidNumber;
940     if (fid != TFrameId::NO_FRAME)
941       fidNumber = "frame:" + fid.expand(TFrameId::NO_PAD);
942     return "$:" + ::to_string(path) + fidNumber;
943   }
944 
945   // All the other types whose icon is the same for file type, get the same id
946   // per type.
947   else if (type == "tpl")
948     return "$:tpl";
949   else if (type == "tzp")
950     return "$:tzp";
951   else if (type == "svg")
952     return "$:svg";
953   else if (type == "tzu")
954     return "$:tzu";
955   else if (TFileType::getInfo(path) == TFileType::AUDIO_LEVEL)
956     return "$:audio";
957   else if (type == "scr")
958     return "$:scr";
959   else if (type == "mpath")
960     return "$:mpath";
961   else if (type == "curve")
962     return "$:curve";
963   else if (type == "cln")
964     return "$:cln";
965   else if (type == "tnzbat")
966     return "$:tnzbat";
967   else
968     return "$:unknown";
969 }
970 
971 //-----------------------------------------------------------------------------
972 
generateVectorFileIcon(const TFilePath & path,const TDimension & iconSize,const TFrameId & fid)973 TRaster32P IconGenerator::generateVectorFileIcon(const TFilePath &path,
974                                                  const TDimension &iconSize,
975                                                  const TFrameId &fid) {
976   TLevelReaderP lr(path);
977   TLevelP level = lr->loadInfo();
978   if (level->begin() == level->end()) return TRaster32P();
979   TFrameId frameId                       = fid;
980   if (fid == TFrameId::NO_FRAME) frameId = level->begin()->first;
981   TImageP img                            = lr->getFrameReader(frameId)->load();
982   TVectorImageP vi                       = img;
983   if (!vi) return TRaster32P();
984   vi->setPalette(level->getPalette());
985   VectorImageIconRenderer vir("", iconSize, vi.getPointer(),
986                               IconGenerator::Settings());
987   return vir.generateRaster(iconSize);
988 }
989 
990 //-----------------------------------------------------------------------------
991 
generateRasterFileIcon(const TFilePath & path,const TDimension & iconSize,const TFrameId & fid)992 TRaster32P IconGenerator::generateRasterFileIcon(const TFilePath &path,
993                                                  const TDimension &iconSize,
994                                                  const TFrameId &fid) {
995   TImageP img;
996 
997   try {
998     // Attempt image reading
999     TLevelReaderP lr(path);
1000     TLevelP level = lr->loadInfo();
1001 
1002     if (level->begin() == level->end()) return TRaster32P();
1003 
1004     TFrameId frameId = fid;
1005     if (fid == TFrameId::NO_FRAME)  // In case no frame was specified, pick the
1006       frameId = level->begin()->first;  // first level frame
1007 
1008     TImageReaderP ir = lr->getFrameReader(frameId);
1009 
1010     if (const TImageInfo *ii = ir->getImageInfo()) {
1011       int shrinkX = ii->m_lx / iconSize.lx;
1012       int shrinkY = ii->m_ly / iconSize.ly;
1013       int shrink  = shrinkX < shrinkY ? shrinkX : shrinkY;
1014 
1015       if (shrink > 1) ir->setShrink(shrink);
1016     }
1017 
1018     img = (toUpper(path.getType()) == "TLV") ? ir->loadIcon() : ir->load();
1019   } catch (...) {
1020   }
1021 
1022   // Extract a 32-bit fullcolor raster from img
1023   TRaster32P ras32;
1024 
1025   if (TRasterImageP ri = img) {
1026     ras32 = ri->getRaster();
1027 
1028     if (!ras32) {
1029       if (TRasterGR8P rasGR8 = ri->getRaster()) {
1030         TRaster32P raux(rasGR8->getSize());
1031         TRop::convert(raux, rasGR8);
1032         ras32 = raux;
1033       }
1034     }
1035   } else if (TToonzImageP ti = img) {
1036     TRasterCM32P auxRaster = ti->getRaster();
1037     TRaster32P dstRaster(auxRaster->getSize());
1038 
1039     if (TPaletteP plt = ti->getPalette())
1040       TRop::convert(dstRaster, auxRaster, plt, false);
1041     else
1042       dstRaster->fill(TPixel32::Magenta);
1043 
1044     ras32 = dstRaster;
1045   }
1046 
1047   if (!ras32) return TRaster32P();
1048 
1049   /*
1050 // NOTE: The following was possible with the old Qt version 4.3.3 - but in the
1051 new 4.5.0
1052 // it's not: 'It is not safe to use QPixmaps outside the GUI thread'...
1053 TRaster32P icon;
1054 {
1055 QPixmap p(rasterToQPixmap(ras32));
1056 icon = rasterFromQPixmap(
1057   scalePixmapKeepingAspectRatio(p, QSize(iconSize.lx, iconSize.ly),
1058 Qt::transparent)
1059   , false);
1060 }
1061 */
1062 
1063   TRaster32P icon(iconSize);
1064 
1065   double sx = double(iconSize.lx) / ras32->getLx();
1066   double sy = double(iconSize.ly) / ras32->getLy();
1067   double sc = std::min(sx, sy);  // show all the image, possibly adding bands
1068 
1069   TAffine aff = TScale(sc).place(ras32->getCenterD(), icon->getCenterD());
1070 
1071   icon->fill(TPixel32(255, 0, 0));  // "bands" color
1072   TRop::resample(icon, ras32, aff, TRop::Triangle);
1073 
1074   if (icon) {
1075     if (::isUnpremultiplied(icon))  // APPALLING. I'm not touching this, but
1076       TRop::premultiply(
1077           icon);  // YOU JUST CAN'T TELL IF AN IMAGE IS PREMULTIPLIED
1078                   // OR NOT BY SCANNING ITS PIXELS.
1079                   // You either know it FOR A GIVEN, or you don't...      >_<
1080     TRectI srcBBoxI = ras32->getBounds();
1081     TRectD srcBBoxD = aff * TRectD(srcBBoxI.x0, srcBBoxI.y0, srcBBoxI.x1 + 1,
1082                                    srcBBoxI.y1 + 1);
1083 
1084     TRect bbox = TRect(tfloor(srcBBoxD.x0), tceil(srcBBoxD.y0) - 1,
1085                        tfloor(srcBBoxD.x1), tceil(srcBBoxD.y1) - 1);
1086 
1087     bbox = (bbox * icon->getBounds())
1088                .enlarge(-1);  // Add a 1 pixel transparent margin - this
1089     if (bbox.getLx() > 0 &&
1090         bbox.getLy() > 0)  // way the actual content doesn't look trimmed.
1091       ::makeChessBackground(icon->extract(bbox));
1092   } else
1093     icon->fill(TPixel32(255, 0, 0));
1094 
1095   return icon;
1096 }
1097 
1098 //-----------------------------------------------------------------------------
1099 
generateSplineFileIcon(const TFilePath & path,const TDimension & iconSize)1100 TRaster32P IconGenerator::generateSplineFileIcon(const TFilePath &path,
1101                                                  const TDimension &iconSize) {
1102   TStageObjectSpline *spline = new TStageObjectSpline();
1103   TIStream is(path);
1104   spline->loadData(is);
1105   SplineIconRenderer sr("", iconSize, spline);
1106   TRaster32P icon = sr.generateRaster(iconSize);
1107   delete spline;
1108   return icon;
1109 }
1110 
1111 //-----------------------------------------------------------------------------
1112 
generateMeshFileIcon(const TFilePath & path,const TDimension & iconSize,const TFrameId & fid)1113 TRaster32P IconGenerator::generateMeshFileIcon(const TFilePath &path,
1114                                                const TDimension &iconSize,
1115                                                const TFrameId &fid) {
1116   TLevelReaderP lr(path);
1117   TLevelP level = lr->loadInfo();
1118   if (level->begin() == level->end()) return TRaster32P();
1119 
1120   TFrameId frameId                       = fid;
1121   if (fid == TFrameId::NO_FRAME) frameId = level->begin()->first;
1122 
1123   TMeshImageP mi = lr->getFrameReader(frameId)->load();
1124   if (!mi) return TRaster32P();
1125 
1126   MeshImageIconRenderer mir("", iconSize, mi.getPointer(),
1127                             IconGenerator::Settings());
1128   return mir.generateRaster(iconSize);
1129 }
1130 
1131 //-----------------------------------------------------------------------------
1132 
generateSceneFileIcon(const TFilePath & path,const TDimension & iconSize,int row)1133 TRaster32P IconGenerator::generateSceneFileIcon(const TFilePath &path,
1134                                                 const TDimension &iconSize,
1135                                                 int row) {
1136   if (row == 0 || row == TFrameId::NO_FRAME - 1) {
1137     TFilePath iconPath =
1138         path.getParentDir() + "sceneIcons" + (path.getWideName() + L" .png");
1139     return generateRasterFileIcon(iconPath, iconSize, TFrameId::NO_FRAME);
1140   } else {
1141     // obsolete
1142     ToonzScene scene;
1143     scene.load(path);
1144     XsheetIconRenderer ir("", iconSize, scene.getXsheet(), row);
1145     return ir.generateRaster(iconSize);
1146   }
1147 }
1148 
1149 //-----------------------------------------------------------------------------
1150 
run()1151 void FileIconRenderer::run() {
1152   TDimension iconSize(getIconSize());
1153   try {
1154     TRaster32P iconRaster;
1155     std::string type(m_path.getType());
1156 
1157     if (type == "tnz" || type == "tab")
1158       iconRaster = IconGenerator::generateSceneFileIcon(m_path, iconSize,
1159                                                         m_fid.getNumber() - 1);
1160     else if (type == "pli")
1161       iconRaster =
1162           IconGenerator::generateVectorFileIcon(m_path, iconSize, m_fid);
1163     else if (type == "tpl") {
1164       QImage palette(":Resources/paletteicon.svg");
1165       setIcon(rasterFromQImage(palette));
1166       return;
1167     } else if (type == "tzp") {
1168       QImage palette(":Resources/tzpicon.png");
1169       setIcon(rasterFromQImage(palette));
1170       return;
1171     } else if (type == "svg") {
1172       QPixmap svg(svgToPixmap(getIconThemePath("mimetypes/60/svg_icon.svg"),
1173                               QSize(iconSize.lx, iconSize.ly),
1174                               Qt::KeepAspectRatio));
1175       setIcon(rasterFromQPixmap(svg));
1176       return;
1177     } else if (type == "tzu") {
1178       QImage palette(":Resources/tzuicon.png");
1179       setIcon(rasterFromQImage(palette));
1180       return;
1181     } else if (TFileType::getInfo(m_path) == TFileType::AUDIO_LEVEL) {
1182       QPixmap loudspeaker(
1183           svgToPixmap(getIconThemePath("mimetypes/60/audio_icon.svg"),
1184                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1185       setIcon(rasterFromQPixmap(loudspeaker));
1186       return;
1187     } else if (type == "scr") {
1188       QImage screensaver(":Resources/savescreen.png");
1189       setIcon(rasterFromQImage(screensaver));
1190       return;
1191     } else if (type == "psd") {
1192       QPixmap psdPath(svgToPixmap(getIconThemePath("mimetypes/60/psd_icon.svg"),
1193                                   QSize(iconSize.lx, iconSize.ly),
1194                                   Qt::KeepAspectRatio));
1195       setIcon(rasterFromQPixmap(psdPath));
1196       return;
1197     } else if (type == "mesh")
1198       iconRaster = IconGenerator::generateMeshFileIcon(m_path, iconSize, m_fid);
1199     else if (TFileType::isViewable(TFileType::getInfo(m_path)) || type == "tlv")
1200       iconRaster =
1201           IconGenerator::generateRasterFileIcon(m_path, iconSize, m_fid);
1202     else if (type == "mpath") {
1203       QPixmap motionPath(
1204           svgToPixmap(getIconThemePath("mimetypes/60/motionpath_icon.svg"),
1205                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1206       setIcon(rasterFromQPixmap(motionPath));
1207       return;
1208     } else if (type == "curve") {
1209       QPixmap curve(svgToPixmap(getIconThemePath("mimetypes/60/curve_icon.svg"),
1210                                 QSize(iconSize.lx, iconSize.ly),
1211                                 Qt::KeepAspectRatio));
1212       setIcon(rasterFromQPixmap(curve));
1213       return;
1214     } else if (type == "cln") {
1215       QPixmap cln(svgToPixmap(getIconThemePath("mimetypes/60/cleanup_icon.svg"),
1216                               QSize(iconSize.lx, iconSize.ly),
1217                               Qt::KeepAspectRatio));
1218       setIcon(rasterFromQPixmap(cln));
1219       return;
1220     } else if (type == "tnzbat") {
1221       QPixmap tnzBat(
1222           svgToPixmap(getIconThemePath("mimetypes/60/tasklist_icon.svg"),
1223                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1224       setIcon(rasterFromQPixmap(tnzBat));
1225       return;
1226     } else if (type == "tls") {
1227       QPixmap tls(svgToPixmap(":Resources/magpie.svg",
1228                               QSize(iconSize.lx, iconSize.ly),
1229                               Qt::KeepAspectRatio));
1230       setIcon(rasterFromQPixmap(tls));
1231       return;
1232     } else if (type == "xdts") {
1233       QPixmap xdts(svgToPixmap(getIconThemePath("mimetypes/60/xdts_icon.svg"),
1234                                QSize(iconSize.lx, iconSize.ly),
1235                                Qt::KeepAspectRatio));
1236       setIcon(rasterFromQPixmap(xdts));
1237       return;
1238     } else if (type == "js") {
1239       QPixmap script(
1240           svgToPixmap(getIconThemePath("mimetypes/60/script_icon.svg"),
1241                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1242       setIcon(rasterFromQPixmap(script));
1243       return;
1244     }
1245 
1246     else {
1247       QPixmap unknown(
1248           svgToPixmap(getIconThemePath("mimetypes/60/unknown_icon.svg"),
1249                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1250       setIcon(rasterFromQPixmap(unknown));
1251       return;
1252     }
1253     if (!iconRaster) {
1254       QPixmap broken(
1255           svgToPixmap(getIconThemePath("mimetypes/60/broken_icon.svg"),
1256                       QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1257       setIcon(rasterFromQPixmap(broken));
1258       return;
1259     }
1260     setIcon(iconRaster);
1261   } catch (const TImageVersionException &) {
1262     QPixmap unknown(
1263         svgToPixmap(getIconThemePath("mimetypes/60/unknown_icon.svg"),
1264                     QSize(iconSize.lx, iconSize.ly), Qt::KeepAspectRatio));
1265     setIcon(rasterFromQPixmap(unknown));
1266   } catch (...) {
1267     QPixmap broken(svgToPixmap(getIconThemePath("mimetypes/60/broken_icon.svg"),
1268                                QSize(iconSize.lx, iconSize.ly),
1269                                Qt::KeepAspectRatio));
1270     setIcon(rasterFromQPixmap(broken));
1271   }
1272 }
1273 
1274 //=============================================================================
1275 
1276 //================================
1277 //    SceneIconRenderer class
1278 //--------------------------------
1279 
1280 class SceneIconRenderer final : public IconRenderer {
1281   ToonzScene *m_toonzScene;
1282 
1283 public:
SceneIconRenderer(const TDimension & iconSize,ToonzScene * scene)1284   SceneIconRenderer(const TDimension &iconSize, ToonzScene *scene)
1285       : IconRenderer(getId(), iconSize), m_toonzScene(scene) {}
1286 
getId()1287   static std::string getId() { return "currentScene"; }
1288 
1289   void run() override;
1290   TRaster32P generateIcon(const TDimension &iconSize) const;
1291 };
1292 
1293 //-----------------------------------------------------------------------------
1294 
generateIcon(const TDimension & iconSize) const1295 TRaster32P SceneIconRenderer::generateIcon(const TDimension &iconSize) const {
1296   TRaster32P ras(iconSize);
1297 
1298   TPixel32 bgColor = m_toonzScene->getProperties()->getBgColor();
1299   bgColor.m        = 255;
1300   ras->fill(bgColor);
1301 
1302   m_toonzScene->renderFrame(ras, 0, 0, false);
1303 
1304   return ras;
1305 }
1306 
1307 //-----------------------------------------------------------------------------
1308 
run()1309 void SceneIconRenderer::run() { setIcon(generateIcon(getIconSize())); }
1310 
1311 //=============================================================================
1312 
1313 //===================================
1314 //
1315 //    IconGenerator class
1316 //
1317 //-----------------------------------
1318 
IconGenerator()1319 IconGenerator::IconGenerator() : m_iconSize(FilmstripIconSize) {
1320   m_executor.setMaxActiveTasks(1);  // Only one thread to render icons...
1321   m_executor.setDedicatedThreads(true);
1322 }
1323 
1324 //-----------------------------------------------------------------------------
1325 
~IconGenerator()1326 IconGenerator::~IconGenerator() {}
1327 
1328 //-----------------------------------------------------------------------------
1329 
instance()1330 IconGenerator *IconGenerator::instance() {
1331   static IconGenerator _instance;
1332   return &_instance;
1333 }
1334 
1335 //-----------------------------------------------------------------------------
1336 
setFilmstripIconSize(const TDimension & dim)1337 void IconGenerator::setFilmstripIconSize(const TDimension &dim) {
1338   FilmstripIconSize = dim;
1339 }
1340 
1341 //-----------------------------------------------------------------------------
1342 
getIconSize() const1343 TDimension IconGenerator::getIconSize() const { return FilmstripIconSize; }
1344 
1345 //-----------------------------------------------------------------------------
1346 
getOfflineGLContext()1347 TOfflineGL *IconGenerator::getOfflineGLContext() {
1348   // One context per rendering thread
1349   if (!m_contexts.hasLocalData()) {
1350     TDimension contextSize(std::max(FilmstripIconSize.lx, IconSize.lx),
1351                            std::max(FilmstripIconSize.ly, IconSize.ly));
1352     m_contexts.setLocalData(new TOfflineGL(contextSize));
1353   }
1354   return m_contexts.localData();
1355 }
1356 
1357 //-----------------------------------------------------------------------------
1358 
addTask(const std::string & id,TThread::RunnableP iconRenderer)1359 void IconGenerator::addTask(const std::string &id,
1360                             TThread::RunnableP iconRenderer) {
1361   iconsMap.insert(id);
1362   m_executor.addTask(iconRenderer);
1363 }
1364 
1365 //-----------------------------------------------------------------------------
1366 
getIcon(TXshLevel * xl,const TFrameId & fid,bool filmStrip,bool onDemand)1367 QPixmap IconGenerator::getIcon(TXshLevel *xl, const TFrameId &fid,
1368                                bool filmStrip, bool onDemand) {
1369   if (!xl) return QPixmap();
1370 
1371   if (TXshChildLevel *cl = xl->getChildLevel()) {
1372     if (filmStrip) return QPixmap();
1373 
1374     std::string id = XsheetIconRenderer::getId(cl, fid.getNumber() - 1);
1375     QPixmap pix;
1376     if (::getIcon(id, pix)) return pix;
1377 
1378     if (onDemand) return pix;
1379 
1380     TDimension iconSize = TDimension(80, 60);
1381 
1382     // The icon must be calculated - add an IconRenderer task.
1383     // storeIcon(id, QPixmap());   //It was automatically added by the former
1384     // access
1385     addTask(id, new XsheetIconRenderer(id, iconSize, cl->getXsheet()));
1386   }
1387 
1388   if (TXshSimpleLevel *sl = xl->getSimpleLevel()) {
1389     // make thumbnails for cleanup preview and cameratest to be the same as
1390     // normal TLV
1391     std::string id;
1392     int status = sl->getFrameStatus(fid);
1393     if (sl->getType() == TZP_XSHLEVEL &&
1394         status & TXshSimpleLevel::CleanupPreview) {
1395       sl->setFrameStatus(fid, status & ~TXshSimpleLevel::CleanupPreview);
1396       id = sl->getIconId(fid);
1397       sl->setFrameStatus(fid, status);
1398     } else
1399       id = sl->getIconId(fid);
1400 
1401     if (!filmStrip) id += "_small";
1402 
1403     QPixmap pix;
1404     if (::getIcon(id, pix, xl->getSimpleLevel())) return pix;
1405 
1406     if (onDemand) return pix;
1407 
1408     IconGenerator::Settings oldSettings = m_settings;
1409 
1410     // Disable transparency check for cast and xsheet icons
1411     if (!filmStrip) m_settings = IconGenerator::Settings();
1412 
1413     TDimension iconSize = filmStrip ? m_iconSize : TDimension(80, 60);
1414 
1415     // storeIcon(id, QPixmap());
1416 
1417     int type = sl->getType();
1418     switch (type) {
1419     case OVL_XSHLEVEL:
1420     case TZI_XSHLEVEL:
1421       addTask(id, new RasterImageIconRenderer(id, iconSize, sl, fid));
1422       break;
1423     case PLI_XSHLEVEL:
1424       addTask(id,
1425               new VectorImageIconRenderer(id, iconSize, sl, fid, m_settings));
1426       break;
1427     case TZP_XSHLEVEL:
1428       // Yep, we could have rasters, due to a cleanupping process
1429       if (status == TXshSimpleLevel::Scanned)
1430         addTask(id, new RasterImageIconRenderer(id, iconSize, sl, fid));
1431       else
1432         addTask(id,
1433                 new ToonzImageIconRenderer(id, iconSize, sl, fid, m_settings));
1434       break;
1435     case MESH_XSHLEVEL:
1436       addTask(id, new MeshImageIconRenderer(id, iconSize, sl, fid, m_settings));
1437       break;
1438     default:
1439       assert(false);
1440       break;
1441     }
1442 
1443     m_settings = oldSettings;
1444   }
1445 
1446   return QPixmap();
1447 }
1448 
1449 //-----------------------------------------------------------------------------
1450 
getSizedIcon(TXshLevel * xl,const TFrameId & fid,std::string newId,TDimension dim)1451 QPixmap IconGenerator::getSizedIcon(TXshLevel *xl, const TFrameId &fid,
1452                                     std::string newId, TDimension dim) {
1453   if (!xl) return QPixmap();
1454 
1455   if (TXshChildLevel *cl = xl->getChildLevel()) {
1456     std::string id = XsheetIconRenderer::getId(cl, fid.getNumber() - 1);
1457     QPixmap pix;
1458     if (::getIcon(id, pix)) return pix;
1459 
1460     // if (onDemand) return pix;
1461 
1462     TDimension iconSize = TDimension(80, 60);
1463     if (dim != TDimension(0, 0)) {
1464       iconSize = dim;
1465     }
1466 
1467     // The icon must be calculated - add an IconRenderer task.
1468     // storeIcon(id, QPixmap());   //It was automatically added by the former
1469     // access
1470     addTask(id, new XsheetIconRenderer(id, iconSize, cl->getXsheet()));
1471   }
1472 
1473   if (TXshSimpleLevel *sl = xl->getSimpleLevel()) {
1474     // make thumbnails for cleanup preview and cameratest to be the same as
1475     // normal TLV
1476     std::string id;
1477     int status = sl->getFrameStatus(fid);
1478     if (sl->getType() == TZP_XSHLEVEL &&
1479         status & TXshSimpleLevel::CleanupPreview) {
1480       sl->setFrameStatus(fid, status & ~TXshSimpleLevel::CleanupPreview);
1481       id = sl->getIconId(fid);
1482       sl->setFrameStatus(fid, status);
1483     } else
1484       id = sl->getIconId(fid);
1485 
1486     id += newId;
1487 
1488     QPixmap pix;
1489     if (::getIcon(id, pix, xl->getSimpleLevel())) return pix;
1490 
1491     // if (onDemand) return pix;
1492 
1493     IconGenerator::Settings oldSettings = m_settings;
1494 
1495     // Disable transparency check for cast and xsheet icons
1496     // if (!filmStrip) m_settings = IconGenerator::Settings();
1497 
1498     TDimension iconSize = TDimension(80, 60);
1499     if (dim != TDimension(0, 0)) {
1500       iconSize = dim;
1501     }
1502 
1503     // storeIcon(id, QPixmap());
1504 
1505     int type = sl->getType();
1506     switch (type) {
1507     case OVL_XSHLEVEL:
1508     case TZI_XSHLEVEL:
1509       addTask(id, new RasterImageIconRenderer(id, iconSize, sl, fid));
1510       break;
1511     case PLI_XSHLEVEL:
1512       addTask(id,
1513               new VectorImageIconRenderer(id, iconSize, sl, fid, m_settings));
1514       break;
1515     case TZP_XSHLEVEL:
1516       // Yep, we could have rasters, due to a cleanupping process
1517       if (status == TXshSimpleLevel::Scanned)
1518         addTask(id, new RasterImageIconRenderer(id, iconSize, sl, fid));
1519       else
1520         addTask(id,
1521                 new ToonzImageIconRenderer(id, iconSize, sl, fid, m_settings));
1522       break;
1523     case MESH_XSHLEVEL:
1524       addTask(id, new MeshImageIconRenderer(id, iconSize, sl, fid, m_settings));
1525       break;
1526     default:
1527       assert(false);
1528       break;
1529     }
1530 
1531     m_settings = oldSettings;
1532   }
1533 
1534   return QPixmap();
1535 }
1536 
1537 //-----------------------------------------------------------------------------
1538 
invalidate(TXshLevel * xl,const TFrameId & fid,bool onlyFilmStrip)1539 void IconGenerator::invalidate(TXshLevel *xl, const TFrameId &fid,
1540                                bool onlyFilmStrip) {
1541   if (!xl) return;
1542 
1543   if (TXshSimpleLevel *sl = xl->getSimpleLevel()) {
1544     std::string id = sl->getIconId(fid);
1545 
1546     int type = sl->getType();
1547 
1548     switch (type) {
1549     case OVL_XSHLEVEL:
1550     case TZI_XSHLEVEL:
1551       addTask(id, new RasterImageIconRenderer(id, getIconSize(), sl, fid));
1552       break;
1553     case PLI_XSHLEVEL:
1554       removeIcon(id);
1555       addTask(id, new VectorImageIconRenderer(id, getIconSize(), sl, fid,
1556                                               m_settings));
1557       break;
1558     case TZP_XSHLEVEL:
1559       if (sl->getFrameStatus(fid) == TXshSimpleLevel::Scanned)
1560         addTask(id, new RasterImageIconRenderer(id, getIconSize(), sl, fid));
1561       else
1562         addTask(id, new ToonzImageIconRenderer(id, getIconSize(), sl, fid,
1563                                                m_settings));
1564       break;
1565     case MESH_XSHLEVEL:
1566       addTask(id, new MeshImageIconRenderer(id, getIconSize(), sl, fid,
1567                                             m_settings));
1568       break;
1569     default:
1570       assert(false);
1571       break;
1572     }
1573 
1574     if (onlyFilmStrip) return;
1575 
1576     id += "_small";
1577     if (iconsMap.find(id) == iconsMap.end()) return;
1578 
1579     // Not-filmstrip icons diable all checks
1580     IconGenerator::Settings oldSettings = m_settings;
1581     m_settings.m_transparencyCheck      = false;
1582     m_settings.m_inkIndex               = -1;
1583     m_settings.m_paintIndex             = -1;
1584     m_settings.m_blackBgCheck           = false;
1585 
1586     switch (type) {
1587     case OVL_XSHLEVEL:
1588     case TZI_XSHLEVEL:
1589       addTask(id, new RasterImageIconRenderer(id, TDimension(80, 60), sl, fid));
1590       break;
1591     case PLI_XSHLEVEL:
1592       addTask(id, new VectorImageIconRenderer(id, TDimension(80, 60), sl, fid,
1593                                               m_settings));
1594       break;
1595     case TZP_XSHLEVEL:
1596       if (sl->getFrameStatus(fid) == TXshSimpleLevel::Scanned)
1597         addTask(id,
1598                 new RasterImageIconRenderer(id, TDimension(80, 60), sl, fid));
1599       else
1600         addTask(id, new ToonzImageIconRenderer(id, TDimension(80, 60), sl, fid,
1601                                                m_settings));
1602       break;
1603     case MESH_XSHLEVEL:
1604       addTask(id, new MeshImageIconRenderer(id, TDimension(80, 60), sl, fid,
1605                                             m_settings));
1606       break;
1607     default:
1608       assert(false);
1609       break;
1610     }
1611 
1612     m_settings = oldSettings;
1613   } else if (TXshChildLevel *cl = xl->getChildLevel()) {
1614     if (onlyFilmStrip) return;
1615 
1616     std::string id = XsheetIconRenderer::getId(cl, fid.getNumber() - 1);
1617     removeIcon(id);
1618 
1619     getIcon(xl, fid);
1620   }
1621 }
1622 
1623 //-----------------------------------------------------------------------------
1624 
remove(TXshLevel * xl,const TFrameId & fid,bool onlyFilmStrip)1625 void IconGenerator::remove(TXshLevel *xl, const TFrameId &fid,
1626                            bool onlyFilmStrip) {
1627   if (!xl) return;
1628   if (TXshSimpleLevel *sl = xl->getSimpleLevel()) {
1629     std::string id(sl->getIconId(fid));
1630 
1631     removeIcon(id);
1632     if (!onlyFilmStrip) removeIcon(id + "_small");
1633   } else {
1634     TXshChildLevel *cl = xl->getChildLevel();
1635     if (cl && !onlyFilmStrip)
1636       removeIcon(XsheetIconRenderer::getId(cl, fid.getNumber() - 1));
1637   }
1638 }
1639 
1640 //-----------------------------------------------------------------------------
1641 
getIcon(TStageObjectSpline * spline)1642 QPixmap IconGenerator::getIcon(TStageObjectSpline *spline) {
1643   if (!spline) return QPixmap();
1644   std::string iconName = spline->getIconId();
1645 
1646   QPixmap pix;
1647   if (::getIcon(iconName, pix)) return pix;
1648 
1649   // storeIcon(id, QPixmap());
1650   addTask(iconName, new SplineIconRenderer(iconName, getIconSize(), spline));
1651 
1652   return QPixmap();
1653 }
1654 
1655 //-----------------------------------------------------------------------------
1656 
invalidate(TStageObjectSpline * spline)1657 void IconGenerator::invalidate(TStageObjectSpline *spline) {
1658   if (!spline) return;
1659   std::string iconName = spline->getIconId();
1660   removeIcon(iconName);
1661 
1662   addTask(iconName, new SplineIconRenderer(iconName, getIconSize(), spline));
1663 }
1664 
1665 //-----------------------------------------------------------------------------
1666 
remove(TStageObjectSpline * spline)1667 void IconGenerator::remove(TStageObjectSpline *spline) {
1668   if (!spline) return;
1669   std::string iconName = spline->getIconId();
1670   removeIcon(iconName);
1671 }
1672 
1673 //-----------------------------------------------------------------------------
1674 
getIcon(const TFilePath & path,const TFrameId & fid)1675 QPixmap IconGenerator::getIcon(const TFilePath &path, const TFrameId &fid) {
1676   std::string id = FileIconRenderer::getId(path, fid);
1677 
1678   QPixmap pix;
1679   TDimension fileIconSize(80, 60);
1680   // Here the fileIconSize is input in order to check if the icon is obtained
1681   // with high-dpi (i.e. devPixRatio > 1.0).
1682   if (::getIcon(id, pix, 0, fileIconSize)) return pix;
1683 
1684   addTask(id, new FileIconRenderer(fileIconSize, path, fid));
1685 
1686   return QPixmap();
1687 }
1688 
1689 //-----------------------------------------------------------------------------
1690 
invalidate(const TFilePath & path,const TFrameId & fid)1691 void IconGenerator::invalidate(const TFilePath &path, const TFrameId &fid) {
1692   std::string id = FileIconRenderer::getId(path, fid);
1693   removeIcon(id);
1694   addTask(id, new FileIconRenderer(TDimension(80, 60), path, fid));
1695 }
1696 
1697 //-----------------------------------------------------------------------------
1698 
remove(const TFilePath & path,const TFrameId & fid)1699 void IconGenerator::remove(const TFilePath &path, const TFrameId &fid) {
1700   removeIcon(FileIconRenderer::getId(path, fid));
1701 }
1702 
1703 //-----------------------------------------------------------------------------
1704 
getSceneIcon(ToonzScene * scene)1705 QPixmap IconGenerator::getSceneIcon(ToonzScene *scene) {
1706   std::string id(SceneIconRenderer::getId());
1707 
1708   QPixmap pix;
1709   if (::getIcon(id, pix)) return pix;
1710 
1711   // storeIcon(id, QPixmap());
1712   addTask(id, new SceneIconRenderer(getIconSize(), scene));
1713 
1714   return QPixmap();
1715 }
1716 
1717 //-----------------------------------------------------------------------------
1718 
invalidateSceneIcon()1719 void IconGenerator::invalidateSceneIcon() {
1720   removeIcon(SceneIconRenderer::getId());
1721 }
1722 
1723 //-----------------------------------------------------------------------------
1724 
remap(const std::string & newIconId,const std::string & oldIconId)1725 void IconGenerator::remap(const std::string &newIconId,
1726                           const std::string &oldIconId) {
1727   IconIterator it = iconsMap.find(oldIconId);
1728   if (it == iconsMap.end()) return;
1729 
1730   iconsMap.erase(it);
1731   iconsMap.insert(newIconId);
1732 
1733   TImageCache::instance()->remap(newIconId, oldIconId);
1734 }
1735 
1736 //-----------------------------------------------------------------------------
1737 
clearRequests()1738 void IconGenerator::clearRequests() { m_executor.cancelAll(); }
1739 
1740 //-----------------------------------------------------------------------------
1741 
clearSceneIcons()1742 void IconGenerator::clearSceneIcons() {
1743   // Eliminate all icons whose prefix is not "$:" (that is, scene-independent
1744   // images).
1745   // The abovementioned prefix is internally recognized by the image cache when
1746   // the scene
1747   // changes to avoid clearing file browser's icons.
1748 
1749   // Observe that image cache's clear function invoked during scene changes is
1750   // called through
1751   // the ImageManager::clear() method, including FilmStrip icons.
1752 
1753   // note the ';' - which follows ':' in the ascii table
1754   iconsMap.erase(iconsMap.begin(), iconsMap.lower_bound("$:"));
1755   iconsMap.erase(iconsMap.lower_bound("$;"), iconsMap.end());
1756 }
1757 
1758 //-----------------------------------------------------------------------------
1759 
onStarted(TThread::RunnableP iconRenderer)1760 void IconGenerator::onStarted(TThread::RunnableP iconRenderer) {
1761   IconRenderer *ir = static_cast<IconRenderer *>(iconRenderer.getPointer());
1762 
1763   ir->hasStarted() = true;
1764 }
1765 
1766 //-----------------------------------------------------------------------------
1767 
onCanceled(TThread::RunnableP iconRenderer)1768 void IconGenerator::onCanceled(TThread::RunnableP iconRenderer) {
1769   IconRenderer *ir = static_cast<IconRenderer *>(iconRenderer.getPointer());
1770 
1771   if (!ir->hasStarted()) {
1772     removeIcon(ir->getId());
1773   }
1774 }
1775 
1776 //-----------------------------------------------------------------------------
1777 
onFinished(TThread::RunnableP iconRenderer)1778 void IconGenerator::onFinished(TThread::RunnableP iconRenderer) {
1779   IconRenderer *ir = static_cast<IconRenderer *>(iconRenderer.getPointer());
1780 
1781   // if the icon was generated in TToonzImage format, cache it instead
1782   ToonzImageIconRenderer *tir = dynamic_cast<ToonzImageIconRenderer *>(ir);
1783   if (tir) {
1784     TRasterCM32P timgp = tir->getIcon_TnzImg();
1785     if (timgp) {
1786       ::setIcon_TnzImg(ir->getId(), timgp);
1787       emit iconGenerated();
1788       if (ir->wasTerminated()) m_iconsTerminationLoop.quit();
1789       return;
1790     }
1791   }
1792 
1793   // Update the icons map
1794   if (ir->getIcon()) {
1795     ::setIcon(ir->getId(), ir->getIcon());
1796     emit iconGenerated();
1797   }
1798 
1799   if (ir->wasTerminated()) m_iconsTerminationLoop.quit();
1800 }
1801 
1802 //-----------------------------------------------------------------------------
1803 
onException(TThread::RunnableP iconRenderer)1804 void IconGenerator::onException(TThread::RunnableP iconRenderer) {
1805   IconRenderer *ir = static_cast<IconRenderer *>(iconRenderer.getPointer());
1806 
1807   if (ir->wasTerminated()) m_iconsTerminationLoop.quit();
1808 }
1809 
1810 //-----------------------------------------------------------------------------
1811 
onTerminated(TThread::RunnableP iconRenderer)1812 void IconGenerator::onTerminated(TThread::RunnableP iconRenderer) {
1813   IconRenderer *ir = static_cast<IconRenderer *>(iconRenderer.getPointer());
1814 
1815   ir->wasTerminated() = true;
1816   m_iconsTerminationLoop.exec();
1817 }
1818