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