1 
2 
3 // TnzCore includes
4 #include "tstencilcontrol.h"
5 #include "tvectorgl.h"
6 #include "tvectorrenderdata.h"
7 #include "tcolorfunctions.h"
8 #include "tpalette.h"
9 #include "tropcm.h"
10 #include "trasterimage.h"
11 #include "tvectorimage.h"
12 #include "tcolorstyles.h"
13 #include "timage_io.h"
14 #include "tofflinegl.h"
15 #include "tpixelutils.h"
16 #include "tstroke.h"
17 #include "tenv.h"
18 #include "tregion.h"
19 
20 // TnzLib includes
21 #include "toonz/stage2.h"
22 #include "toonz/stageplayer.h"
23 #include "toonz/stagevisitor.h"
24 #include "toonz/txsheet.h"
25 #include "toonz/txshsimplelevel.h"
26 #include "toonz/txshchildlevel.h"
27 #include "toonz/txshcolumn.h"
28 #include "toonz/txshcell.h"
29 #include "toonz/onionskinmask.h"
30 #include "toonz/dpiscale.h"
31 #include "toonz/imagemanager.h"
32 #include "toonz/tstageobjecttree.h"
33 #include "toonz/preferences.h"
34 #include "toonz/fill.h"
35 #include "toonz/levelproperties.h"
36 #include "toonz/autoclose.h"
37 #include "toonz/txshleveltypes.h"
38 #include "imagebuilders.h"
39 
40 // Qt includes
41 #include <QImage>
42 #include <QPainter>
43 #include <QPolygon>
44 #include <QThreadStorage>
45 #include <QMatrix>
46 
47 #include "toonz/stage.h"
48 
49 //#define  NUOVO_ONION
50 
51 //=============================================================================
52 /*! \namespace Stage
53                 \brief The Stage namespace provides a set of objects, class and
54    method, useful to view images in display.
55 */
56 using namespace Stage;
57 
58 //=============================================================================
59 
60 typedef std::vector<Player> PlayerSet;
61 
62 //=============================================================================
63 
64 /*! \var Stage::inch
65                 For historical reasons camera stand is defined in a coordinate
66    system in which
67                 an inch is equal to 'Stage::inch' unit.
68                 Pay attention: modify this value condition apparent line
69    thickness of
70                 images .pli.
71 */
72 const double Stage::inch        = 53.33333;
73 const double Stage::standardDpi = 120;
74 
75 namespace {
updateOnionSkinSize(const PlayerSet & players)76 void updateOnionSkinSize(const PlayerSet &players) {
77   assert(Player::m_onionSkinFrontSize == 0 && Player::m_onionSkinBackSize == 0);
78   int i;
79   int maxOnionSkinFrontValue = 0, maxOnionSkinBackValue = 0,
80       firstFrontOnionSkin = 0, firstBackOnionSkin = 0, lastBackVisibleSkin = 0;
81   for (i = 0; i < (int)players.size(); i++) {
82     Player player = players[i];
83     if (player.m_onionSkinDistance == c_noOnionSkin) continue;
84     if (player.m_onionSkinDistance > 0 &&
85         maxOnionSkinFrontValue < player.m_onionSkinDistance)
86       maxOnionSkinFrontValue = player.m_onionSkinDistance;
87     if (player.m_onionSkinDistance < 0 &&
88         maxOnionSkinBackValue > player.m_onionSkinDistance)
89       maxOnionSkinBackValue = player.m_onionSkinDistance;
90     if (firstFrontOnionSkin == 0 && player.m_onionSkinDistance > 0)
91       firstFrontOnionSkin = player.m_onionSkinDistance;
92     else if (player.m_onionSkinDistance > 0 &&
93              firstFrontOnionSkin > player.m_onionSkinDistance)
94       firstFrontOnionSkin = player.m_onionSkinDistance;
95 
96     if (firstBackOnionSkin == 0 && player.m_onionSkinDistance < 0)
97       firstBackOnionSkin = player.m_onionSkinDistance;
98     else if (player.m_onionSkinDistance < 0 &&
99              firstBackOnionSkin < player.m_onionSkinDistance)
100       firstBackOnionSkin = player.m_onionSkinDistance;
101     // Level Editing Mode can send players at a depth beyond what is visible.
102     if (player.m_onionSkinDistance < lastBackVisibleSkin &&
103         player.m_isVisibleinOSM)
104       lastBackVisibleSkin = player.m_onionSkinDistance;
105   }
106   Player::m_onionSkinFrontSize  = maxOnionSkinFrontValue;
107   Player::m_onionSkinBackSize   = maxOnionSkinBackValue;
108   Player::m_firstFrontOnionSkin = firstFrontOnionSkin;
109   Player::m_firstBackOnionSkin  = firstBackOnionSkin;
110   Player::m_lastBackVisibleSkin = lastBackVisibleSkin;
111 }
112 
113 //-----------------------------------------------------------------------------
114 
descending(int i,int j)115 bool descending(int i, int j) { return (i > j); }
116 
117 //----------------------------------------------------------------
118 }  // namespace
119 
120 //=============================================================================
121 /*! The ZPlacement class preserve camera position information.
122  */
123 //=============================================================================
124 
125 class ZPlacement {
126 public:
127   TAffine m_aff;
128   double m_z;
ZPlacement()129   ZPlacement() : m_aff(), m_z(0) {}
ZPlacement(const TAffine & aff,double z)130   ZPlacement(const TAffine &aff, double z) : m_aff(aff), m_z(z) {}
131 };
132 
133 //=============================================================================
134 /*! The class PlayerLt allows a priority operator for player.
135 \n	Compare zdepth of two players.
136 */
137 //=============================================================================
138 
139 class PlayerLt {
140 public:
PlayerLt()141   PlayerLt() {}
operator ()(const Player & a,const Player & b) const142   inline bool operator()(const Player &a, const Player &b) const {
143     if (a.m_bingoOrder < b.m_bingoOrder)
144       return true;
145     else if (a.m_bingoOrder > b.m_bingoOrder)
146       return false;
147     return a.m_z < b.m_z;
148   }
149 };
150 
151 //=============================================================================
152 
153 class StackingOrder {
154 public:
StackingOrder()155   StackingOrder() {}
operator ()(const std::pair<double,int> & a,const std::pair<double,int> & b) const156   inline bool operator()(const std::pair<double, int> &a,
157                          const std::pair<double, int> &b) const {
158     return a.first < b.first;
159   }
160 };
161 
162 //=============================================================================
163 /*! The StageBuilder class finds and provides data for frame visualization.
164 \n	The class contains a \b PlayerSet, a vector of player, of all
165 necessary information.
166 */
167 // Tutto cio che riguarda una "colonna maschera" non e' utilizzato in TOONZ ma
168 // in TAB Pro.
169 //=============================================================================
170 
171 class StageBuilder {
172 public:
173   struct SubXSheet {
174     ZPlacement m_camera;
175     TAffine m_aff, m_zaff;
176     double m_z;
177   };
178 
179 public:
180   enum ShiftTraceGhostId { NO_GHOST, FIRST_GHOST, SECOND_GHOST, TRACED };
181 
182 public:
183   PlayerSet m_players;
184   std::vector<PlayerSet *> m_maskPool;
185 
186   std::vector<ZPlacement> m_placementStack;
187   ZPlacement m_cameraPlacement;
188 
189   std::vector<SubXSheet> m_subXSheetStack;
190 
191   std::vector<int> m_masks;
192   int m_onionSkinDistance;
193   double m_fade;
194 
195   ShiftTraceGhostId m_shiftTraceGhostId;
196 
197   bool m_camera3d;
198   OnionSkinMask m_onionSkinMask;
199 
200   int m_currentColumnIndex;   // application's current column index
201   int m_ancestorColumnIndex;  // index of the column that is the deepest
202                               // ancestor of the column being processed
203   int m_currentXsheetLevel;   // level of the current xsheet, see: editInPlace
204   int m_xsheetLevel;          // xsheet-level of the column being processed
205 
206   // for guided drawing
207   TFrameId m_currentFrameId;
208   int m_isGuidedDrawingEnabled;
209   int m_guidedFrontStroke = -1;
210   int m_guidedBackStroke  = -1;
211   std::vector<TXshColumn *> m_ancestors;
212 
213   const ImagePainter::VisualSettings *m_vs;
214 
215 #if defined(x64)
216   TRasterImageP m_liveViewImage;
217   TRasterImageP m_lineupImage;
218   Stage::Player m_liveViewPlayer;
219   Stage::Player m_lineupPlayer;
220 #endif
221 
222 public:
223   StageBuilder();
224   virtual ~StageBuilder();
225 
226   /*! Add in \b players vector information about specify cell.
227   \n	Analyze cell in row \b row and column \b col of xsheet \b xsh. If level
228                   containing this cell is a simple level, \b TXshSimpleLevel,
229   create a Pleyer
230                   object with information of the cell. Else if level is a child
231   level,
232                   \b TXshChildLevel, recall \b addFrame().
233   */
234   void addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh, int row,
235                int col, int level, int subSheetColIndex = -1);
236 
237   /*! Verify if onion-skin is active and recall \b addCell().
238   \n	Compute the distance between each cell with active onion-skin and
239   current
240                   cell and recall \b addCell(). If onion-skin is not active
241   recall \b addCell()
242                   with argument current cell.
243   */
244   void addCellWithOnionSkin(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
245                             int row, int col, int level,
246                             int subSheetColIndex = -1);
247 
248   /*!Recall \b addCellWithOnionSkin() for each cell of row \b row.*/
249   // if subSheetColIndex >= 0 it means that this function is called on visiting
250   // subxsheet images and it indicates the column index in the current (parent)
251   // xsheet. Checking options (like Fill Check etc.) will then valuate this
252   // index to identify if the column is current.
253   void addFrame(PlayerSet &players, ToonzScene *scene, TXsheet *xsh, int row,
254                 int level, bool includeUnvisible, bool checkPreviewVisibility,
255                 int subSheetColIndex = -1);
256 
257   /*! Add in \b players vector information about \b level cell with \b TFrameId
258   \b fid.
259   \n	Compute information for all cell with active onion-skin, if onion-skin
260   is not active
261                   compute information only for current cell.
262   */
263   void addSimpleLevelFrame(PlayerSet &players, TXshSimpleLevel *level,
264                            const TFrameId &fid);
265 
266   /*! Recall \b visitor.onImage(player) for each \b player contained in \b
267    * players vector.
268    */
269   void visit(PlayerSet &players, Visitor &visitor, bool isPlaying = false);
270 
271 // debug!
272 #ifdef _DEBUG
273   void dumpPlayerSet(PlayerSet &players, std::ostream &out);
274   void dumpAll(std::ostream &out);
275 #endif
276 };
277 
278 //-----------------------------------------------------------------------------
279 
StageBuilder()280 StageBuilder::StageBuilder()
281     : m_onionSkinDistance(c_noOnionSkin)
282     , m_camera3d(false)
283     , m_currentColumnIndex(-1)
284     , m_ancestorColumnIndex(-1)
285     , m_fade(0)
286     , m_shiftTraceGhostId(NO_GHOST)
287     , m_currentXsheetLevel(0)
288     , m_xsheetLevel(0) {
289   m_placementStack.push_back(ZPlacement());
290 }
291 
292 //-----------------------------------------------------------------------------
293 
~StageBuilder()294 StageBuilder::~StageBuilder() { clearPointerContainer(m_maskPool); }
295 
296 //-----------------------------------------------------------------------------
297 
298 #ifdef _DEBUG
dumpPlayerSet(PlayerSet & players,std::ostream & out)299 void StageBuilder::dumpPlayerSet(PlayerSet &players, std::ostream &out) {
300   out << "[";
301   int m = players.size();
302   for (int i = 0; i < m; i++) {
303     if (i > 0) out << " ";
304     const Player &player = players[i];
305     out << "img";
306     if (!player.m_masks.empty()) {
307       out << "(" << player.m_masks[0];
308       for (unsigned int j = 1; j < player.m_masks.size(); j++)
309         out << "," << player.m_masks[j];
310       out << ")";
311     }
312   }
313   out << "]";
314 }
315 
316 //-----------------------------------------------------------------------------
317 
dumpAll(std::ostream & out)318 void StageBuilder::dumpAll(std::ostream &out) {
319   dumpPlayerSet(m_players, out);
320   for (unsigned int i = 0; i < m_maskPool.size(); i++) {
321     if (i > 0) out << " ";
322     out << " mask:";
323     dumpPlayerSet(*m_maskPool[i], out);
324   }
325 }
326 #endif
327 
328 //-----------------------------------------------------------------------------
329 
addCell(PlayerSet & players,ToonzScene * scene,TXsheet * xsh,int row,int col,int level,int subSheetColIndex)330 void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
331                            int row, int col, int level, int subSheetColIndex) {
332   // Local functions
333   struct locals {
334     static inline bool isMeshDeformed(TXsheet *xsh, TStageObject *obj,
335                                       const ImagePainter::VisualSettings *vs) {
336       const TStageObjectId &parentId = obj->getParent();
337       if (parentId.isColumn() && obj->getParentHandle()[0] != 'H') {
338         TStageObject *parentObj = xsh->getStageObject(parentId);
339         if (!parentObj->getPlasticSkeletonDeformation()) return false;
340 
341         TXshColumn *parentCol = xsh->getColumn(parentId.getIndex());
342         return (parentCol->getColumnType() == TXshColumn::eMeshType) &&
343                (parentCol != vs->m_plasticVisualSettings.m_showOriginalColumn);
344       }
345 
346       return false;
347     }
348 
349     //-----------------------------------------------------------------------------
350 
351     static inline bool applyPlasticDeform(
352         TXshColumn *col, const ImagePainter::VisualSettings *vs) {
353       const PlasticVisualSettings &pvs = vs->m_plasticVisualSettings;
354       return pvs.m_applyPlasticDeformation && (col != pvs.m_showOriginalColumn);
355     }
356   };  // locals
357 
358   TXshColumnP column = xsh->getColumn(col);
359   assert(column);
360 
361   TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(col));
362 
363   // Build affine placements
364 
365   TAffine columnAff     = pegbar->getPlacement(row);
366   double columnZ        = pegbar->getZ(row);
367   double columnNoScaleZ = pegbar->getGlobalNoScaleZ();
368 
369   TXshCell cell = xsh->getCell(row, col);
370   TXshLevel *xl = cell.m_level.getPointer();
371 
372 #if defined(x64)
373   // check the previous row for a stop motion layer
374   if (!xl) {
375     if (!m_liveViewImage) return;
376     cell = xsh->getCell(row - 1, col);
377     xl   = cell.m_level.getPointer();
378     if (!xl) {
379       return;
380     } else {
381       xl                  = cell.m_level.getPointer();
382       TXshSimpleLevel *sl = xl->getSimpleLevel();
383       if (sl && sl == m_liveViewPlayer.m_sl) {
384         row -= 1;
385       } else
386         return;
387     }
388   }
389 #else
390   if (!xl) return;
391 #endif
392 
393   ZPlacement cameraPlacement;
394   if (m_subXSheetStack.empty())
395     cameraPlacement = m_cameraPlacement;
396   else
397     cameraPlacement = m_subXSheetStack.back().m_camera;
398   TAffine columnZaff;
399   bool columnBehindCamera = TStageObject::perspective(
400       columnZaff, cameraPlacement.m_aff, cameraPlacement.m_z, columnAff,
401       columnZ, columnNoScaleZ);
402 
403   if (!columnBehindCamera) return;
404 
405   TXshSimpleLevel *sl = xl->getSimpleLevel();
406 
407   bool storePlayer =
408       sl || (xl->getChildLevel() &&
409              locals::applyPlasticDeform(column.getPointer(), m_vs) &&
410              locals::isMeshDeformed(xsh, pegbar, m_vs));
411 
412   if (storePlayer) {
413     // Build and store a player
414 
415     Player player;
416     player.m_sl                     = sl;
417     player.m_fid                    = cell.m_frameId;
418     player.m_xsh                    = xsh;
419     player.m_column                 = col;
420     player.m_frame                  = row;
421     player.m_currentFrameId         = m_currentFrameId;
422     player.m_isGuidedDrawingEnabled = m_isGuidedDrawingEnabled;
423     player.m_guidedFrontStroke      = m_guidedFrontStroke;
424     player.m_guidedBackStroke       = m_guidedBackStroke;
425     player.m_dpiAff = sl ? getDpiAffine(sl, cell.m_frameId) : TAffine();
426     player.m_onionSkinDistance = m_onionSkinDistance;
427     // when visiting the subxsheet, valuate the subxsheet column index
428     bool isCurrent = (subSheetColIndex >= 0)
429                          ? (subSheetColIndex == m_currentColumnIndex)
430                          : (col == m_currentColumnIndex);
431     player.m_isCurrentColumn     = isCurrent;
432     player.m_ancestorColumnIndex = m_ancestorColumnIndex;
433     player.m_masks               = m_masks;
434     player.m_opacity             = column->getOpacity();
435     player.m_filterColor         = column->getFilterColor();
436 
437     if (m_subXSheetStack.empty()) {
438       player.m_z         = columnZ;
439       player.m_placement = m_camera3d ? columnAff : columnZaff;
440     } else {
441       const SubXSheet &parent = m_subXSheetStack.back();
442 
443       player.m_z = parent.m_z;
444       player.m_placement =
445           (m_camera3d ? parent.m_aff : parent.m_zaff) * columnZaff;
446     }
447 
448     if (m_shiftTraceGhostId != NO_GHOST) {
449       // if F1, F2 or F3 key is pressed, then draw only the corresponding ghost
450       int flipKey = m_onionSkinMask.getGhostFlipKey();
451       if (Qt::Key_F1 <= flipKey && flipKey <= Qt::Key_F3) {
452         if (m_shiftTraceGhostId == TRACED && flipKey == Qt::Key_F2) {
453           players.push_back(player);
454         } else if (m_shiftTraceGhostId == FIRST_GHOST &&
455                    flipKey == Qt::Key_F1) {
456           player.m_placement =
457               m_onionSkinMask.getShiftTraceGhostAff(0) * player.m_placement;
458           players.push_back(player);
459         } else if (m_shiftTraceGhostId == SECOND_GHOST &&
460                    flipKey == Qt::Key_F3) {
461           player.m_placement =
462               m_onionSkinMask.getShiftTraceGhostAff(1) * player.m_placement;
463           players.push_back(player);
464         }
465         return;
466       }
467 
468       else {
469         int opacity         = player.m_opacity;
470         player.m_bingoOrder = 10;
471         if (m_onionSkinMask.getShiftTraceStatus() !=
472             OnionSkinMask::ENABLED_WITHOUT_GHOST_MOVEMENTS) {
473           if (m_shiftTraceGhostId == FIRST_GHOST) {
474             player.m_opacity = 30;
475             players.push_back(player);
476             player.m_opacity           = opacity;
477             player.m_onionSkinDistance = -1;
478             player.m_placement =
479                 m_onionSkinMask.getShiftTraceGhostAff(0) * player.m_placement;
480           } else if (m_shiftTraceGhostId == SECOND_GHOST) {
481             player.m_opacity = 30;
482             players.push_back(player);
483             player.m_opacity           = opacity;
484             player.m_onionSkinDistance = 1;
485             player.m_placement =
486                 m_onionSkinMask.getShiftTraceGhostAff(1) * player.m_placement;
487           }
488         }
489       }
490     }
491 
492     players.push_back(player);
493   } else if (TXshChildLevel *cl = xl->getChildLevel()) {
494     int childRow         = cell.m_frameId.getNumber() - 1;
495     TXsheet *childXsheet = cl->getXsheet();
496     TStageObjectId childCameraId =
497         childXsheet->getStageObjectTree()->getCurrentCameraId();
498     TStageObject *childCamera = childXsheet->getStageObject(childCameraId);
499     TAffine childCameraAff    = childCamera->getPlacement(childRow);
500     double childCameraZ       = childCamera->getZ(childRow);
501 
502     std::vector<UCHAR> originalOpacity(childXsheet->getColumnCount());
503 
504     for (int c = 0; c < childXsheet->getColumnCount(); c++) {
505       originalOpacity[c] = childXsheet->getColumn(c)->getOpacity();
506       childXsheet->getColumn(c)->setOpacity(column->getOpacity());
507     }
508 
509     SubXSheet subXSheet;
510     subXSheet.m_camera = ZPlacement(childCameraAff, childCameraZ);
511     subXSheet.m_z      = columnZ;
512 
513     TAffine childCameraZaff =
514         childCameraAff *
515         TScale((1000 + childCameraZ) / 1000);  // TODO: put in some lib
516     TAffine invChildCameraZaff = childCameraZaff.inv();
517 
518     subXSheet.m_aff  = columnAff * invChildCameraZaff;
519     subXSheet.m_zaff = columnZaff * invChildCameraZaff;
520 
521     if (!m_subXSheetStack.empty()) {
522       const SubXSheet &parentXSheet = m_subXSheetStack.back();
523 
524       subXSheet.m_z =
525           m_subXSheetStack.front().m_z;  // Top-level xsheet cell's z
526       subXSheet.m_aff  = parentXSheet.m_aff * subXSheet.m_aff;
527       subXSheet.m_zaff = parentXSheet.m_zaff * subXSheet.m_zaff;
528     }
529 
530     m_subXSheetStack.push_back(subXSheet);
531 
532     ++m_currentXsheetLevel;
533     // inherit the column index when visiting grandchild (sub-sub) sheet
534     int subCol = (subSheetColIndex >= 0) ? subSheetColIndex : col;
535     addFrame(players, scene, childXsheet, childRow, level + 1, false, false,
536              subCol);
537     --m_currentXsheetLevel;
538 
539     m_subXSheetStack.pop_back();
540 
541     for (int c = 0; c < childXsheet->getColumnCount(); c++)
542       childXsheet->getColumn(c)->setOpacity(originalOpacity[c]);
543   }
544 }
545 
546 //-----------------------------------------------------------------------------
547 
alreadyAdded(TXsheet * xsh,int row,int index,const std::vector<int> & rows,int col)548 static bool alreadyAdded(TXsheet *xsh, int row, int index,
549                          const std::vector<int> &rows, int col) {
550   int i;
551   for (i = 0; i < index; i++)
552     if (xsh->getCell(rows[i], col) == xsh->getCell(rows[index], col))
553       return true;
554   if (xsh->getCell(rows[index], col) == xsh->getCell(row, col)) return true;
555   return false;
556 }
557 
558 //-----------------------------------------------------------------------------
559 
addCellWithOnionSkin(PlayerSet & players,ToonzScene * scene,TXsheet * xsh,int row,int col,int level,int subSheetColIndex)560 void StageBuilder::addCellWithOnionSkin(PlayerSet &players, ToonzScene *scene,
561                                         TXsheet *xsh, int row, int col,
562                                         int level, int subSheetColIndex) {
563   struct locals {
564     static inline bool hasOnionSkinnedMeshParent(StageBuilder *sb, TXsheet *xsh,
565                                                  int col) {
566       const TStageObjectId &parentId =
567           xsh->getStageObject(TStageObjectId::ColumnId(col))->getParent();
568 
569       return parentId.isColumn() &&
570              (parentId.getIndex() == sb->m_currentColumnIndex);
571     }
572 
573     //-----------------------------------------------------------------------------
574 
575     static inline bool doStandardOnionSkin(StageBuilder *sb, TXsheet *xsh,
576                                            int level, int col) {
577       const OnionSkinMask &osm = sb->m_onionSkinMask;
578 
579       return level == sb->m_xsheetLevel && osm.isEnabled() && !osm.isEmpty() &&
580              (osm.isWholeScene() || col == sb->m_currentColumnIndex ||
581               locals::hasOnionSkinnedMeshParent(sb, xsh, col));
582     }
583   };  // locals
584 
585   if (m_onionSkinMask.isShiftTraceEnabled()) {
586     // when visiting the subxsheet, valuate the subxsheet column index
587     bool isCurrent = (subSheetColIndex >= 0)
588                          ? (subSheetColIndex == m_currentColumnIndex)
589                          : (col == m_currentColumnIndex);
590     if (isCurrent) {
591       TXshCell cell = xsh->getCell(row, col);
592 
593       // First Ghost
594       int r;
595       r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(0);
596       if (r >= 0 && xsh->getCell(r, col) != cell &&
597           (cell.getSimpleLevel() == 0 ||
598            xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
599         m_shiftTraceGhostId = FIRST_GHOST;
600         addCell(players, scene, xsh, r, col, level, subSheetColIndex);
601       }
602 
603       r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(1);
604       if (r >= 0 && xsh->getCell(r, col) != cell &&
605           (cell.getSimpleLevel() == 0 ||
606            xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
607         m_shiftTraceGhostId = SECOND_GHOST;
608         addCell(players, scene, xsh, r, col, level, subSheetColIndex);
609       }
610 
611       // draw current working frame
612       if (!cell.isEmpty()) {
613         m_shiftTraceGhostId = TRACED;
614         addCell(players, scene, xsh, row, col, level, subSheetColIndex);
615         m_shiftTraceGhostId = NO_GHOST;
616       }
617     }
618     // flip non-current columns as well
619     else {
620       int flipKey = m_onionSkinMask.getGhostFlipKey();
621       if (flipKey == Qt::Key_F1) {
622         int r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(0);
623         addCell(players, scene, xsh, r, col, level, subSheetColIndex);
624       } else if (flipKey == Qt::Key_F3) {
625         int r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(1);
626         addCell(players, scene, xsh, r, col, level, subSheetColIndex);
627       } else
628         addCell(players, scene, xsh, row, col, level, subSheetColIndex);
629     }
630   } else if (locals::doStandardOnionSkin(this, xsh, level, col)) {
631     std::vector<int> rows;
632     m_onionSkinMask.getAll(row, rows);
633     std::vector<int>::iterator it = rows.begin();
634     while (it != rows.end() && *it < row) it++;
635     std::sort(rows.begin(), it, descending);
636 
637     int frontPos = 0, backPos = 0;
638     for (int i = 0; i < (int)rows.size(); i++) {
639       if (rows[i] == row) continue;
640 
641 #ifdef NUOVO_ONION
642       m_onionSkinDistance = rows[i] - row;
643 #else
644       if (!Preferences::instance()->isAnimationSheetEnabled() ||
645           !alreadyAdded(xsh, row, i, rows, col)) {
646         m_onionSkinDistance = (rows[i] - row) < 0 ? --backPos : ++frontPos;
647         addCell(players, scene, xsh, rows[i], col, level, subSheetColIndex);
648       }
649 #endif
650     }
651 
652     m_onionSkinDistance = 0;
653     addCell(players, scene, xsh, row, col, level, subSheetColIndex);
654 
655     m_onionSkinDistance = c_noOnionSkin;
656   } else
657     addCell(players, scene, xsh, row, col, level, subSheetColIndex);
658 }
659 
660 //-----------------------------------------------------------------------------
661 
addFrame(PlayerSet & players,ToonzScene * scene,TXsheet * xsh,int row,int level,bool includeUnvisible,bool checkPreviewVisibility,int subSheetColIndex)662 void StageBuilder::addFrame(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
663                             int row, int level, bool includeUnvisible,
664                             bool checkPreviewVisibility, int subSheetColIndex) {
665   int columnCount        = xsh->getColumnCount();
666   unsigned int maskCount = m_masks.size();
667 
668   std::vector<std::pair<double, int>> shuffle;
669   for (int c = 0; c < columnCount; c++) {
670     TXshColumnP column = xsh->getColumn(c);
671     assert(column);
672     TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(c));
673     double columnSO      = pegbar->getSO(row);
674     shuffle.push_back(std::make_pair(columnSO, c));
675   }
676   std::stable_sort(shuffle.begin(), shuffle.end(), StackingOrder());
677 
678   for (int i = 0; i < columnCount; i++) {
679     int c = shuffle[i].second;
680     if (CameraTestCheck::instance()->isEnabled() && c != m_currentColumnIndex)
681       continue;
682     if (level == 0) {
683       // m_isCurrentColumn = (c == m_currentColumnIndex);
684       m_ancestorColumnIndex = c;
685     }
686     TXshColumn *column = xsh->getColumn(c);
687     bool isMask        = false;
688     if (column && !column->isEmpty() && !column->getSoundColumn()) {
689       if (!column->isPreviewVisible() && checkPreviewVisibility) continue;
690       if (column->isCamstandVisible() ||
691           includeUnvisible)  // se l'"occhietto" non e' chiuso
692       {
693         if (column->isMask())  // se e' una maschera (usate solo in tab pro)
694         {
695           isMask = true;
696           std::vector<int> saveMasks;
697           saveMasks.swap(m_masks);
698           int maskIndex   = m_maskPool.size();
699           PlayerSet *mask = new PlayerSet();
700           m_maskPool.push_back(mask);
701           addCellWithOnionSkin(*mask, scene, xsh, row, c, level);
702           std::stable_sort(mask->begin(), mask->end(), PlayerLt());
703           saveMasks.swap(m_masks);
704           m_masks.push_back(maskIndex);
705         } else
706           addCellWithOnionSkin(players, scene, xsh, row, c, level,
707                                subSheetColIndex);
708       }
709     }
710     if (!isMask) {
711       while (m_masks.size() > maskCount) m_masks.pop_back();
712     }
713   }
714   if (level == 0) std::stable_sort(players.begin(), players.end(), PlayerLt());
715 }
716 
717 //-----------------------------------------------------------------------------
718 
addSimpleLevelFrame(PlayerSet & players,TXshSimpleLevel * level,const TFrameId & fid)719 void StageBuilder::addSimpleLevelFrame(PlayerSet &players,
720                                        TXshSimpleLevel *level,
721                                        const TFrameId &fid) {
722   auto addGhost = [&](int ghostIndex, int ghostRow, bool fullOpac = false) {
723     const TFrameId &ghostFid = level->index2fid(ghostRow);
724 
725     Player player;
726     player.m_sl                     = level;
727     player.m_frame                  = level->guessIndex(ghostFid);
728     player.m_fid                    = ghostFid;
729     player.m_isCurrentColumn        = true;
730     player.m_isCurrentXsheetLevel   = true;
731     player.m_isEditingLevel         = true;
732     player.m_currentFrameId         = m_currentFrameId;
733     player.m_isGuidedDrawingEnabled = m_isGuidedDrawingEnabled;
734     player.m_guidedFrontStroke      = m_guidedFrontStroke;
735     player.m_guidedBackStroke       = m_guidedBackStroke;
736     player.m_isVisibleinOSM         = ghostRow >= 0;
737     player.m_onionSkinDistance      = m_onionSkinDistance;
738     player.m_dpiAff                 = getDpiAffine(level, ghostFid);
739     player.m_ancestorColumnIndex    = -1;
740 
741     if (fullOpac) {
742       player.m_placement = m_onionSkinMask.getShiftTraceGhostAff(ghostIndex) *
743                            player.m_placement;
744       players.push_back(player);
745       return;
746     }
747 
748     int opacity         = player.m_opacity;
749     player.m_bingoOrder = 10;
750     if (m_onionSkinMask.getShiftTraceStatus() !=
751         OnionSkinMask::ENABLED_WITHOUT_GHOST_MOVEMENTS) {
752       player.m_opacity = 30;
753       players.push_back(player);
754       player.m_opacity           = opacity;
755       player.m_onionSkinDistance = (ghostIndex == 0) ? -1 : 1;
756       player.m_placement = m_onionSkinMask.getShiftTraceGhostAff(ghostIndex) *
757                            player.m_placement;
758     }
759     players.push_back(player);
760   };
761 
762   int index = -1;
763 
764   int row = level->guessIndex(fid);
765 
766   // Shift & Trace
767   if (m_onionSkinMask.isShiftTraceEnabled()) {
768     int previousOffset = m_onionSkinMask.getShiftTraceGhostFrameOffset(0);
769     int forwardOffset  = m_onionSkinMask.getShiftTraceGhostFrameOffset(1);
770 
771     // If F1, F2 or F3 key is pressed, then only
772     // display the corresponding ghost
773     int flipKey = m_onionSkinMask.getGhostFlipKey();
774     if (Qt::Key_F1 <= flipKey && flipKey <= Qt::Key_F3) {
775       if (flipKey == Qt::Key_F1 && previousOffset != 0) {
776         addGhost(0, row + previousOffset, true);
777         return;
778       } else if (flipKey == Qt::Key_F3 && forwardOffset != 0) {
779         addGhost(1, row + forwardOffset, true);
780         return;
781       }
782     }
783 
784     else {
785       // draw the first ghost
786       if (previousOffset != 0) addGhost(0, row + previousOffset);
787       if (forwardOffset != 0) addGhost(1, row + forwardOffset);
788     }
789   }
790   // Onion Skin
791   else if (!m_onionSkinMask.isEmpty() && m_onionSkinMask.isEnabled()) {
792     std::vector<int> rows;
793     m_onionSkinMask.getAll(row, rows);
794 
795     std::vector<int>::iterator it = rows.begin();
796     while (it != rows.end() && *it < row) ++it;
797     std::sort(rows.begin(), it, descending);
798     m_onionSkinDistance = 0;
799     int frontPos = 0, backPos = 0;
800 
801     for (int i = 0; i < (int)rows.size(); i++) {
802       const TFrameId &fid2 = level->index2fid(rows[i]);
803       if (fid2 == fid) continue;
804       players.push_back(Player());
805       Player &player                  = players.back();
806       player.m_sl                     = level;
807       player.m_frame                  = level->guessIndex(fid);
808       player.m_fid                    = fid2;
809       player.m_isCurrentColumn        = true;
810       player.m_isCurrentXsheetLevel   = true;
811       player.m_isEditingLevel         = true;
812       player.m_currentFrameId         = m_currentFrameId;
813       player.m_isGuidedDrawingEnabled = m_isGuidedDrawingEnabled;
814       player.m_guidedFrontStroke      = m_guidedFrontStroke;
815       player.m_guidedBackStroke       = m_guidedBackStroke;
816       player.m_isVisibleinOSM         = rows[i] >= 0;
817 #ifdef NUOVO_ONION
818       player.m_onionSkinDistance = rows[i] - row;
819 #else
820       player.m_onionSkinDistance = rows[i] - row < 0 ? --backPos : ++frontPos;
821 #endif
822       player.m_dpiAff = getDpiAffine(level, fid2);
823     }
824   }
825   players.push_back(Player());
826   Player &player = players.back();
827   player.m_sl    = level;
828   player.m_frame = level->guessIndex(fid);
829   player.m_fid   = fid;
830   if (!m_onionSkinMask.isEmpty() && m_onionSkinMask.isEnabled())
831     player.m_onionSkinDistance = 0;
832   player.m_isCurrentColumn      = true;
833   player.m_isCurrentXsheetLevel = true;
834   player.m_isEditingLevel       = true;
835   player.m_ancestorColumnIndex  = -1;
836   player.m_dpiAff               = getDpiAffine(level, fid);
837 }
838 
839 //-----------------------------------------------------------------------------
840 
visit(PlayerSet & players,Visitor & visitor,bool isPlaying)841 void StageBuilder::visit(PlayerSet &players, Visitor &visitor, bool isPlaying) {
842   std::vector<int> masks;
843   int m                = players.size();
844   int h                = 0;
845   bool stopMotionShown = false;
846   for (; h < m; h++) {
847     Player &player = players[h];
848     unsigned int i = 0;
849     // vale solo per TAB pro
850     for (; i < masks.size() && i < player.m_masks.size(); i++)
851       if (masks[i] != player.m_masks[i]) break;
852     // vale solo per TAB pro
853     if (i < masks.size() || i < player.m_masks.size()) {
854       while (i < masks.size()) {
855         masks.pop_back();
856         visitor.disableMask();
857       }
858       while (i < player.m_masks.size()) {
859         int maskIndex = player.m_masks[i];
860         visitor.beginMask();
861         visit(*m_maskPool[maskIndex], visitor, isPlaying);
862         visitor.endMask();
863         masks.push_back(maskIndex);
864         visitor.enableMask();
865         i++;
866       }
867     }
868     player.m_isPlaying = isPlaying;
869 
870 #if defined(x64)
871     if (m_liveViewImage && player.m_sl == m_liveViewPlayer.m_sl) {
872       if (m_lineupImage) {
873         m_lineupPlayer.m_sl = NULL;
874         visitor.onRasterImage(m_lineupImage.getPointer(), m_lineupPlayer);
875         stopMotionShown = true;
876       }
877       if (m_liveViewImage) {
878         m_liveViewPlayer.m_sl = NULL;
879         visitor.onRasterImage(m_liveViewImage.getPointer(), m_liveViewPlayer);
880         stopMotionShown = true;
881       }
882     } else {
883       visitor.onImage(player);
884     }
885   }
886   if (!stopMotionShown && (m_liveViewImage || m_lineupImage)) {
887     if (m_lineupImage) {
888       m_lineupPlayer.m_sl = NULL;
889       visitor.onRasterImage(m_lineupImage.getPointer(), m_lineupPlayer);
890     }
891     if (m_liveViewImage) {
892       m_liveViewPlayer.m_sl = NULL;
893       visitor.onRasterImage(m_liveViewImage.getPointer(), m_liveViewPlayer);
894     }
895 #else
896     visitor.onImage(player);
897 #endif
898   }
899   // vale solo per TAB pro
900   for (h = 0; h < (int)masks.size(); h++) visitor.disableMask();
901 }
902 
903 //=============================================================================
904 
905 //=============================================================================
906 /*! Declare a \b StageBuilder object and recall \b StageBuilder::addFrame() and
907                 \b StageBuilder::visit().
908 */
909 //=============================================================================
910 
visit(Visitor & visitor,const VisitArgs & args)911 void Stage::visit(Visitor &visitor, const VisitArgs &args) {
912   ToonzScene *scene        = args.m_scene;
913   TXsheet *xsh             = args.m_xsh;
914   int row                  = args.m_row;
915   int col                  = args.m_col;
916   const OnionSkinMask *osm = args.m_osm;
917   bool camera3d            = args.m_camera3d;
918   int xsheetLevel          = args.m_xsheetLevel;
919   bool isPlaying           = args.m_isPlaying;
920 
921   StageBuilder sb;
922   sb.m_vs                     = &visitor.m_vs;
923   TStageObjectId cameraId     = xsh->getStageObjectTree()->getCurrentCameraId();
924   TStageObject *camera        = xsh->getStageObject(cameraId);
925   TAffine cameraAff           = camera->getPlacement(row);
926   double z                    = camera->getZ(row);
927   sb.m_cameraPlacement        = ZPlacement(cameraAff, z);
928   sb.m_camera3d               = camera3d;
929   sb.m_currentColumnIndex     = col;
930   sb.m_xsheetLevel            = xsheetLevel;
931   sb.m_onionSkinMask          = *osm;
932   sb.m_currentFrameId         = args.m_currentFrameId;
933   sb.m_isGuidedDrawingEnabled = args.m_isGuidedDrawingEnabled;
934   sb.m_guidedFrontStroke      = args.m_guidedFrontStroke;
935   sb.m_guidedBackStroke       = args.m_guidedBackStroke;
936 #if defined(x64)
937   if (args.m_liveViewImage) {
938     sb.m_liveViewImage  = args.m_liveViewImage;
939     sb.m_liveViewPlayer = args.m_liveViewPlayer;
940   }
941   if (args.m_lineupImage) {
942     sb.m_lineupImage  = args.m_lineupImage;
943     sb.m_lineupPlayer = args.m_lineupPlayer;
944   }
945 #endif
946   Player::m_onionSkinFrontSize     = 0;
947   Player::m_onionSkinBackSize      = 0;
948   Player::m_firstFrontOnionSkin    = 0;
949   Player::m_firstBackOnionSkin     = 0;
950   Player::m_lastBackVisibleSkin    = 0;
951   Player::m_isShiftAndTraceEnabled = osm->isShiftTraceEnabled();
952   sb.addFrame(sb.m_players, scene, xsh, row, 0, args.m_onlyVisible,
953               args.m_checkPreviewVisibility);
954 
955   updateOnionSkinSize(sb.m_players);
956 
957   sb.visit(sb.m_players, visitor, isPlaying);
958 }
959 
960 //-----------------------------------------------------------------------------
961 
visit(Visitor & visitor,ToonzScene * scene,TXsheet * xsh,int row)962 void Stage::visit(Visitor &visitor, ToonzScene *scene, TXsheet *xsh, int row) {
963   Stage::VisitArgs args;
964   args.m_scene = scene;
965   args.m_xsh   = xsh;
966   args.m_row   = row;
967   args.m_col   = -1;
968   OnionSkinMask osm;
969   args.m_osm = &osm;
970 
971   visit(visitor, args);  // scene, xsh, row, -1, OnionSkinMask(), false, 0);
972 }
973 
974 //-----------------------------------------------------------------------------
975 /*! Declare a \b StageBuilder object and recall \b
976    StageBuilder::addSimpleLevelFrame()
977                 and \b StageBuilder::visit().
978 */
visit(Visitor & visitor,TXshSimpleLevel * level,const TFrameId & fid,const OnionSkinMask & osm,bool isPlaying,int isGuidedDrawingEnabled,int guidedBackStroke,int guidedFrontStroke)979 void Stage::visit(Visitor &visitor, TXshSimpleLevel *level, const TFrameId &fid,
980                   const OnionSkinMask &osm, bool isPlaying,
981                   int isGuidedDrawingEnabled, int guidedBackStroke,
982                   int guidedFrontStroke) {
983   StageBuilder sb;
984   sb.m_vs                          = &visitor.m_vs;
985   sb.m_onionSkinMask               = osm;
986   sb.m_currentFrameId              = fid;
987   sb.m_isGuidedDrawingEnabled      = isGuidedDrawingEnabled;
988   sb.m_guidedFrontStroke           = guidedFrontStroke;
989   sb.m_guidedBackStroke            = guidedBackStroke;
990   Player::m_onionSkinFrontSize     = 0;
991   Player::m_onionSkinBackSize      = 0;
992   Player::m_firstFrontOnionSkin    = 0;
993   Player::m_firstBackOnionSkin     = 0;
994   Player::m_lastBackVisibleSkin    = 0;
995   Player::m_isShiftAndTraceEnabled = osm.isShiftTraceEnabled();
996   sb.addSimpleLevelFrame(sb.m_players, level, fid);
997   updateOnionSkinSize(sb.m_players);
998   sb.visit(sb.m_players, visitor, isPlaying);
999 }
1000 
1001 //-----------------------------------------------------------------------------
1002 
visit(Visitor & visitor,TXshLevel * level,const TFrameId & fid,const OnionSkinMask & osm,bool isPlaying,double isGuidedDrawingEnabled,int guidedBackStroke,int guidedFrontStroke)1003 void Stage::visit(Visitor &visitor, TXshLevel *level, const TFrameId &fid,
1004                   const OnionSkinMask &osm, bool isPlaying,
1005                   double isGuidedDrawingEnabled, int guidedBackStroke,
1006                   int guidedFrontStroke) {
1007   if (level && level->getSimpleLevel())
1008     visit(visitor, level->getSimpleLevel(), fid, osm, isPlaying,
1009           (int)isGuidedDrawingEnabled, guidedBackStroke, guidedFrontStroke);
1010 }
1011