1 
2 
3 // TnzCore includes
4 #include "tpixelutils.h"
5 #include "tstroke.h"
6 #include "tofflinegl.h"
7 #include "tstencilcontrol.h"
8 #include "tvectorgl.h"
9 #include "tvectorrenderdata.h"
10 #include "tcolorfunctions.h"
11 #include "tpalette.h"
12 #include "tropcm.h"
13 #include "trasterimage.h"
14 #include "tvectorimage.h"
15 #include "tmeshimage.h"
16 #include "tcolorstyles.h"
17 #include "timage_io.h"
18 #include "tregion.h"
19 #include "toonz/toonzscene.h"
20 
21 // TnzBase includes
22 #include "tenv.h"
23 
24 // TnzExt includes
25 #include "ext/meshutils.h"
26 #include "ext/plasticskeleton.h"
27 #include "ext/plasticskeletondeformation.h"
28 #include "ext/plasticdeformerstorage.h"
29 
30 // TnzLib includes
31 #include "toonz/stageplayer.h"
32 #include "toonz/stage.h"
33 #include "toonz/stage2.h"
34 #include "toonz/tcolumnfx.h"
35 #include "toonz/txsheet.h"
36 #include "toonz/txshsimplelevel.h"
37 #include "toonz/txshchildlevel.h"
38 #include "toonz/txshcolumn.h"
39 #include "toonz/txshcell.h"
40 #include "toonz/onionskinmask.h"
41 #include "toonz/dpiscale.h"
42 #include "toonz/imagemanager.h"
43 #include "toonz/tstageobjecttree.h"
44 #include "toonz/glrasterpainter.h"
45 #include "toonz/preferences.h"
46 #include "toonz/fill.h"
47 #include "toonz/levelproperties.h"
48 #include "toonz/autoclose.h"
49 #include "toonz/txshleveltypes.h"
50 #include "imagebuilders.h"
51 #include "toonz/tframehandle.h"
52 #include "toonz/preferences.h"
53 
54 // Qt includes
55 #include <QImage>
56 #include <QPainter>
57 #include <QPolygon>
58 #include <QThreadStorage>
59 #include <QMatrix>
60 #include <QThread>
61 #include <QGuiApplication>
62 
63 #include "toonz/stagevisitor.h"
64 
65 //**********************************************************************************************
66 //    Stage namespace
67 //**********************************************************************************************
68 
69 /*! \namespace Stage
70     \brief The Stage namespace provides objects, classes and methods useful to
71    view or display images.
72 */
73 
74 using namespace Stage;
75 
76 /*! \var Stage::inch
77                 For historical reasons camera stand is defined in a coordinate
78    system in which
79                 an inch is equal to 'Stage::inch' unit.
80                 Pay attention: modify this value condition apparent line
81    thickness of
82                 images .pli.
83 */
84 // const double Stage::inch = 53.33333;
85 
86 //**********************************************************************************************
87 //    Local namespace
88 //**********************************************************************************************
89 
90 namespace {
91 
rasterToQImage(const TRaster32P & ras)92 QImage rasterToQImage(const TRaster32P &ras) {
93   QImage image(ras->getRawData(), ras->getLx(), ras->getLy(),
94                QImage::Format_ARGB32_Premultiplied);
95   return image;
96 }
97 
98 //----------------------------------------------------------------
99 
rasterToQImage(const TRasterGR8P & ras)100 QImage rasterToQImage(const TRasterGR8P &ras) {
101   QImage image(ras->getLx(), ras->getLy(), QImage::Format_ARGB32_Premultiplied);
102   int lx = ras->getLx(), ly = ras->getLy();
103 
104   for (int y = 0; y < ly; y++) {
105     TPixelGR8 *pix    = ras->pixels(y);
106     TPixelGR8 *endPix = pix + lx;
107     QRgb *outPix      = (QRgb *)image.scanLine(y);
108     for (; pix < endPix; ++pix) {
109       int value = pix->value;
110       *outPix++ = qRgba(value, value, value, 255);
111     }
112   }
113   return image;
114 }
115 
116 //----------------------------------------------------------------
117 
rasterToQImage(const TRasterP & ras)118 QImage rasterToQImage(const TRasterP &ras) {
119   if (TRaster32P src32 = ras)
120     return rasterToQImage(src32);
121   else if (TRasterGR8P srcGr8 = ras)
122     return rasterToQImage(srcGr8);
123 
124   // assert(!"Cannot use drawImage with this image!");
125   return QImage();
126 }
127 
128 //----------------------------------------------------------------
129 
130 //! Draw orthogonal projection of \b bbox onto x-axis and y-axis.
draw3DShadow(const TRectD & bbox,double z,double phi)131 void draw3DShadow(const TRectD &bbox, double z, double phi) {
132   // bruttino assai, ammetto
133 
134   double a = bigBoxSize[0];
135   double b = bigBoxSize[1];
136 
137   glColor3d(0.9, 0.9, 0.86);
138   glBegin(GL_LINE_STRIP);
139   glVertex3d(bbox.x0, bbox.y0, z);
140   glVertex3d(bbox.x0, bbox.y1, z);
141   glVertex3d(bbox.x1, bbox.y1, z);
142   glVertex3d(bbox.x1, bbox.y0, z);
143   glVertex3d(bbox.x0, bbox.y0, z);
144   glEnd();
145 
146   double y = -b;
147   double x = phi >= 0 ? a : -a;
148 
149   double xm = 0.5 * (bbox.x0 + bbox.x1);
150   double ym = 0.5 * (bbox.y0 + bbox.y1);
151 
152   if (bbox.y0 > y) {
153     glBegin(GL_LINE_STRIP);
154     glVertex3d(xm, y, z);
155     glVertex3d(xm, bbox.y0, z);
156     glEnd();
157   } else if (bbox.y1 < y) {
158     glBegin(GL_LINE_STRIP);
159     glVertex3d(xm, y, z);
160     glVertex3d(xm, bbox.y1, z);
161     glEnd();
162   }
163 
164   if (bbox.x0 > x) {
165     glBegin(GL_LINE_STRIP);
166     glVertex3d(x, ym, z);
167     glVertex3d(bbox.x0, ym, z);
168     glEnd();
169   } else if (bbox.x1 < x) {
170     glBegin(GL_LINE_STRIP);
171     glVertex3d(x, ym, z);
172     glVertex3d(bbox.x1, ym, z);
173     glEnd();
174   }
175 
176   glColor3d(0.0, 0.0, 0.0);
177 
178   glBegin(GL_LINE_STRIP);
179   glVertex3d(bbox.x0, -b, z);
180   glVertex3d(bbox.x1, -b, z);
181   glEnd();
182 
183   glBegin(GL_LINE_STRIP);
184   glVertex3d(x, bbox.y0, z);
185   glVertex3d(x, bbox.y1, z);
186   glEnd();
187 }
188 
189 //=====================================================================
190 
191 //  Plastic function declarations
192 
193 /*!
194   Returns from the specified player the stage object to be plastic
195   deformed - or 0 if current Toonz rules prevent it from being deformed.
196 */
197 TStageObject *plasticDeformedObj(const Stage::Player &player,
198                                  const PlasticVisualSettings &pvs);
199 
200 //! Draws the specified mesh image
201 void onMeshImage(TMeshImage *mi, const Stage::Player &player,
202                  const ImagePainter::VisualSettings &vs,
203                  const TAffine &viewAff);
204 
205 //! Applies Plastic deformation of the specified player's stage object.
206 void onPlasticDeformedImage(TStageObject *playerObj,
207                             const Stage::Player &player,
208                             const ImagePainter::VisualSettings &vs,
209                             const TAffine &viewAff);
210 
211 }  // namespace
212 
213 //**********************************************************************************************
214 //    Picker  implementation
215 //**********************************************************************************************
216 
Picker(const TAffine & viewAff,const TPointD & point,const ImagePainter::VisualSettings & vs,int devPixRatio)217 Picker::Picker(const TAffine &viewAff, const TPointD &point,
218                const ImagePainter::VisualSettings &vs, int devPixRatio)
219     : Visitor(vs)
220     , m_viewAff(viewAff)
221     , m_point(point)
222     , m_columnIndexes()
223     , m_minDist2(25.0)
224     , m_devPixRatio(devPixRatio) {}
225 
226 //-----------------------------------------------------------------------------
227 
setMinimumDistance(double d)228 void Picker::setMinimumDistance(double d) {
229   m_minDist2 = (double)(m_devPixRatio * m_devPixRatio) * d * d;
230 }
231 
232 //-----------------------------------------------------------------------------
233 
onImage(const Stage::Player & player)234 void Picker::onImage(const Stage::Player &player) {
235   // if m_currentColumnIndex is other than the default value (-1),
236   // then pick only the current column.
237   if (m_currentColumnIndex != -1 &&
238       m_currentColumnIndex != player.m_ancestorColumnIndex)
239     return;
240 
241   bool picked   = false;
242   TAffine aff   = m_viewAff * player.m_placement;
243   TPointD point = aff.inv() * m_point;
244 
245   const TImageP &img = player.image();
246 
247   if (TVectorImageP vi = img) {
248     double w         = 0;
249     UINT strokeIndex = 0;
250     double dist2     = 0;
251     TRegion *r       = vi->getRegion(point);
252     int styleId      = 0;
253     if (r) styleId = r->getStyle();
254     if (styleId != 0)
255       picked = true;
256     else if (vi->getNearestStroke(point, w, strokeIndex, dist2)) {
257       dist2 *= aff.det();
258       TStroke *stroke        = vi->getStroke(strokeIndex);
259       TThickPoint thickPoint = stroke->getThickPoint(w);
260       double len2            = thickPoint.thick * thickPoint.thick * aff.det();
261       double checkDist       = std::max(m_minDist2, len2);
262       if (dist2 < checkDist) picked = true;
263     }
264   } else if (TRasterImageP ri = img) {
265     TRaster32P ras = ri->getRaster();
266     if (!ras) return;
267 
268     ras->lock();
269     TPointD pp = player.m_dpiAff.inv() * point + ras->getCenterD();
270     TPoint p(tround(pp.x), tround(pp.y));
271     if (!ras->getBounds().contains(p)) return;
272 
273     TPixel32 *pix = ras->pixels(p.y);
274     if (pix[p.x].m != 0) picked = true;
275 
276     TAffine aff2 = (aff * player.m_dpiAff).inv();
277 
278     TPointD pa(p.x, p.y);
279     TPointD dx = aff2 * (m_point + TPointD(3, 0)) - aff2 * m_point;
280     TPointD dy = aff2 * (m_point + TPointD(0, 3)) - aff2 * m_point;
281     double rx  = dx.x * dx.x + dx.y * dx.y;
282     double ry  = dy.x * dy.x + dy.y * dy.y;
283     int radius = tround(sqrt(rx > ry ? rx : ry));
284     TRect rect = TRect(p.x - radius, p.y - radius, p.x + radius, p.y + radius) *
285                  ras->getBounds();
286     for (int y = rect.y0; !picked && y <= rect.y1; y++) {
287       pix = ras->pixels(y);
288       for (int x = rect.x0; !picked && x <= rect.x1; x++)
289         if (pix[x].m != 0) picked = true;
290     }
291 
292     ras->unlock();
293   } else if (TToonzImageP ti = img) {
294     TRasterCM32P ras = ti->getRaster();
295     if (!ras) return;
296 
297     ras->lock();
298     TPointD pp = player.m_dpiAff.inv() * point + ras->getCenterD();
299     TPoint p(tround(pp.x), tround(pp.y));
300     if (!ras->getBounds().contains(p)) return;
301 
302     TPixelCM32 *pix = ras->pixels(p.y) + p.x;
303     if (!pix->isPurePaint() || pix->getPaint() != 0) picked = true;
304 
305     ras->unlock();
306   }
307 
308   if (picked) {
309     int columnIndex = player.m_ancestorColumnIndex;
310     if (m_columnIndexes.empty() || m_columnIndexes.back() != columnIndex)
311       m_columnIndexes.push_back(columnIndex);
312 
313     int row = player.m_frame;
314     if (m_rows.empty() || m_rows.back() != row) m_rows.push_back(row);
315   }
316 }
317 
318 //-----------------------------------------------------------------------------
319 
beginMask()320 void Picker::beginMask() {}
321 
322 //-----------------------------------------------------------------------------
323 
endMask()324 void Picker::endMask() {}
325 
326 //-----------------------------------------------------------------------------
327 
enableMask()328 void Picker::enableMask() {}
329 
330 //-----------------------------------------------------------------------------
331 
disableMask()332 void Picker::disableMask() {}
333 
334 //-----------------------------------------------------------------------------
335 
getColumnIndex() const336 int Picker::getColumnIndex() const {
337   if (m_columnIndexes.empty())
338     return -1;
339   else
340     return m_columnIndexes.back();
341 }
342 
343 //-----------------------------------------------------------------------------
344 
getColumnIndexes(std::vector<int> & indexes) const345 void Picker::getColumnIndexes(std::vector<int> &indexes) const {
346   indexes = m_columnIndexes;
347 }
348 //-----------------------------------------------------------------------------
349 
getRow() const350 int Picker::getRow() const {
351   if (m_rows.empty())
352     return -1;
353   else
354     return m_rows.back();
355 }
356 
357 //**********************************************************************************************
358 //    RasterPainter  implementation
359 //**********************************************************************************************
360 
RasterPainter(const TDimension & dim,const TAffine & viewAff,const TRect & rect,const ImagePainter::VisualSettings & vs,bool checkFlags)361 RasterPainter::RasterPainter(const TDimension &dim, const TAffine &viewAff,
362                              const TRect &rect,
363                              const ImagePainter::VisualSettings &vs,
364                              bool checkFlags)
365     : Visitor(vs)
366     , m_dim(dim)
367     , m_viewAff(viewAff)
368     , m_clipRect(rect)
369     , m_maskLevel(0)
370     , m_singleColumnEnabled(false)
371     , m_checkFlags(checkFlags)
372     , m_doRasterDarkenBlendedView(false) {}
373 
374 //-----------------------------------------------------------------------------
375 
376 //! Utilizzato solo per TAB Pro
beginMask()377 void RasterPainter::beginMask() {
378   flushRasterImages();  // per evitare che venga fatto dopo il beginMask
379   ++m_maskLevel;
380   TStencilControl::instance()->beginMask();
381 }
382 //! Utilizzato solo per TAB Pro
endMask()383 void RasterPainter::endMask() {
384   flushRasterImages();  // se ci sono delle immagini raster nella maschera
385                         // devono uscire ora
386   --m_maskLevel;
387   TStencilControl::instance()->endMask();
388 }
389 //! Utilizzato solo per TAB Pro
enableMask()390 void RasterPainter::enableMask() {
391   TStencilControl::instance()->enableMask(TStencilControl::SHOW_INSIDE);
392 }
393 //! Utilizzato solo per TAB Pro
disableMask()394 void RasterPainter::disableMask() {
395   flushRasterImages();  // se ci sono delle immagini raster mascherate devono
396                         // uscire ora
397   TStencilControl::instance()->disableMask();
398 }
399 
400 //-----------------------------------------------------------------------------
401 
402 //-----------------------------------------------------------------------------
403 
404 TEnv::DoubleVar AutocloseDistance("InknpaintAutocloseDistance", 10.0);
405 TEnv::DoubleVar AutocloseAngle("InknpaintAutocloseAngle", 60.0);
406 TEnv::IntVar AutocloseInk("InknpaintAutocloseInk", 1);
407 TEnv::IntVar AutocloseOpacity("InknpaintAutocloseOpacity", 255);
408 
409 //-----------------------------------------------------------------------------
410 
getNodesCount()411 int RasterPainter::getNodesCount() { return m_nodes.size(); }
412 
413 //-----------------------------------------------------------------------------
414 
clearNodes()415 void RasterPainter::clearNodes() { m_nodes.clear(); }
416 
417 //-----------------------------------------------------------------------------
418 
getRaster(int index,QMatrix & matrix)419 TRasterP RasterPainter::getRaster(int index, QMatrix &matrix) {
420   if ((int)m_nodes.size() <= index) return TRasterP();
421 
422   if (m_nodes[index].m_onionMode != Node::eOnionSkinNone) return TRasterP();
423 
424   if (m_nodes.empty()) return TRasterP();
425 
426   double delta = sqrt(fabs(m_nodes[0].m_aff.det()));
427   TRectD bbox  = m_nodes[0].m_bbox.enlarge(delta);
428 
429   int i;
430   for (i = 1; i < (int)m_nodes.size(); i++) {
431     delta = sqrt(fabs(m_nodes[i].m_aff.det()));
432     bbox += m_nodes[i].m_bbox.enlarge(delta);
433   }
434   TRect rect(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1), tceil(bbox.y1));
435   rect = rect * TRect(0, 0, m_dim.lx - 1, m_dim.ly - 1);
436 
437   TAffine aff = TTranslation(-rect.x0, -rect.y0) * m_nodes[index].m_aff;
438   matrix      = QMatrix(aff.a11, aff.a21, aff.a12, aff.a22, aff.a13, aff.a23);
439 
440   return m_nodes[index].m_raster;
441 }
442 
443 //-----------------------------------------------------------------------------
444 
445 /*! Make frame visualization.
446 \n	If onon-skin is active, create a new raster with dimension containing
447 all
448                 frame with onion-skin; recall \b TRop::quickPut with argument
449 each frame
450                 with onion-skin and new raster. If onion-skin is not active
451 recall
452                 \b TRop::quickPut with argument current frame and new raster.
453 */
454 
455 namespace {
456 QThreadStorage<std::vector<char> *> threadBuffers;
457 }
458 
flushRasterImages()459 void RasterPainter::flushRasterImages() {
460   if (m_nodes.empty()) return;
461 
462   // Build nodes bbox union
463   double delta = sqrt(fabs(m_nodes[0].m_aff.det()));
464   TRectD bbox  = m_nodes[0].m_bbox.enlarge(delta);
465 
466   int i, nodesCount = m_nodes.size();
467   for (i = 1; i < nodesCount; ++i) {
468     delta = sqrt(fabs(m_nodes[i].m_aff.det()));
469     bbox += m_nodes[i].m_bbox.enlarge(delta);
470   }
471 
472   TRect rect(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1), tceil(bbox.y1));
473   rect = rect * TRect(0, 0, m_dim.lx - 1, m_dim.ly - 1);
474 
475   int lx = rect.getLx(), ly = rect.getLy();
476   TDimension dim(lx, ly);
477 
478   // this is needed since a stop motion live view
479   // doesn't register as a node correctly
480   // there is probably a better way to do this.
481   if (rect.getLx() == 0 && lx == 0) {
482     rect = m_clipRect;
483     dim  = m_dim;
484   }
485 
486   // Build a raster buffer of sufficient size to hold said union.
487   // The buffer is per-thread cached in order to improve the rendering speed.
488   if (!threadBuffers.hasLocalData())
489     threadBuffers.setLocalData(new std::vector<char>());
490 
491   int size = dim.lx * dim.ly * sizeof(TPixel32);
492 
493   std::vector<char> *vbuff = (std::vector<char> *)threadBuffers.localData();
494   if (size > (int)vbuff->size()) vbuff->resize(size);
495 
496   TRaster32P ras(dim.lx, dim.ly, dim.lx, (TPixel32 *)&(*vbuff)[0]);
497   TRaster32P ras2;
498 
499   if (m_vs.m_colorMask != 0) {
500     ras2 = TRaster32P(ras->getSize());
501     ras->clear();
502   } else
503     ras2 = ras;
504 
505   // Clear the buffer - it will hold all the stacked nodes content to be overed
506   // on top of the OpenGL buffer through a glDrawPixel()
507   ras->lock();
508 
509   ras->clear();  // ras is typically reused - and we need it transparent first
510 
511   TRect r                 = rect - rect.getP00();
512   TRaster32P viewedRaster = ras->extract(r);
513 
514   int current = -1;
515 
516   // Retrieve preferences-related data
517   int tc    = m_checkFlags ? ToonzCheck::instance()->getChecks() : 0;
518   int index = ToonzCheck::instance()->getColorIndex();
519 
520   TPixel32 frontOnionColor, backOnionColor;
521   bool onionInksOnly;
522 
523   Preferences::instance()->getOnionData(frontOnionColor, backOnionColor,
524                                         onionInksOnly);
525 
526   // Stack every node on top of the raster buffer
527   for (i = 0; i < nodesCount; ++i) {
528     if (m_nodes[i].m_isCurrentColumn) current = i;
529 
530     TAffine aff         = TTranslation(-rect.x0, -rect.y0) * m_nodes[i].m_aff;
531     TDimension imageDim = m_nodes[i].m_raster->getSize();
532     TPointD offset(0.5, 0.5);
533     aff *= TTranslation(offset);  // very quick and very dirty fix: in
534                                   // camerastand the images seems shifted of an
535                                   // half pixel...it's a quickput approximation?
536 
537     TPixel32 colorscale = TPixel32(0, 0, 0, m_nodes[i].m_alpha);
538     int inksOnly;
539 
540     if (m_nodes[i].m_onionMode != Node::eOnionSkinNone) {
541       inksOnly = onionInksOnly;
542 
543       if (m_nodes[i].m_onionMode == Node::eOnionSkinFront)
544         colorscale = TPixel32(frontOnionColor.r, frontOnionColor.g,
545                               frontOnionColor.b, m_nodes[i].m_alpha);
546       else if (m_nodes[i].m_onionMode == Node::eOnionSkinBack)
547         colorscale = TPixel32(backOnionColor.r, backOnionColor.g,
548                               backOnionColor.b, m_nodes[i].m_alpha);
549     } else {
550       if (m_nodes[i].m_filterColor != TPixel32::Black) {
551         colorscale   = m_nodes[i].m_filterColor;
552         colorscale.m = m_nodes[i].m_alpha;
553       }
554       inksOnly = tc & ToonzCheck::eInksOnly;
555     }
556 
557     if (TRaster32P src32 = m_nodes[i].m_raster)
558       TRop::quickPut(viewedRaster, src32, aff, colorscale,
559                      m_nodes[i].m_doPremultiply, m_nodes[i].m_whiteTransp,
560                      m_nodes[i].m_isFirstColumn, m_doRasterDarkenBlendedView);
561     else if (TRasterGR8P srcGr8 = m_nodes[i].m_raster)
562       TRop::quickPut(viewedRaster, srcGr8, aff, colorscale);
563     else if (TRasterCM32P srcCm = m_nodes[i].m_raster) {
564       assert(m_nodes[i].m_palette);
565       int oldframe = m_nodes[i].m_palette->getFrame();
566       m_nodes[i].m_palette->setFrame(m_nodes[i].m_frame);
567 
568       TPaletteP plt;
569       int styleIndex = -1;
570       if ((tc & ToonzCheck::eGap || tc & ToonzCheck::eAutoclose) &&
571           m_nodes[i].m_isCurrentColumn) {
572         srcCm      = srcCm->clone();
573         plt        = m_nodes[i].m_palette->clone();
574         styleIndex = plt->addStyle(TPixel::Magenta);
575         if (tc & ToonzCheck::eAutoclose)
576           TAutocloser(srcCm, AutocloseDistance, AutocloseAngle, styleIndex,
577                       AutocloseOpacity)
578               .exec();
579         if (tc & ToonzCheck::eGap)
580           AreaFiller(srcCm).rectFill(m_nodes[i].m_savebox, 1, true, true,
581                                      false);
582       } else
583         plt = m_nodes[i].m_palette;
584 
585       if (tc == 0 || tc == ToonzCheck::eBlackBg ||
586           !m_nodes[i].m_isCurrentColumn)
587         TRop::quickPut(viewedRaster, srcCm, plt, aff, colorscale, inksOnly);
588       else {
589         TRop::CmappedQuickputSettings settings;
590 
591         settings.m_globalColorScale = colorscale;
592         settings.m_inksOnly         = inksOnly;
593         settings.m_transparencyCheck =
594             tc & (ToonzCheck::eTransparency | ToonzCheck::eGap);
595         settings.m_blackBgCheck = tc & ToonzCheck::eBlackBg;
596         /*-- InkCheck, Ink#1Check, PaintCheckはカレントカラムにのみ有効 --*/
597         settings.m_inkIndex =
598             m_nodes[i].m_isCurrentColumn
599                 ? (tc & ToonzCheck::eInk ? index
600                                          : (tc & ToonzCheck::eInk1 ? 1 : -1))
601                 : -1;
602         settings.m_paintIndex = m_nodes[i].m_isCurrentColumn
603                                     ? (tc & ToonzCheck::ePaint ? index : -1)
604                                     : -1;
605 
606         Preferences::instance()->getTranspCheckData(
607             settings.m_transpCheckBg, settings.m_transpCheckInk,
608             settings.m_transpCheckPaint);
609 
610         settings.m_isOnionSkin = m_nodes[i].m_onionMode != Node::eOnionSkinNone;
611         settings.m_gapCheckIndex = styleIndex;
612 
613         TRop::quickPut(viewedRaster, srcCm, plt, aff, settings);
614       }
615 
616       srcCm = TRasterCM32P();
617       plt   = TPaletteP();
618 
619       m_nodes[i].m_palette->setFrame(oldframe);
620     } else
621       assert(!"Cannot use quickput with this raster combination!");
622   }
623 
624   if (m_vs.m_colorMask != 0) {
625     TRop::setChannel(ras, ras, m_vs.m_colorMask, false);
626     TRop::quickPut(ras2, ras, TAffine());
627   }
628 
629   // Now, output the raster buffer on top of the OpenGL buffer
630   glPushAttrib(GL_COLOR_BUFFER_BIT);  // Preserve blending and stuff
631 
632   glEnable(GL_BLEND);
633   glBlendFunc(GL_ONE,
634               GL_ONE_MINUS_SRC_ALPHA);  // The raster buffer is intended in
635   // premultiplied form - thus the GL_ONE on src
636   glDisable(GL_DEPTH_TEST);
637   glDisable(GL_DITHER);
638   glDisable(GL_LOGIC_OP);
639 
640 /* disable, since these features are never enabled, and cause OpenGL to assert
641  * on systems that don't support them: see #591 */
642 #if 0
643 #ifdef GL_EXT_convolution
644   if( GLEW_EXT_convolution ) {
645     glDisable(GL_CONVOLUTION_1D_EXT);
646     glDisable(GL_CONVOLUTION_2D_EXT);
647     glDisable(GL_SEPARABLE_2D_EXT);
648   }
649 #endif
650 
651 #ifdef GL_EXT_histogram
652   if( GLEW_EXT_histogram ) {
653     glDisable(GL_HISTOGRAM_EXT);
654     glDisable(GL_MINMAX_EXT);
655   }
656 #endif
657 #endif
658 
659 #ifdef GL_EXT_texture3D
660   if (GL_EXT_texture3D) {
661     glDisable(GL_TEXTURE_3D_EXT);
662   }
663 #endif
664 
665   glPushMatrix();
666   glLoadIdentity();
667 
668   glRasterPos2d(rect.x0, rect.y0);
669   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
670   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
671 
672   glDrawPixels(ras2->getLx(), ras2->getLy(),            // Perform the over
673                TGL_FMT, TGL_TYPE, ras2->getRawData());  //
674 
675   ras->unlock();
676   glPopMatrix();
677 
678   glPopAttrib();  // Restore blending status
679 
680   if (m_vs.m_showBBox && current > -1) {
681     glPushMatrix();
682     glLoadIdentity();
683     tglColor(TPixel(200, 200, 200));
684     tglMultMatrix(m_nodes[current].m_aff);
685     tglDrawRect(m_nodes[current].m_raster->getBounds());
686     glPopMatrix();
687   }
688 
689   m_nodes.clear();
690 }
691 
692 //-----------------------------------------------------------------------------
693 /*! Make frame visualization in QPainter.
694 \n	Draw in painter mode just raster image in m_nodes.
695 \n  Onon-skin or channel mode are not considered.
696 */
drawRasterImages(QPainter & p,QPolygon cameraPol)697 void RasterPainter::drawRasterImages(QPainter &p, QPolygon cameraPol) {
698   if (m_nodes.empty()) return;
699 
700   double delta = sqrt(fabs(m_nodes[0].m_aff.det()));
701   TRectD bbox  = m_nodes[0].m_bbox.enlarge(delta);
702 
703   int i;
704   for (i = 1; i < (int)m_nodes.size(); i++) {
705     delta = sqrt(fabs(m_nodes[i].m_aff.det()));
706     bbox += m_nodes[i].m_bbox.enlarge(delta);
707   }
708   TRect rect(tfloor(bbox.x0), tfloor(bbox.y0), tceil(bbox.x1), tceil(bbox.y1));
709   rect = rect * TRect(0, 0, m_dim.lx - 1, m_dim.ly - 1);
710 
711   TRect r = rect - rect.getP00();
712   TAffine flipY(1, 0, 0, 0, -1, m_dim.ly);
713   p.setClipRegion(QRegion(cameraPol));
714   for (i = 0; i < (int)m_nodes.size(); i++) {
715     if (m_nodes[i].m_onionMode != Node::eOnionSkinNone) continue;
716     p.resetTransform();
717     TRasterP ras = m_nodes[i].m_raster;
718     TAffine aff  = TTranslation(-rect.x0, -rect.y0) * flipY * m_nodes[i].m_aff;
719     QMatrix matrix(aff.a11, aff.a21, aff.a12, aff.a22, aff.a13, aff.a23);
720     QImage image = rasterToQImage(ras);
721     if (image.isNull()) continue;
722     p.setMatrix(matrix);
723     p.drawImage(rect.getP00().x, rect.getP00().y, image);
724   }
725 
726   p.resetTransform();
727   m_nodes.clear();
728 }
729 
buildAutocloseImage(TVectorImage * vaux,TVectorImage * vi,const std::vector<std::pair<int,double>> & startPoints,const std::vector<std::pair<int,double>> & endPoints)730 static void buildAutocloseImage(
731     TVectorImage *vaux, TVectorImage *vi,
732     const std::vector<std::pair<int, double>> &startPoints,
733     const std::vector<std::pair<int, double>> &endPoints) {
734   for (UINT i = 0; i < startPoints.size(); i++) {
735     TThickPoint p1 = vi->getStroke(startPoints[i].first)
736                          ->getThickPoint(startPoints[i].second);
737     TThickPoint p2 =
738         vi->getStroke(endPoints[i].first)->getThickPoint(endPoints[i].second);
739     std::vector<TThickPoint> points(3);
740     points[0]       = p1;
741     points[1]       = 0.5 * (p1 + p2);
742     points[2]       = p2;
743     points[0].thick = points[1].thick = points[2].thick = 0.0;
744     TStroke *auxStroke                                  = new TStroke(points);
745     auxStroke->setStyle(2);
746     vaux->addStroke(auxStroke);
747   }
748 }
749 
750 TEnv::DoubleVar AutocloseFactor("InknpaintAutocloseFactor", 4.0);
751 
drawAutocloses(TVectorImage * vi,TVectorRenderData & rd)752 static void drawAutocloses(TVectorImage *vi, TVectorRenderData &rd) {
753   static TPalette *plt = 0;
754   if (!plt) {
755     plt = new TPalette();
756     plt->addStyle(TPixel::Magenta);
757   }
758 
759   std::vector<std::pair<int, double>> startPoints, endPoints;
760   getClosingPoints(vi->getBBox(), AutocloseFactor, vi, startPoints, endPoints);
761   TVectorImage *vaux = new TVectorImage();
762 
763   rd.m_palette = plt;
764   buildAutocloseImage(vaux, vi, startPoints, endPoints);
765   // temporarily disable fill check, to preserve the gap indicator color
766   bool tCheckEnabledOriginal = rd.m_tcheckEnabled;
767   rd.m_tcheckEnabled         = false;
768   // draw
769   tglDraw(rd, vaux);
770   // restore original value
771   rd.m_tcheckEnabled = tCheckEnabledOriginal;
772   delete vaux;
773 }
774 
775 //-----------------------------------------------------------------------------
776 
777 /*! Take image from \b Stage::Player \b data and recall the right method for
778                 this kind of image, for vector image recall \b onVectorImage(),
779    for raster
780                 image recall \b onRasterImage() for toonz image recall \b
781    onToonzImage().
782 */
onImage(const Stage::Player & player)783 void RasterPainter::onImage(const Stage::Player &player) {
784   if (m_singleColumnEnabled && !player.m_isCurrentColumn) return;
785 
786   // Attempt Plastic-deformed drawing
787   // For now generating icons of plastic-deformed image causes crash as
788   // QOffscreenSurface is created outside the gui thread.
789   // As a quick workaround, ignore the deformation if this is called from
790   // non-gui thread (i.e. icon generator thread)
791   // 12/1/2018 Now the scene icon is rendered without deformation either
792   // since it causes unknown error with opengl contexts..
793   TStageObject *obj =
794       ::plasticDeformedObj(player, m_vs.m_plasticVisualSettings);
795   if (obj && QThread::currentThread() == qGuiApp->thread() &&
796       !m_vs.m_forSceneIcon) {
797     flushRasterImages();
798     ::onPlasticDeformedImage(obj, player, m_vs, m_viewAff);
799   } else {
800     // Common image draw
801     const TImageP &img = player.image();
802 
803     if (TVectorImageP vi = img)
804       onVectorImage(vi.getPointer(), player);
805     else if (TRasterImageP ri = img)
806       onRasterImage(ri.getPointer(), player);
807     else if (TToonzImageP ti = img)
808       onToonzImage(ti.getPointer(), player);
809     else if (TMeshImageP mi = img) {
810       flushRasterImages();
811       ::onMeshImage(mi.getPointer(), player, m_vs, m_viewAff);
812     }
813   }
814 }
815 
816 //-----------------------------------------------------------------------------
817 /*! View a vector cell images.
818 \n	If onion-skin is active compute \b TOnionFader value.
819                 Create and boot a \b TVectorRenderData and recall \b tglDraw().
820 */
onVectorImage(TVectorImage * vi,const Stage::Player & player)821 void RasterPainter::onVectorImage(TVectorImage *vi,
822                                   const Stage::Player &player) {
823   flushRasterImages();
824 
825   // When loaded, vectorimages needs to have regions recomputed, but doing that
826   // while loading them
827   // is quite slow (think about loading whole scenes!..). They are recomputed
828   // the first time they
829   // are selected and shown on screen...except when playing back, to avoid
830   // slowness!
831 
832   // (Daniele) This function should *NOT* be responsible of that.
833   //           It's the *image itself* that should recalculate or initialize
834   //           said data
835   //           if queried about it and turns out it's not available...
836 
837   if (!player.m_isPlaying && player.m_isCurrentColumn)
838     vi->recomputeRegionsIfNeeded();
839 
840   const Preferences &prefs = *Preferences::instance();
841 
842   TColorFunction *cf = 0, *guidedCf = 0;
843   TPalette *vPalette = vi->getPalette();
844   TPixel32 bgColor   = TPixel32::White;
845 
846   int tc = (m_checkFlags && player.m_isCurrentColumn)
847                ? ToonzCheck::instance()->getChecks()
848                : 0;
849   bool inksOnly = tc & ToonzCheck::eInksOnly;
850 
851   int oldFrame = vPalette->getFrame();
852   vPalette->setFrame(player.m_frame);
853 
854   if (player.m_onionSkinDistance != c_noOnionSkin) {
855     TPixel32 frontOnionColor, backOnionColor;
856 
857     if (player.m_onionSkinDistance != 0 &&
858         (!player.m_isShiftAndTraceEnabled ||
859          Preferences::instance()->areOnionColorsUsedForShiftAndTraceGhosts())) {
860       prefs.getOnionData(frontOnionColor, backOnionColor, inksOnly);
861       bgColor =
862           (player.m_onionSkinDistance < 0) ? backOnionColor : frontOnionColor;
863     }
864 
865     double m[4] = {1.0, 1.0, 1.0, 1.0}, c[4];
866 
867     // Weighted addition to RGB and matte multiplication
868     m[3] = 1.0 -
869            ((player.m_onionSkinDistance == 0)
870                 ? 0.1
871                 : OnionSkinMask::getOnionSkinFade(player.m_onionSkinDistance));
872     c[0] = (1.0 - m[3]) * bgColor.r, c[1] = (1.0 - m[3]) * bgColor.g,
873     c[2] = (1.0 - m[3]) * bgColor.b;
874     c[3] = 0.0;
875 
876     cf = new TGenericColorFunction(m, c);
877   } else if (player.m_filterColor != TPixel::Black) {
878     TPixel32 colorScale = player.m_filterColor;
879     colorScale.m        = player.m_opacity;
880     cf                  = new TColumnColorFilterFunction(colorScale);
881   } else if (player.m_opacity < 255)
882     cf = new TTranspFader(player.m_opacity / 255.0);
883 
884   TVectorRenderData rd(m_viewAff * player.m_placement, TRect(), vPalette, cf,
885                        true  // alpha enabled
886   );
887 
888   rd.m_drawRegions           = !inksOnly;
889   rd.m_inkCheckEnabled       = tc & ToonzCheck::eInk;
890   rd.m_ink1CheckEnabled      = tc & ToonzCheck::eInk1;
891   rd.m_paintCheckEnabled     = tc & ToonzCheck::ePaint;
892   rd.m_blackBgEnabled        = tc & ToonzCheck::eBlackBg;
893   rd.m_colorCheckIndex       = ToonzCheck::instance()->getColorIndex();
894   rd.m_show0ThickStrokes     = prefs.getShow0ThickLines();
895   rd.m_regionAntialias       = prefs.getRegionAntialias();
896   rd.m_animatedGuidedDrawing = prefs.getAnimatedGuidedDrawing();
897   if (player.m_onionSkinDistance != 0 &&
898       (player.m_isCurrentColumn || player.m_isCurrentXsheetLevel)) {
899     if (player.m_isGuidedDrawingEnabled == 3         // show guides on all
900         || (player.m_isGuidedDrawingEnabled == 1 &&  // show guides on closest
901             (player.m_onionSkinDistance == player.m_firstBackOnionSkin ||
902              player.m_onionSkinDistance == player.m_firstFrontOnionSkin)) ||
903         (player.m_isGuidedDrawingEnabled == 2 &&  // show guides on farthest
904          (player.m_onionSkinDistance == player.m_onionSkinBackSize ||
905           player.m_onionSkinDistance == player.m_onionSkinFrontSize)) ||
906         (player.m_isEditingLevel &&  // fix for level editing mode sending extra
907                                      // players
908          player.m_isGuidedDrawingEnabled == 2 &&
909          player.m_onionSkinDistance == player.m_lastBackVisibleSkin)) {
910       rd.m_showGuidedDrawing = player.m_isGuidedDrawingEnabled > 0;
911       int currentStrokeCount = 0;
912       int totalStrokes       = vi->getStrokeCount();
913       TXshSimpleLevel *sl    = player.m_sl;
914 
915       if (sl) {
916         TImageP image          = sl->getFrame(player.m_currentFrameId, false);
917         TVectorImageP vecImage = image;
918         if (vecImage) currentStrokeCount = vecImage->getStrokeCount();
919         if (currentStrokeCount < 0) currentStrokeCount = 0;
920         if (player.m_guidedFrontStroke != -1 &&
921             (player.m_onionSkinDistance == player.m_onionSkinFrontSize ||
922              player.m_onionSkinDistance == player.m_firstFrontOnionSkin))
923           rd.m_indexToHighlight = player.m_guidedFrontStroke;
924         else if (player.m_guidedBackStroke != -1 &&
925                  (player.m_onionSkinDistance == player.m_onionSkinBackSize ||
926                   player.m_onionSkinDistance == player.m_firstBackOnionSkin))
927           rd.m_indexToHighlight = player.m_guidedBackStroke;
928         else if (currentStrokeCount < totalStrokes)
929           rd.m_indexToHighlight = currentStrokeCount;
930 
931         double guidedM[4] = {1.0, 1.0, 1.0, 1.0}, guidedC[4];
932         TPixel32 bgColor  = TPixel32::Blue;
933         guidedM[3] =
934             1.0 -
935             ((player.m_onionSkinDistance == 0)
936                  ? 0.1
937                  : OnionSkinMask::getOnionSkinFade(player.m_onionSkinDistance));
938 
939         guidedC[0] = (1.0 - guidedM[3]) * bgColor.r,
940         guidedC[1] = (1.0 - guidedM[3]) * bgColor.g,
941         guidedC[2] = (1.0 - guidedM[3]) * bgColor.b;
942         guidedC[3] = 0.0;
943 
944         guidedCf      = new TGenericColorFunction(guidedM, guidedC);
945         rd.m_guidedCf = guidedCf;
946       }
947     }
948   }
949 
950   if (tc & (ToonzCheck::eTransparency | ToonzCheck::eGap)) {
951     TPixel dummy;
952     rd.m_tcheckEnabled = true;
953 
954     if (rd.m_blackBgEnabled)
955       prefs.getTranspCheckData(rd.m_tCheckInk, dummy, rd.m_tCheckPaint);
956     else
957       prefs.getTranspCheckData(dummy, rd.m_tCheckInk, rd.m_tCheckPaint);
958   }
959 
960   if (m_vs.m_colorMask != 0) {
961     glColorMask((m_vs.m_colorMask & TRop::RChan) ? GL_TRUE : GL_FALSE,
962                 (m_vs.m_colorMask & TRop::GChan) ? GL_TRUE : GL_FALSE,
963                 (m_vs.m_colorMask & TRop::BChan) ? GL_TRUE : GL_FALSE, GL_TRUE);
964   }
965   TVectorImageP viDelete;
966   if (tc & ToonzCheck::eGap) {
967     viDelete = vi->clone();
968     vi       = viDelete.getPointer();
969     vi->selectFill(vi->getBBox(), 0, 1, true, true, false);
970   }
971 
972   TStroke *guidedStroke = 0;
973   if (m_maskLevel > 0)
974     tglDrawMask(rd, vi);
975   else
976     tglDraw(rd, vi, &guidedStroke);
977 
978   if (tc & ToonzCheck::eAutoclose) drawAutocloses(vi, rd);
979 
980   glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
981   vPalette->setFrame(oldFrame);
982 
983   delete cf;
984   delete guidedCf;
985 
986   if (guidedStroke) m_guidedStrokes.push_back(guidedStroke);
987 }
988 
989 //-----------------------------------------------------
990 
991 /*! Create a \b Node and put it in \b m_nodes.
992  */
onRasterImage(TRasterImage * ri,const Stage::Player & player)993 void RasterPainter::onRasterImage(TRasterImage *ri,
994                                   const Stage::Player &player) {
995   TRasterP r = ri->getRaster();
996 
997   TAffine aff;
998   aff = m_viewAff * player.m_placement * player.m_dpiAff;
999   aff = TTranslation(m_dim.lx * 0.5, m_dim.ly * 0.5) * aff *
1000         TTranslation(-r->getCenterD() +
1001                      convert(ri->getOffset()));  // this offset is !=0 only if
1002                                                  // in cleanup camera test mode
1003 
1004   TRectD bbox = TRectD(0, 0, m_dim.lx, m_dim.ly);
1005   bbox *= convert(m_clipRect);
1006   if (bbox.isEmpty()) return;
1007 
1008   int alpha                 = 255;
1009   Node::OnionMode onionMode = Node::eOnionSkinNone;
1010   if (player.m_onionSkinDistance != c_noOnionSkin) {
1011     // GetOnionSkinFade va bene per il vettoriale mentre il raster funziona al
1012     // contrario
1013     // 1 opaco -> 0 completamente trasparente
1014     // inverto quindi il risultato della funzione stando attento al caso 0
1015     // (in cui era scolpito il valore 0.9)
1016     double onionSkiFade = player.m_onionSkinDistance == 0
1017                               ? 0.9
1018                               : (1.0 - OnionSkinMask::getOnionSkinFade(
1019                                            player.m_onionSkinDistance));
1020     alpha = tcrop(tround(onionSkiFade * 255.0), 0, 255);
1021     if (player.m_isShiftAndTraceEnabled &&
1022         !Preferences::instance()->areOnionColorsUsedForShiftAndTraceGhosts())
1023       onionMode = Node::eOnionSkinNone;
1024     else {
1025       onionMode =
1026           (player.m_onionSkinDistance > 0)
1027               ? Node::eOnionSkinFront
1028               : ((player.m_onionSkinDistance < 0) ? Node::eOnionSkinBack
1029                                                   : Node::eOnionSkinNone);
1030     }
1031   } else if (player.m_opacity < 255)
1032     alpha = player.m_opacity;
1033   TXshSimpleLevel *sl = player.m_sl;
1034   bool doPremultiply  = false;
1035   bool whiteTransp    = false;
1036   if (sl) {
1037     LevelProperties *levelProp = sl->getProperties();
1038     if (levelProp->doPremultiply())
1039       doPremultiply = true;
1040     else if (levelProp->whiteTransp())
1041       whiteTransp = true;
1042   }
1043 
1044   bool ignoreAlpha =
1045       (Preferences::instance()->isIgnoreAlphaonColumn1Enabled() &&
1046        player.m_column == 0 &&
1047        isSubsheetChainOnColumn0(sl->getScene()->getTopXsheet(), player.m_xsh,
1048                                 player.m_frame));
1049 
1050   m_nodes.push_back(Node(r, 0, alpha, aff, ri->getSavebox(), bbox,
1051                          player.m_frame, player.m_isCurrentColumn, onionMode,
1052                          doPremultiply, whiteTransp, ignoreAlpha,
1053                          player.m_filterColor));
1054 }
1055 
1056 //-----------------------------------------------------------------------------
1057 /*! Create a \b Node and put it in \b m_nodes.
1058  */
onToonzImage(TToonzImage * ti,const Stage::Player & player)1059 void RasterPainter::onToonzImage(TToonzImage *ti, const Stage::Player &player) {
1060   TRasterCM32P r = ti->getRaster();
1061   if (!ti->getPalette()) return;
1062 
1063   TAffine aff = m_viewAff * player.m_placement * player.m_dpiAff;
1064   aff         = TTranslation(m_dim.lx / 2.0, m_dim.ly / 2.0) * aff *
1065         TTranslation(-r->getCenterD());
1066 
1067   TRectD bbox = TRectD(0, 0, m_dim.lx, m_dim.ly);
1068   bbox *= convert(m_clipRect);
1069   if (bbox.isEmpty()) return;
1070 
1071   int alpha                 = 255;
1072   Node::OnionMode onionMode = Node::eOnionSkinNone;
1073   if (player.m_onionSkinDistance != c_noOnionSkin) {
1074     // GetOnionSkinFade is good for the vector while the raster works at the
1075     //    Opposite 1 opaque -> 0 completely transparent
1076     //    I therefore reverse the result of the function by being attentive to
1077     //    case 0
1078     //    (where the value 0.9 was carved)
1079     double onionSkiFade = player.m_onionSkinDistance == 0
1080                               ? 0.9
1081                               : (1.0 - OnionSkinMask::getOnionSkinFade(
1082                                            player.m_onionSkinDistance));
1083     alpha = tcrop(tround(onionSkiFade * 255.0), 0, 255);
1084 
1085     if (player.m_isShiftAndTraceEnabled &&
1086         !Preferences::instance()->areOnionColorsUsedForShiftAndTraceGhosts())
1087       onionMode = Node::eOnionSkinNone;
1088     else {
1089       onionMode =
1090           (player.m_onionSkinDistance > 0)
1091               ? Node::eOnionSkinFront
1092               : ((player.m_onionSkinDistance < 0) ? Node::eOnionSkinBack
1093                                                   : Node::eOnionSkinNone);
1094     }
1095 
1096   } else if (player.m_opacity < 255)
1097     alpha = player.m_opacity;
1098 
1099   m_nodes.push_back(Node(r, ti->getPalette(), alpha, aff, ti->getSavebox(),
1100                          bbox, player.m_frame, player.m_isCurrentColumn,
1101                          onionMode, false, false, false, player.m_filterColor));
1102 }
1103 
1104 //**********************************************************************************************
1105 //    OpenGLPainter  implementation
1106 //**********************************************************************************************
1107 
OpenGlPainter(const TAffine & viewAff,const TRect & rect,const ImagePainter::VisualSettings & vs,bool isViewer,bool alphaEnabled)1108 OpenGlPainter::OpenGlPainter(const TAffine &viewAff, const TRect &rect,
1109                              const ImagePainter::VisualSettings &vs,
1110                              bool isViewer, bool alphaEnabled)
1111     : Visitor(vs)
1112     , m_viewAff(viewAff)
1113     , m_clipRect(rect)
1114     , m_camera3d(false)
1115     , m_phi(0)
1116     , m_maskLevel(0)
1117     , m_isViewer(isViewer)
1118     , m_alphaEnabled(alphaEnabled)
1119     , m_paletteHasChanged(false)
1120     , m_minZ(0) {}
1121 
1122 //-----------------------------------------------------------------------------
1123 
onImage(const Stage::Player & player)1124 void OpenGlPainter::onImage(const Stage::Player &player) {
1125   if (player.m_z < m_minZ) m_minZ = player.m_z;
1126 
1127   glPushAttrib(GL_ALL_ATTRIB_BITS);
1128   glPushMatrix();
1129 
1130   if (m_camera3d)
1131     glTranslated(
1132         0, 0,
1133         player.m_z);  // Ok, move object along z as specified in the player
1134 
1135   // Attempt Plastic-deformed drawing
1136   if (TStageObject *obj =
1137           ::plasticDeformedObj(player, m_vs.m_plasticVisualSettings))
1138     ::onPlasticDeformedImage(obj, player, m_vs, m_viewAff);
1139   else if (const TImageP &image = player.image()) {
1140     if (TVectorImageP vi = image)
1141       onVectorImage(vi.getPointer(), player);
1142     else if (TRasterImageP ri = image)
1143       onRasterImage(ri.getPointer(), player);
1144     else if (TToonzImageP ti = image)
1145       onToonzImage(ti.getPointer(), player);
1146     else if (TMeshImageP mi = image)
1147       onMeshImage(mi.getPointer(), player, m_vs, m_viewAff);
1148   }
1149 
1150   glPopMatrix();
1151   glPopAttrib();
1152 }
1153 
1154 //-----------------------------------------------------------------------------
1155 
onVectorImage(TVectorImage * vi,const Stage::Player & player)1156 void OpenGlPainter::onVectorImage(TVectorImage *vi,
1157                                   const Stage::Player &player) {
1158   if (m_camera3d && (player.m_onionSkinDistance == c_noOnionSkin ||
1159                      player.m_onionSkinDistance == 0)) {
1160     const TRectD &bbox = player.m_placement * player.m_dpiAff * vi->getBBox();
1161     draw3DShadow(bbox, player.m_z, m_phi);
1162   }
1163 
1164   TColorFunction *cf = 0;
1165   TOnionFader fader;
1166 
1167   TPalette *vPalette = vi->getPalette();
1168 
1169   int oldFrame = vPalette->getFrame();
1170   vPalette->setFrame(player.m_frame);  // Hehe. Should be locking
1171                                        // vPalette's mutex here...
1172   if (player.m_onionSkinDistance != c_noOnionSkin) {
1173     TPixel32 bgColor = TPixel32::White;
1174     fader            = TOnionFader(
1175         bgColor, OnionSkinMask::getOnionSkinFade(player.m_onionSkinDistance));
1176     cf = &fader;
1177   }
1178 
1179   TVectorRenderData rd =
1180       isViewer() ? TVectorRenderData(TVectorRenderData::ViewerSettings(),
1181                                      m_viewAff * player.m_placement, m_clipRect,
1182                                      vPalette)
1183                  : TVectorRenderData(TVectorRenderData::ProductionSettings(),
1184                                      m_viewAff * player.m_placement, m_clipRect,
1185                                      vPalette);
1186 
1187   rd.m_alphaChannel = m_alphaEnabled;
1188   rd.m_is3dView     = m_camera3d;
1189 
1190   if (m_maskLevel > 0)
1191     tglDrawMask(rd, vi);
1192   else
1193     tglDraw(rd, vi);
1194 
1195   vPalette->setFrame(oldFrame);
1196 }
1197 
1198 //-----------------------------------------------------------------------------
1199 
onRasterImage(TRasterImage * ri,const Stage::Player & player)1200 void OpenGlPainter::onRasterImage(TRasterImage *ri,
1201                                   const Stage::Player &player) {
1202   if (m_camera3d && (player.m_onionSkinDistance == c_noOnionSkin ||
1203                      player.m_onionSkinDistance == 0)) {
1204     TRectD bbox(ri->getBBox());
1205 
1206     // Since bbox is in image coordinates, adjust to level coordinates first
1207     bbox -= ri->getRaster()->getCenterD() - convert(ri->getOffset());
1208 
1209     // Then, to world coordinates
1210     bbox = player.m_placement * player.m_dpiAff * bbox;
1211 
1212     draw3DShadow(bbox, player.m_z, m_phi);
1213   }
1214 
1215   bool tlvFlag = player.m_sl && player.m_sl->getType() == TZP_XSHLEVEL;
1216 
1217   if (tlvFlag &&
1218       m_paletteHasChanged)  // o.o! Raster images here - should never be
1219     assert(false);          // dealing with tlv stuff!
1220 
1221   bool premultiplied = tlvFlag;  // xD
1222   static std::vector<char>
1223       matteChan;  // Wtf this is criminal. Although probably this
1224                   // stuff is used only in the main thread... hmmm....
1225   TRaster32P r = (TRaster32P)ri->getRaster();
1226   r->lock();
1227 
1228   if (c_noOnionSkin != player.m_onionSkinDistance) {
1229     double fade =
1230         0.5 - 0.45 * (1 - 1 / (1 + 0.15 * abs(player.m_onionSkinDistance)));
1231     if ((int)matteChan.size() < r->getLx() * r->getLy())
1232       matteChan.resize(r->getLx() * r->getLy());
1233 
1234     int k = 0;
1235     for (int y = 0; y < r->getLy(); ++y) {
1236       TPixel32 *pix    = r->pixels(y);
1237       TPixel32 *endPix = pix + r->getLx();
1238 
1239       while (pix < endPix) {
1240         matteChan[k++] = pix->m;
1241         pix->m         = (int)(pix->m * fade);
1242         pix++;
1243       }
1244     }
1245 
1246     premultiplied = false;  // pfff
1247   }
1248 
1249   TAffine aff = player.m_dpiAff;
1250 
1251   glPushAttrib(GL_ALL_ATTRIB_BITS);
1252   tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1253 
1254   GLRasterPainter::drawRaster(m_viewAff * player.m_placement * aff *
1255                                   TTranslation(convert(ri->getOffset())),
1256                               ri, premultiplied);
1257 
1258   glPopAttrib();
1259 
1260   if (c_noOnionSkin != player.m_onionSkinDistance) {
1261     int k = 0;
1262     for (int y = 0; y < r->getLy(); ++y) {
1263       TPixel32 *pix    = r->pixels(y);
1264       TPixel32 *endPix = pix + r->getLx();
1265 
1266       while (pix < endPix) pix++->m = matteChan[k++];
1267     }
1268   }
1269 
1270   r->unlock();
1271 }
1272 
1273 //-----------------------------------------------------------------------------
1274 
onToonzImage(TToonzImage * ti,const Stage::Player & player)1275 void OpenGlPainter::onToonzImage(TToonzImage *ti, const Stage::Player &player) {
1276   if (m_camera3d && (player.m_onionSkinDistance == c_noOnionSkin ||
1277                      player.m_onionSkinDistance == 0)) {
1278     TRectD bbox(ti->getBBox());
1279 
1280     bbox -= ti->getRaster()->getCenterD();
1281     bbox = player.m_placement * player.m_dpiAff * bbox;
1282 
1283     draw3DShadow(bbox, player.m_z, m_phi);
1284   }
1285 
1286   TRasterCM32P ras = ti->getRaster();
1287   TRaster32P ras32(ras->getSize());
1288   ras32->fill(TPixel32(0, 0, 0, 0));
1289 
1290   // onionSkin
1291   TRop::quickPut(ras32, ras, ti->getPalette(), TAffine());
1292   TAffine aff = player.m_dpiAff;
1293 
1294   TRasterImageP ri(ras32);
1295 
1296   GLRasterPainter::drawRaster(m_viewAff * player.m_placement * aff, ri, true);
1297 }
1298 
1299 //-----------------------------------------------------------------------------
1300 
beginMask()1301 void OpenGlPainter::beginMask() {
1302   ++m_maskLevel;
1303   TStencilControl::instance()->beginMask();
1304 }
endMask()1305 void OpenGlPainter::endMask() {
1306   --m_maskLevel;
1307   TStencilControl::instance()->endMask();
1308 }
enableMask()1309 void OpenGlPainter::enableMask() {
1310   TStencilControl::instance()->enableMask(TStencilControl::SHOW_INSIDE);
1311 }
disableMask()1312 void OpenGlPainter::disableMask() {
1313   TStencilControl::instance()->disableMask();
1314 }
1315 
1316 //**********************************************************************************************
1317 //    Plastic functions  implementation
1318 //**********************************************************************************************
1319 
1320 namespace {
1321 
plasticDeformedObj(const Stage::Player & player,const PlasticVisualSettings & pvs)1322 TStageObject *plasticDeformedObj(const Stage::Player &player,
1323                                  const PlasticVisualSettings &pvs) {
1324   struct locals {
1325     static inline bool isDeformableMeshColumn(
1326         TXshColumn *column, const PlasticVisualSettings &pvs) {
1327       return (column->getColumnType() == TXshColumn::eMeshType) &&
1328              (pvs.m_showOriginalColumn != column);
1329     }
1330 
1331     static inline bool isDeformableMeshLevel(TXshSimpleLevel *sl) {
1332       return sl && (sl->getType() == MESH_XSHLEVEL);
1333     }
1334   };  // locals
1335 
1336   if (pvs.m_applyPlasticDeformation && player.m_column >= 0) {
1337     // Check whether the player's column is a direct stage-schematic child of a
1338     // mesh object
1339     TStageObject *playerObj =
1340         player.m_xsh->getStageObject(TStageObjectId::ColumnId(player.m_column));
1341     assert(playerObj);
1342 
1343     const TStageObjectId &parentId = playerObj->getParent();
1344     if (parentId.isColumn() && playerObj->getParentHandle()[0] != 'H') {
1345       TXshColumn *parentCol = player.m_xsh->getColumn(parentId.getIndex());
1346 
1347       if (locals::isDeformableMeshColumn(parentCol, pvs) &&
1348           !locals::isDeformableMeshLevel(player.m_sl)) {
1349         const SkDP &sd = player.m_xsh->getStageObject(parentId)
1350                              ->getPlasticSkeletonDeformation();
1351 
1352         const TXshCell &parentCell =
1353             player.m_xsh->getCell(player.m_frame, parentId.getIndex());
1354         TXshSimpleLevel *parentSl = parentCell.getSimpleLevel();
1355 
1356         if (sd && locals::isDeformableMeshLevel(parentSl)) return playerObj;
1357       }
1358     }
1359   }
1360 
1361   return 0;
1362 }
1363 
1364 //-----------------------------------------------------------------------------
1365 
onMeshImage(TMeshImage * mi,const Stage::Player & player,const ImagePainter::VisualSettings & vs,const TAffine & viewAff)1366 void onMeshImage(TMeshImage *mi, const Stage::Player &player,
1367                  const ImagePainter::VisualSettings &vs,
1368                  const TAffine &viewAff) {
1369   assert(mi);
1370 
1371   static const double soMinColor[4]  = {0.0, 0.0, 0.0,
1372                                        0.6};  // Translucent black
1373   static const double soMaxColor[4]  = {1.0, 1.0, 1.0,
1374                                        0.6};  // Translucent white
1375   static const double rigMinColor[4] = {0.0, 1.0, 0.0,
1376                                         0.6};  // Translucent green
1377   static const double rigMaxColor[4] = {1.0, 0.0, 0.0, 0.6};  // Translucent red
1378 
1379   bool doOnionSkin    = (player.m_onionSkinDistance != c_noOnionSkin);
1380   bool onionSkinImage = doOnionSkin && (player.m_onionSkinDistance != 0);
1381   bool drawMeshes =
1382       vs.m_plasticVisualSettings.m_drawMeshesWireframe && !onionSkinImage;
1383   bool drawSO = vs.m_plasticVisualSettings.m_drawSO && !onionSkinImage;
1384   bool drawRigidity =
1385       vs.m_plasticVisualSettings.m_drawRigidity && !onionSkinImage;
1386 
1387   // Currently skipping onion skinned meshes
1388   if (onionSkinImage) return;
1389 
1390   // Build dpi
1391   TPointD meshSlDpi(player.m_sl->getDpi(player.m_fid, 0));
1392   assert(meshSlDpi.x != 0.0 && meshSlDpi.y != 0.0);
1393 
1394   // Build reference change affines
1395 
1396   const TAffine &worldMeshToMeshAff =
1397       TScale(meshSlDpi.x / Stage::inch, meshSlDpi.y / Stage::inch);
1398   const TAffine &meshToWorldMeshAff =
1399       TScale(Stage::inch / meshSlDpi.x, Stage::inch / meshSlDpi.y);
1400   const TAffine &worldMeshToWorldAff = player.m_placement;
1401 
1402   const TAffine &meshToWorldAff = worldMeshToWorldAff * meshToWorldMeshAff;
1403 
1404   // Prepare OpenGL
1405   glEnable(GL_BLEND);
1406   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1407   glEnable(GL_LINE_SMOOTH);
1408 
1409   // Push mesh coordinates
1410   glPushMatrix();
1411   tglMultMatrix(viewAff * meshToWorldAff);
1412 
1413   // Fetch deformation
1414   PlasticSkeletonDeformation *deformation = 0;
1415   double sdFrame;
1416 
1417   if (vs.m_plasticVisualSettings.m_applyPlasticDeformation &&
1418       player.m_column >= 0) {
1419     TXshColumn *column = player.m_xsh->getColumn(player.m_column);
1420 
1421     if (column != vs.m_plasticVisualSettings.m_showOriginalColumn) {
1422       TStageObject *playerObj = player.m_xsh->getStageObject(
1423           TStageObjectId::ColumnId(player.m_column));
1424 
1425       deformation = playerObj->getPlasticSkeletonDeformation().getPointer();
1426       sdFrame     = playerObj->paramsTime(player.m_frame);
1427     }
1428   }
1429 
1430   if (deformation) {
1431     // Retrieve the associated plastic deformers data (this may eventually
1432     // update the deforms)
1433     const PlasticDeformerDataGroup *dataGroup =
1434         PlasticDeformerStorage::instance()->process(
1435             sdFrame, mi, deformation, deformation->skeletonId(sdFrame),
1436             worldMeshToMeshAff);
1437 
1438     // Draw faces first
1439     if (drawSO)
1440       tglDrawSO(*mi, (double *)soMinColor, (double *)soMaxColor, dataGroup,
1441                 true);
1442 
1443     if (drawRigidity)
1444       tglDrawRigidity(*mi, (double *)rigMinColor, (double *)rigMaxColor,
1445                       dataGroup, true);
1446 
1447     // Draw edges next
1448     if (drawMeshes) {
1449       glColor4d(0.0, 1.0, 0.0, 0.7 * player.m_opacity / 255.0);  // Green
1450       tglDrawEdges(*mi, dataGroup);  // The mesh must be deformed
1451     }
1452   } else {
1453     // Draw un-deformed data
1454 
1455     // Draw faces first
1456     if (drawSO) tglDrawSO(*mi, (double *)soMinColor, (double *)soMaxColor);
1457 
1458     if (drawRigidity)
1459       tglDrawRigidity(*mi, (double *)rigMinColor, (double *)rigMaxColor);
1460 
1461     // Just draw the mesh image next
1462     if (drawMeshes) {
1463       glColor4d(0.0, 1.0, 0.0, 0.7 * player.m_opacity / 255.0);  // Green
1464       tglDrawEdges(*mi);
1465     }
1466   }
1467 
1468   // Cleanup OpenGL
1469   glPopMatrix();
1470 
1471   glDisable(GL_LINE_SMOOTH);
1472   glDisable(GL_BLEND);
1473 }
1474 
1475 //-----------------------------------------------------------------------------
1476 
1477 //! Applies Plastic deformation of the specified player's stage object.
onPlasticDeformedImage(TStageObject * playerObj,const Stage::Player & player,const ImagePainter::VisualSettings & vs,const TAffine & viewAff)1478 void onPlasticDeformedImage(TStageObject *playerObj,
1479                             const Stage::Player &player,
1480                             const ImagePainter::VisualSettings &vs,
1481                             const TAffine &viewAff) {
1482   bool doOnionSkin    = (player.m_onionSkinDistance != c_noOnionSkin);
1483   bool onionSkinImage = doOnionSkin && (player.m_onionSkinDistance != 0);
1484 
1485   // Deal with color scaling due to transparency / onion skin
1486   double pixScale[4] = {1.0, 1.0, 1.0, 1.0};
1487 
1488   if (doOnionSkin) {
1489     if (onionSkinImage) {
1490       TPixel32 frontOnionColor, backOnionColor;
1491       bool inksOnly;
1492 
1493       Preferences::instance()->getOnionData(frontOnionColor, backOnionColor,
1494                                             inksOnly);
1495 
1496       const TPixel32 &refColor =
1497           (player.m_onionSkinDistance < 0) ? backOnionColor : frontOnionColor;
1498 
1499       pixScale[3] =
1500           1.0 - OnionSkinMask::getOnionSkinFade(player.m_onionSkinDistance);
1501       pixScale[0] =
1502           (refColor.r / 255.0) * pixScale[3];  // refColor is not premultiplied
1503       pixScale[1] = (refColor.g / 255.0) * pixScale[3];
1504       pixScale[2] = (refColor.b / 255.0) * pixScale[3];
1505     }
1506   } else if (player.m_opacity < 255) {
1507     pixScale[3] = player.m_opacity / 255.0;
1508     pixScale[0] = pixScale[1] = pixScale[2] = 0.0;
1509   }
1510 
1511   // Build the Mesh-related data
1512   const TXshCell &cell =
1513       player.m_xsh->getCell(player.m_frame, playerObj->getParent().getIndex());
1514 
1515   TXshSimpleLevel *meshLevel = cell.getSimpleLevel();
1516   const TFrameId &meshFid    = cell.getFrameId();
1517 
1518   const TMeshImageP &mi = meshLevel->getFrame(meshFid, false);
1519   if (!mi) return;
1520 
1521   // Build deformation-related data
1522   TStageObject *parentObj =
1523       player.m_xsh->getStageObject(playerObj->getParent());
1524   assert(playerObj);
1525 
1526   const PlasticSkeletonDeformationP &deformation =
1527       parentObj->getPlasticSkeletonDeformation().getPointer();
1528   assert(deformation);
1529 
1530   double sdFrame = parentObj->paramsTime(player.m_frame);
1531 
1532   // Build dpis
1533 
1534   TPointD meshSlDpi(meshLevel->getDpi(meshFid, 0));
1535   assert(meshSlDpi.x != 0.0 && meshSlDpi.y != 0.0);
1536 
1537   TPointD slDpi(player.m_sl ? player.m_sl->getDpi(player.m_fid, 0) : TPointD());
1538   if (slDpi.x == 0.0 || slDpi.y == 0.0 ||
1539       player.m_sl->getType() == PLI_XSHLEVEL)
1540     slDpi.x = slDpi.y = Stage::inch;
1541 
1542   // Build reference transforms
1543 
1544   const TAffine &worldTextureToWorldMeshAff =
1545       playerObj->getLocalPlacement(player.m_frame);
1546   const TAffine &worldTextureToWorldAff = player.m_placement;
1547 
1548   if (fabs(worldTextureToWorldMeshAff.det()) < 1e-6)
1549     return;  // Skip near-singular mesh/texture placements
1550 
1551   const TAffine &worldMeshToWorldTextureAff = worldTextureToWorldMeshAff.inv();
1552   const TAffine &worldMeshToWorldAff =
1553       worldTextureToWorldAff * worldMeshToWorldTextureAff;
1554 
1555   const TAffine &meshToWorldMeshAff =
1556       TScale(Stage::inch / meshSlDpi.x, Stage::inch / meshSlDpi.y);
1557   const TAffine &worldMeshToMeshAff =
1558       TScale(meshSlDpi.x / Stage::inch, meshSlDpi.y / Stage::inch);
1559   const TAffine &worldTextureToTextureAff = TScale(
1560       slDpi.x / Stage::inch,
1561       slDpi.y /
1562           Stage::inch);  // ::getDpiScale().inv() should be used instead...
1563 
1564   const TAffine &meshToWorldAff   = worldMeshToWorldAff * meshToWorldMeshAff;
1565   const TAffine &meshToTextureAff = worldTextureToTextureAff *
1566                                     worldMeshToWorldTextureAff *
1567                                     meshToWorldMeshAff;
1568 
1569   // Retrieve a drawable texture from the player's simple level
1570   const DrawableTextureDataP &texData = player.texture();
1571   if (!texData) return;
1572 
1573   // Retrieve the associated plastic deformers data (this may eventually update
1574   // the deforms)
1575   const PlasticDeformerDataGroup *dataGroup =
1576       PlasticDeformerStorage::instance()->process(
1577           sdFrame, mi.getPointer(), deformation.getPointer(),
1578           deformation->skeletonId(sdFrame), worldMeshToMeshAff);
1579   assert(dataGroup);
1580 
1581   // Set up OpenGL stuff
1582   glEnable(GL_BLEND);
1583   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1584   glEnable(GL_LINE_SMOOTH);
1585 
1586   // Push mesh coordinates
1587   glPushMatrix();
1588 
1589   tglMultMatrix(viewAff * meshToWorldAff);
1590 
1591   glEnable(GL_TEXTURE_2D);
1592 
1593   // Applying modulation by the specified transparency parameter
1594 
1595   glColor4d(pixScale[3], pixScale[3], pixScale[3], pixScale[3]);
1596   tglDraw(*mi, *texData, meshToTextureAff, *dataGroup);
1597 
1598   glDisable(GL_TEXTURE_2D);
1599 
1600   if (onionSkinImage) {
1601     glBlendFunc(GL_ONE, GL_ONE);
1602 
1603     // Add Onion skin color. Observe that this way we don't consider blending
1604     // with the texture's
1605     // alpha - to obtain that, there is no simple way...
1606 
1607     double k = (1.0 - pixScale[3]);
1608     glColor4d(k * pixScale[0], k * pixScale[1], k * pixScale[2], 0.0);
1609     tglDrawFaces(*mi, dataGroup);
1610 
1611     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1612   }
1613 
1614   glPopMatrix();
1615 
1616   glDisable(GL_LINE_SMOOTH);
1617   glDisable(GL_BLEND);
1618   assert(glGetError() == GL_NO_ERROR);
1619 }
1620 
1621 }  // namespace
1622