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