1 
2 #if defined(LINUX) || defined(FREEBSD)
3 #define GL_GLEXT_PROTOTYPES
4 #endif
5 
6 // Toonz includes
7 #include "tapp.h"
8 #include "viewerpane.h"
9 #include "onionskinmaskgui.h"
10 #include "viewerdraw.h"
11 #include "menubarcommandids.h"
12 #include "ruler.h"
13 #include "locatorpopup.h"
14 #if defined(x64)
15 #include "../stopmotion/stopmotion.h"
16 #endif
17 
18 // TnzTools includes
19 #include "tools/cursors.h"
20 #include "tools/cursormanager.h"
21 #include "tools/toolhandle.h"
22 #include "tools/toolcommandids.h"
23 #include "tools/toolutils.h"
24 
25 // TnzQt includes
26 #include "toonzqt/icongenerator.h"
27 #include "toonzqt/gutil.h"
28 #include "toonzqt/imageutils.h"
29 #include "toonzqt/lutcalibrator.h"
30 #include "toonzqt/viewcommandids.h"
31 
32 // TnzLib includes
33 #include "toonz/tscenehandle.h"
34 #include "toonz/txsheethandle.h"
35 #include "toonz/tframehandle.h"
36 #include "toonz/tcolumnhandle.h"
37 #include "toonz/txshlevelhandle.h"
38 #include "toonz/sceneproperties.h"
39 #include "toonz/toonzscene.h"
40 #include "toonz/levelset.h"
41 #include "toonz/txshsimplelevel.h"
42 #include "toonz/tcamera.h"
43 #include "toonz/stage2.h"
44 #include "toonz/stage.h"
45 #include "toonz/stageplayer.h"
46 #include "toonz/stagevisitor.h"
47 #include "toonz/txsheet.h"
48 #include "toonz/tstageobjecttree.h"
49 #include "toonz/tstageobjectspline.h"
50 #include "toonz/tobjecthandle.h"
51 #include "toonz/tonionskinmaskhandle.h"
52 #include "toonz/palettecontroller.h"
53 #include "toonz/tpalettehandle.h"
54 #include "toonz/childstack.h"
55 #include "toonz/dpiscale.h"
56 #include "toonz/txshlevel.h"
57 #include "toonz/txshlevelcolumn.h"
58 #include "toonz/preferences.h"
59 #include "toonz/glrasterpainter.h"
60 #include "toonz/cleanupparameters.h"
61 #include "toonz/toonzimageutils.h"
62 #include "toonz/txshleveltypes.h"
63 #include "subcameramanager.h"
64 
65 // TnzCore includes
66 #include "tpalette.h"
67 #include "tropcm.h"
68 #include "tgl.h"
69 #include "tofflinegl.h"
70 #include "tstopwatch.h"
71 #include "trop.h"
72 #include "tproperty.h"
73 #include "timagecache.h"
74 #include "trasterimage.h"
75 #include "tstroke.h"
76 #include "ttoonzimage.h"
77 
78 // Qt includes
79 #include <QMenu>
80 #include <QApplication>
81 #include <QDesktopWidget>
82 #if QT_VERSION >= 0x050000
83 #include <QInputMethod>
84 #else
85 #include <QInputContext>
86 #endif
87 #include <QGLContext>
88 #include <QOpenGLFramebufferObject>
89 #include <QMainWindow>
90 
91 #include "sceneviewer.h"
92 
93 void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d,
94                 double pixelSize);
95 
96 //-------------------------------------------------------------------------------
97 namespace {
98 
99 int l_mainDisplayListsSpaceId =
100     -1;  //!< Display lists space id associated with SceneViewers
101 std::set<TGlContext>
102     l_contexts;  //!< Stores every SceneViewer context (see ~SceneViewer)
103 
104 //-------------------------------------------------------------------------------
105 
106 struct DummyProxy : public TGLDisplayListsProxy {
~DummyProxy__anon6ddebecf0111::DummyProxy107   ~DummyProxy() {}
makeCurrent__anon6ddebecf0111::DummyProxy108   void makeCurrent() {}
doneCurrent__anon6ddebecf0111::DummyProxy109   void doneCurrent() {}
110 };
111 
112 //-------------------------------------------------------------------------------
113 
getActualFrameRate()114 double getActualFrameRate() {
115   // compute frame per second
116   static double fps = 0;
117   static TStopWatch stopwatch;
118   static int frame = 0;
119   ++frame;
120 
121   stopwatch.start();
122 
123   unsigned long tt = stopwatch.getTotalTime();
124 
125   // wait a time greater than one second
126   if (tt > 1000) {
127     stopwatch.stop();
128     fps = troundp(((1000 * frame) / (double)tt));
129     stopwatch.start(true);
130     frame = 0;
131   }
132   return fps;
133 }
134 
135 //-----------------------------------------------------------------------------
136 
copyFrontBufferToBackBuffer()137 void copyFrontBufferToBackBuffer() {
138   static GLint viewport[4];
139   static GLfloat raster_pos[4];
140 
141   glGetIntegerv(GL_VIEWPORT, viewport);
142 
143   /* set source buffer */
144   glReadBuffer(GL_FRONT);
145 
146   /* set projection matrix */
147   glMatrixMode(GL_PROJECTION);
148   glPushMatrix();
149   glLoadIdentity();
150   gluOrtho2D(0, viewport[2], 0, viewport[3]);
151 
152   /* set modelview matrix */
153   glMatrixMode(GL_MODELVIEW);
154   glPushMatrix();
155   glLoadIdentity();
156 
157   /* save old raster position */
158   glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos);
159 
160   /* set raster position */
161   glRasterPos4f(0.0, 0.0, 0.0, 1.0);
162 
163   /* copy buffer */
164   glCopyPixels(0, 0, viewport[2], viewport[3], GL_COLOR);
165 
166   /* restore old raster position */
167   glRasterPos4fv(raster_pos);
168 
169   /* restore old matrices */
170   glPopMatrix();
171   glMatrixMode(GL_PROJECTION);
172   glPopMatrix();
173   glMatrixMode(GL_MODELVIEW);
174 
175   /* restore source buffer */
176   glReadBuffer(GL_BACK);
177 }
178 //-----------------------------------------------------------------------------
179 /*! Compute new 3Dposition and new 2D position. */
computeNew3DPosition(T3DPointD start3DPos,TPointD delta2D,TPointD & new2dPos,GLdouble modelView3D[16],GLdouble projection3D[16],GLint viewport3D[4],int devPixRatio)180 T3DPointD computeNew3DPosition(T3DPointD start3DPos, TPointD delta2D,
181                                TPointD &new2dPos, GLdouble modelView3D[16],
182                                GLdouble projection3D[16], GLint viewport3D[4],
183                                int devPixRatio) {
184   GLdouble pos2D_x, pos2D_y, pos2D_z;
185   gluProject(-start3DPos.x, -start3DPos.y, start3DPos.z, modelView3D,
186              projection3D, viewport3D, &pos2D_x, &pos2D_y, &pos2D_z);
187   new2dPos = TPointD(pos2D_x + delta2D.x, pos2D_y + delta2D.y);
188   GLdouble pos3D_x, pos3D_y, pos3D_z;
189   gluUnProject(new2dPos.x, new2dPos.y, 1, modelView3D, projection3D, viewport3D,
190                &pos3D_x, &pos3D_y, &pos3D_z);
191   new2dPos.y = viewport3D[3] - new2dPos.y - 20 * devPixRatio;
192   return T3DPointD(pos3D_x, pos3D_y, pos3D_z);
193 }
194 
195 //-----------------------------------------------------------------------------
196 #ifdef DA_RIVEDERE
197 
198 // Il metodo copia una porzione del BACK_BUFFER definita da rect nel
199 // FRONTE_BUFFER
200 // Attualmente si notano delle brutture intorno al rettangolo.
201 // Per il momento si lavora nel BACK_BUFFER e sicopia tutto il FRONT_BUFFER.
202 // Riattivando questo metodo bisogna ricordarsi di disattivare lo swapbuffer in
203 // GLInvalidateRect prima di
204 // fare updateGL() e riattivarlo immediatamernte dopo!
copyBackBufferToFrontBuffer(const TRect & rect)205 void copyBackBufferToFrontBuffer(const TRect &rect) {
206   static GLint viewport[4];
207   static GLfloat raster_pos[4];
208 
209   glGetIntegerv(GL_VIEWPORT, viewport);
210 
211   /* set source buffer */
212   glReadBuffer(GL_BACK);
213   glDrawBuffer(GL_FRONT);
214 
215   /* set projection matrix */
216   glMatrixMode(GL_PROJECTION);
217   glPushMatrix();
218   glLoadIdentity();
219   gluOrtho2D(0, viewport[2], 0, viewport[3]);
220 
221   /* set modelview matrix */
222   glMatrixMode(GL_MODELVIEW);
223   glPushMatrix();
224   glLoadIdentity();
225 
226   /* save old raster position */
227   glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos);
228 
229   /* set raster position */
230   glRasterPos4f(0.0, 0.0, 0.0, 1.0);
231 
232   /* copy buffer */
233   glCopyPixels(0, 0, viewport[2], viewport[3], GL_COLOR);
234 
235   /* restore old raster position */
236   glRasterPos4fv(raster_pos);
237 
238   /* restore old matrices */
239   glPopMatrix();
240   glMatrixMode(GL_PROJECTION);
241   glPopMatrix();
242   glMatrixMode(GL_MODELVIEW);
243 
244   /* restore source buffer */
245   glDrawBuffer(GL_BACK);
246 }
247 
248 #endif
249 
250 const TRectD InvalidateAllRect(0, 0, -1, -1);
251 
252 //-----------------------------------------------------------------------------
253 }  // namespace
254 //-----------------------------------------------------------------------------
255 
256 //=============================================================================
257 // ToggleCommand
258 //-----------------------------------------------------------------------------
259 
ToggleCommandHandler(CommandId id,bool startStatus)260 ToggleCommandHandler::ToggleCommandHandler(CommandId id, bool startStatus)
261     : MenuItemHandler(id), m_status(startStatus) {}
262 
execute()263 void ToggleCommandHandler::execute() {
264   m_status = !m_status;
265   // emit sceneChanged WITHOUT dirty flag
266   TApp::instance()->getCurrentScene()->notifySceneChanged(false);
267 }
268 
269 //-----------------------------------------------------------------------------
270 
271 ToggleCommandHandler viewTableToggle(MI_ViewTable, false);
272 ToggleCommandHandler editInPlaceToggle(MI_ToggleEditInPlace, false);
273 ToggleCommandHandler fieldGuideToggle(MI_FieldGuide, false);
274 ToggleCommandHandler safeAreaToggle(MI_SafeArea, false);
275 ToggleCommandHandler rasterizePliToggle(MI_RasterizePli, false);
276 
277 ToggleCommandHandler viewClcToggle("MI_ViewColorcard", false);
278 ToggleCommandHandler viewCameraToggle("MI_ViewCamera", false);
279 ToggleCommandHandler viewBBoxToggle("MI_ViewBBox", false);
280 ToggleCommandHandler viewGuideToggle("MI_ViewGuide", false);
281 ToggleCommandHandler viewRulerToggle("MI_ViewRuler", false);
282 
283 //-----------------------------------------------------------------------------
284 
invalidateIcons()285 void invalidateIcons() {
286   ToonzCheck *tc = ToonzCheck::instance();
287   int mask       = tc->getChecks();
288   IconGenerator::Settings s;
289   s.m_blackBgCheck      = mask & ToonzCheck::eBlackBg;
290   s.m_transparencyCheck = mask & ToonzCheck::eTransparency;
291   s.m_inksOnly          = mask & ToonzCheck::eInksOnly;
292   // emphasize lines with style#1 regardless of the current style
293   if (mask & ToonzCheck::eInk1) s.m_inkIndex = 1;
294   // emphasize lines with the current style
295   else if (mask & ToonzCheck::eInk)
296     s.m_inkIndex = tc->getColorIndex();
297   else
298     s.m_inkIndex = -1;
299   s.m_paintIndex = mask & ToonzCheck::ePaint ? tc->getColorIndex() : -1;
300   IconGenerator::instance()->setSettings(s);
301 
302   // Force icons to refresh for Toonz Vector levels
303   TXshLevel *sl = TApp::instance()->getCurrentLevel()->getLevel();
304   if (sl && sl->getType() == PLI_XSHLEVEL) {
305     std::vector<TFrameId> fids;
306     sl->getFids(fids);
307 
308     for (int i = 0; i < (int)fids.size(); i++)
309       IconGenerator::instance()->invalidate(sl, fids[i]);
310   }
311 
312   // Do not remove icons here as they will be re-used for updating icons in the
313   // level strip
314 
315   // emit sceneChanged WITHOUT dirty flag
316   TApp::instance()->getCurrentScene()->notifySceneChanged(false);
317   TApp::instance()->getCurrentLevel()->notifyLevelViewChange();
318 }
319 
320 //--------------------------------------------------------------
321 
executeCheck(int checkType)322 static void executeCheck(int checkType) {
323   ToonzCheck::instance()->toggleCheck(checkType);
324   invalidateIcons();
325 }
326 
327 //-----------------------------------------------------------------------------
328 
329 class TCheckToggleCommand final : public MenuItemHandler {
330 public:
TCheckToggleCommand()331   TCheckToggleCommand() : MenuItemHandler("MI_TCheck") {}
execute()332   void execute() override { executeCheck(ToonzCheck::eTransparency); }
333 } tcheckToggle;
334 
335 //-----------------------------------------------------------------------------
336 
337 class ICheckToggleCommand final : public MenuItemHandler {
338 public:
ICheckToggleCommand()339   ICheckToggleCommand() : MenuItemHandler("MI_ICheck") {}
execute()340   void execute() override { executeCheck(ToonzCheck::eInk); }
341 } icheckToggle;
342 
343 //-----------------------------------------------------------------------------
344 
345 class PCheckToggleCommand final : public MenuItemHandler {
346 public:
PCheckToggleCommand()347   PCheckToggleCommand() : MenuItemHandler("MI_PCheck") {}
execute()348   void execute() override { executeCheck(ToonzCheck::ePaint); }
349 } pcheckToggle;
350 
351 //-----------------------------------------------------------------------------
352 
353 class BCheckToggleCommand final : public MenuItemHandler {
354 public:
BCheckToggleCommand()355   BCheckToggleCommand() : MenuItemHandler("MI_BCheck") {}
execute()356   void execute() override { executeCheck(ToonzCheck::eBlackBg); }
357 } bcheckToggle;
358 
359 //-----------------------------------------------------------------------------
360 
361 class TAutocloseToggleCommand final : public MenuItemHandler {
362 public:
TAutocloseToggleCommand()363   TAutocloseToggleCommand() : MenuItemHandler("MI_ACheck") {}
execute()364   void execute() override { executeCheck(ToonzCheck::eAutoclose); }
365 } tautocloseToggle;
366 
367 //-----------------------------------------------------------------------------
368 
369 class TGapToggleCommand final : public MenuItemHandler {
370 public:
TGapToggleCommand()371   TGapToggleCommand() : MenuItemHandler("MI_GCheck") {}
execute()372   void execute() override { executeCheck(ToonzCheck::eGap); }
373 } tgapToggle;
374 
375 //-----------------------------------------------------------------------------
376 
377 class TInksOnlyToggleCommand final : public MenuItemHandler {
378 public:
TInksOnlyToggleCommand()379   TInksOnlyToggleCommand() : MenuItemHandler("MI_IOnly") {}
execute()380   void execute() override { executeCheck(ToonzCheck::eInksOnly); }
381 } tinksOnlyToggle;
382 
383 //-----------------------------------------------------------------------------
384 /*! emphasize lines with style#1 regardless of the current style
385  */
386 class Ink1CheckToggleCommand final : public MenuItemHandler {
387 public:
Ink1CheckToggleCommand()388   Ink1CheckToggleCommand() : MenuItemHandler("MI_Ink1Check") {}
execute()389   void execute() override { executeCheck(ToonzCheck::eInk1); }
390 } ink1checkToggle;
391 
392 //=============================================================================
393 
394 class TShiftTraceToggleCommand final : public MenuItemHandler {
395   CommandId m_cmdId;
396 
397 public:
TShiftTraceToggleCommand(CommandId cmdId)398   TShiftTraceToggleCommand(CommandId cmdId)
399       : MenuItemHandler(cmdId), m_cmdId(cmdId) {}
execute()400   void execute() override {
401     CommandManager *cm = CommandManager::instance();
402     QAction *action    = cm->getAction(m_cmdId);
403     bool checked       = action->isChecked();
404     if (std::string(m_cmdId) == MI_ShiftTrace) {
405       cm->enable(MI_EditShift, checked);
406       cm->enable(MI_NoShift, checked);
407       if (checked) OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
408       //     cm->getAction(MI_NoShift)->setChecked(false);
409       TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
410     } else if (std::string(m_cmdId) == MI_EditShift) {
411       if (checked) {
412         QAction *noShiftAction =
413             CommandManager::instance()->getAction(MI_NoShift);
414         if (noShiftAction) noShiftAction->setChecked(false);
415         TApp::instance()->getCurrentTool()->setPseudoTool("T_ShiftTrace");
416       } else {
417         TApp::instance()->getCurrentTool()->unsetPseudoTool();
418       }
419       CommandManager::instance()->enable(MI_NoShift, !checked);
420     } else if (std::string(m_cmdId) == MI_NoShift) {
421     }
422     updateShiftTraceStatus();
423   }
424 
isChecked(CommandId id) const425   bool isChecked(CommandId id) const {
426     QAction *action = CommandManager::instance()->getAction(id);
427     return action != 0 && action->isChecked();
428   }
updateShiftTraceStatus()429   void updateShiftTraceStatus() {
430     OnionSkinMask::ShiftTraceStatus status = OnionSkinMask::DISABLED;
431     if (isChecked(MI_ShiftTrace)) {
432       if (isChecked(MI_EditShift))
433         status = OnionSkinMask::EDITING_GHOST;
434       else if (isChecked(MI_NoShift))
435         status = OnionSkinMask::ENABLED_WITHOUT_GHOST_MOVEMENTS;
436       else
437         status = OnionSkinMask::ENABLED;
438     }
439     OnionSkinMask osm =
440         TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
441     osm.setShiftTraceStatus(status);
442     osm.clearGhostFlipKey();
443     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
444     TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
445   }
446 };
447 
448 TShiftTraceToggleCommand shiftTraceToggleCommand(MI_ShiftTrace),
449     editShiftToggleCommand(MI_EditShift), noShiftToggleCommand(MI_NoShift);
450 
451 class TResetShiftTraceCommand final : public MenuItemHandler {
452 public:
TResetShiftTraceCommand()453   TResetShiftTraceCommand() : MenuItemHandler(MI_ResetShift) {}
execute()454   void execute() override {
455     OnionSkinMask osm =
456         TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
457     osm.setShiftTraceGhostCenter(0, TPointD());
458     osm.setShiftTraceGhostCenter(1, TPointD());
459     osm.setShiftTraceGhostAff(0, TAffine());
460     osm.setShiftTraceGhostAff(1, TAffine());
461     TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
462     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
463     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
464     if (tool) tool->reset();
465   }
466 } resetShiftTraceCommand;
467 
468 //-----------------------------------------------------------------------------
469 // Following commands (VB_***) are registered for command bar buttons.
470 // They are separatd from the original visalization commands
471 // so that they will not break a logic of ShortcutZoomer.
472 
473 class TViewResetCommand final : public MenuItemHandler {
474 public:
TViewResetCommand()475   TViewResetCommand() : MenuItemHandler(VB_ViewReset) {}
execute()476   void execute() override {
477     if (TApp::instance()->getActiveViewer())
478       TApp::instance()->getActiveViewer()->resetSceneViewer();
479   }
480 } viewResetCommand;
481 
482 class TZoomResetCommand final : public MenuItemHandler {
483 public:
TZoomResetCommand()484   TZoomResetCommand() : MenuItemHandler(VB_ZoomReset) {}
execute()485   void execute() override {
486     if (TApp::instance()->getActiveViewer())
487       TApp::instance()->getActiveViewer()->resetZoom();
488   }
489 } zoomResetCommand;
490 
491 class TZoomFitCommand final : public MenuItemHandler {
492 public:
TZoomFitCommand()493   TZoomFitCommand() : MenuItemHandler(VB_ZoomFit) {}
execute()494   void execute() override {
495     if (TApp::instance()->getActiveViewer())
496       TApp::instance()->getActiveViewer()->fitToCamera();
497   }
498 } zoomFitCommand;
499 
500 class TActualPixelSizeCommand final : public MenuItemHandler {
501 public:
TActualPixelSizeCommand()502   TActualPixelSizeCommand() : MenuItemHandler(VB_ActualPixelSize) {}
execute()503   void execute() override {
504     if (TApp::instance()->getActiveViewer())
505       TApp::instance()->getActiveViewer()->setActualPixelSize();
506   }
507 } actualPixelSizeCommand;
508 
509 class TFlipViewerXCommand final : public MenuItemHandler {
510 public:
TFlipViewerXCommand()511   TFlipViewerXCommand() : MenuItemHandler(VB_FlipX) {}
execute()512   void execute() override {
513     if (TApp::instance()->getActiveViewer())
514       TApp::instance()->getActiveViewer()->flipX();
515   }
516 } flipViewerXCommand;
517 
518 class TFlipViewerYCommand final : public MenuItemHandler {
519 public:
TFlipViewerYCommand()520   TFlipViewerYCommand() : MenuItemHandler(VB_FlipY) {}
execute()521   void execute() override {
522     if (TApp::instance()->getActiveViewer())
523       TApp::instance()->getActiveViewer()->flipY();
524   }
525 } flipViewerYCommand;
526 
527 class TRotateResetCommand final : public MenuItemHandler {
528 public:
TRotateResetCommand()529   TRotateResetCommand() : MenuItemHandler(VB_RotateReset) {}
execute()530   void execute() override {
531     if (TApp::instance()->getActiveViewer())
532       TApp::instance()->getActiveViewer()->resetRotation();
533   }
534 } rotateResetCommand;
535 
536 class TPositionResetCommand final : public MenuItemHandler {
537 public:
TPositionResetCommand()538   TPositionResetCommand() : MenuItemHandler(VB_PositionReset) {}
execute()539   void execute() override {
540     if (TApp::instance()->getActiveViewer())
541       TApp::instance()->getActiveViewer()->resetPosition();
542   }
543 } positionResetCommand;
544 
545 class TVectorGuidedDrawingToggleCommand final : public MenuItemHandler {
546 public:
TVectorGuidedDrawingToggleCommand()547   TVectorGuidedDrawingToggleCommand()
548       : MenuItemHandler(MI_VectorGuidedDrawing) {}
execute()549   void execute() override {
550     CommandManager *cm = CommandManager::instance();
551     QAction *action    = cm->getAction(MI_VectorGuidedDrawing);
552     Preferences::instance()->setValue(guidedDrawingType,
553                                       action->isChecked() ? 1 : 0);
554     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
555     TApp::instance()->getCurrentScene()->notifyPreferenceChanged(
556         "GuidedDrawingFrame");
557   }
558 
isChecked(CommandId id) const559   bool isChecked(CommandId id) const {
560     QAction *action = CommandManager::instance()->getAction(id);
561     return action != 0 && action->isChecked();
562   }
563 } vectorGuidedDrawingToggleCommand;
564 
565 class TSelectGuideStrokeResetCommand final : public MenuItemHandler {
566 public:
TSelectGuideStrokeResetCommand()567   TSelectGuideStrokeResetCommand()
568       : MenuItemHandler(MI_SelectGuideStrokeReset) {}
execute()569   void execute() override {
570     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
571     if (!vi) return;
572 
573     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
574     if (!tool) return;
575 
576     tool->getViewer()->setGuidedStrokePickerMode(0);
577     tool->getViewer()->setGuidedBackStroke(-1);
578     tool->getViewer()->setGuidedFrontStroke(-1);
579     tool->getViewer()->invalidateAll();
580   }
581 } SelectGuideStrokeResetCommand;
582 
583 class TSelectGuideStrokeNextCommand final : public MenuItemHandler {
584 public:
TSelectGuideStrokeNextCommand()585   TSelectGuideStrokeNextCommand() : MenuItemHandler(MI_SelectNextGuideStroke) {}
execute()586   void execute() override {
587     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
588     if (!vi) return;
589 
590     Preferences *pref = Preferences::instance();
591     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
592                                         pref->getGuidedDrawingType() != 2))
593       return;
594 
595     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
596     if (!tool) return;
597 
598     tool->getViewer()->setGuidedStrokePickerMode(1);
599   }
600 } selectGuideStrokeNextCommand;
601 
602 class TSelectGuideStrokePrevCommand final : public MenuItemHandler {
603 public:
TSelectGuideStrokePrevCommand()604   TSelectGuideStrokePrevCommand() : MenuItemHandler(MI_SelectPrevGuideStroke) {}
execute()605   void execute() override {
606     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
607     if (!vi) return;
608 
609     Preferences *pref = Preferences::instance();
610     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
611                                         pref->getGuidedDrawingType() != 2))
612       return;
613 
614     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
615     if (!tool) return;
616 
617     tool->getViewer()->setGuidedStrokePickerMode(-1);
618   }
619 } selectGuideStrokePrevCommand;
620 
621 class TSelectBothGuideStrokesCommand final : public MenuItemHandler {
622 public:
TSelectBothGuideStrokesCommand()623   TSelectBothGuideStrokesCommand()
624       : MenuItemHandler(MI_SelectBothGuideStrokes) {}
execute()625   void execute() override {
626     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
627     if (!vi) return;
628 
629     Preferences *pref = Preferences::instance();
630     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
631                                         pref->getGuidedDrawingType() != 2))
632       return;
633 
634     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
635     if (!tool) return;
636 
637     tool->getViewer()->setGuidedStrokePickerMode(-2);
638   }
639 } selectBothGuideStrokesCommand;
640 
641 class TTweenGuideStrokesCommand final : public MenuItemHandler {
642 public:
TTweenGuideStrokesCommand()643   TTweenGuideStrokesCommand() : MenuItemHandler(MI_TweenGuideStrokes) {}
execute()644   void execute() override {
645     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
646     if (!vi) return;
647 
648     Preferences *pref = Preferences::instance();
649     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
650                                         pref->getGuidedDrawingType() != 2))
651       return;
652 
653     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
654     if (!tool) return;
655 
656     tool->tweenSelectedGuideStrokes();
657   }
658 } tweenGuideStrokesCommand;
659 
660 class TTweenGuideStrokeToSelectedCommand final : public MenuItemHandler {
661 public:
TTweenGuideStrokeToSelectedCommand()662   TTweenGuideStrokeToSelectedCommand()
663       : MenuItemHandler(MI_TweenGuideStrokeToSelected) {}
execute()664   void execute() override {
665     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
666     if (!vi) return;
667 
668     Preferences *pref = Preferences::instance();
669     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
670                                         pref->getGuidedDrawingType() != 2))
671       return;
672 
673     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
674     if (!tool) return;
675 
676     tool->tweenGuideStrokeToSelected();
677   }
678 } tweenGuideStrokeToSelectedCommand;
679 
680 class TSelectGuidesAndTweenCommand final : public MenuItemHandler {
681 public:
TSelectGuidesAndTweenCommand()682   TSelectGuidesAndTweenCommand()
683       : MenuItemHandler(MI_SelectGuidesAndTweenMode) {}
execute()684   void execute() override {
685     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
686     if (!vi) return;
687 
688     Preferences *pref = Preferences::instance();
689     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
690                                         pref->getGuidedDrawingType() != 2))
691       return;
692 
693     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
694     if (!tool) return;
695 
696     tool->getViewer()->setGuidedStrokePickerMode(-3);
697   }
698 } selectGuidesAndTweenCommand;
699 
700 class TFlipNextStrokeDirectionCommand final : public MenuItemHandler {
701 public:
TFlipNextStrokeDirectionCommand()702   TFlipNextStrokeDirectionCommand() : MenuItemHandler(MI_FlipNextGuideStroke) {}
execute()703   void execute() override {
704     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
705     if (!vi) return;
706 
707     Preferences *pref = Preferences::instance();
708     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
709                                         pref->getGuidedDrawingType() != 2))
710       return;
711 
712     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
713     if (!tool) return;
714 
715     tool->flipGuideStrokeDirection(1);
716   }
717 } flipNextStrokeDirectionCommand;
718 
719 class TFlipPrevStrokeDirectionCommand final : public MenuItemHandler {
720 public:
TFlipPrevStrokeDirectionCommand()721   TFlipPrevStrokeDirectionCommand() : MenuItemHandler(MI_FlipPrevGuideStroke) {}
execute()722   void execute() override {
723     TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
724     if (!vi) return;
725 
726     Preferences *pref = Preferences::instance();
727     if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
728                                         pref->getGuidedDrawingType() != 2))
729       return;
730 
731     TTool *tool = TApp::instance()->getCurrentTool()->getTool();
732     if (!tool) return;
733 
734     tool->flipGuideStrokeDirection(-1);
735   }
736 } flipPrevStrokeDirectionCommand;
737 
738 //=============================================================================
739 // SceneViewer
740 //-----------------------------------------------------------------------------
741 
SceneViewer(ImageUtils::FullScreenWidget * parent)742 SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent)
743     : GLWidgetForHighDpi(parent)
744     , m_pressure(0)
745     , m_lastMousePos(0, 0)
746     , m_mouseButton(Qt::NoButton)
747     , m_foregroundDrawing(false)
748     , m_tabletEvent(false)
749     , m_tabletMove(false)
750     , m_buttonClicked(false)
751     , m_referenceMode(NORMAL_REFERENCE)
752     , m_previewMode(NO_PREVIEW)
753     , m_isMouseEntered(false)
754     , m_forceGlFlush(true)
755     , m_freezedStatus(NO_FREEZED)
756     , m_viewGrabImage(0)
757     , m_FPS(0)
758     , m_hRuler(0)
759     , m_vRuler(0)
760     , m_viewMode(SCENE_VIEWMODE)
761     , m_pos(0, 0)
762     , m_pan3D(TPointD(0, 0))
763     , m_zoomScale3D(0.1)
764     , m_theta3D(20)
765     , m_phi3D(30)
766     , m_dpiScale(TPointD(1, 1))
767     , m_compareSettings()
768     , m_minZ(0)
769     , m_tableDLId(-1)
770     , m_groupIndexToBeEntered(-1)
771     , m_pixelSize(1)
772     , m_eraserPointerOn(false)
773     , m_backupTool("")
774     , m_clipRect()
775     , m_isPicking(false)
776     , m_current3DDevice(NONE)
777     , m_sideRasterPos()
778     , m_topRasterPos()
779     , m_toolDisableReason("")
780     , m_editPreviewSubCamera(false)
781     , m_locator(NULL)
782     , m_isLocator(false)
783     , m_isBusyOnTabletMove(false) {
784   m_visualSettings.m_sceneProperties =
785       TApp::instance()->getCurrentScene()->getScene()->getProperties();
786 #if defined(x64)
787   m_stopMotion = StopMotion::instance();
788 #endif
789   // Enables multiple key input.
790   setAttribute(Qt::WA_KeyCompression);
791   // Enables input methods for Asian languages.
792   setAttribute(Qt::WA_InputMethodEnabled);
793   setFocusPolicy(Qt::StrongFocus);
794   setAcceptDrops(true);
795   this->setMouseTracking(true);
796 // introduced from Qt 5.9
797 #if QT_VERSION >= 0x050900
798   this->setTabletTracking(true);
799 #endif
800 
801   for (int i = 0; i < m_viewAff.size(); ++i) {
802     setViewMatrix(getNormalZoomScale(), i);
803     m_rotationAngle[i] = 0.0;
804   }
805 
806   m_3DSideR = rasterFromQPixmap(svgToPixmap(":Resources/3Dside_r.svg"));
807   m_3DSideL = rasterFromQPixmap(svgToPixmap(":Resources/3Dside_l.svg"));
808   m_3DTop   = rasterFromQPixmap(svgToPixmap(":Resources/3Dtop.svg"));
809 
810   setAttribute(Qt::WA_AcceptTouchEvents);
811   grabGesture(Qt::SwipeGesture);
812   grabGesture(Qt::PanGesture);
813   grabGesture(Qt::PinchGesture);
814 
815   setUpdateBehavior(QOpenGLWidget::PartialUpdate);
816 
817   if (Preferences::instance()->isColorCalibrationEnabled())
818     m_lutCalibrator = new LutCalibrator();
819 }
820 
821 //-----------------------------------------------------------------------------
822 
setVisual(const ImagePainter::VisualSettings & settings)823 void SceneViewer::setVisual(const ImagePainter::VisualSettings &settings) {
824   // m_visualSettings.m_blankColor = settings.m_blankColor;//for the blank
825   // frames, I don't have to repaint the viewer are using updateGl!
826   bool repaint     = m_visualSettings.needRepaint(settings);
827   m_visualSettings = settings;
828   m_visualSettings.m_sceneProperties =
829       TApp::instance()->getCurrentScene()->getScene()->getProperties();
830   if (repaint) GLInvalidateAll();
831 }
832 
833 //-----------------------------------------------------------------------------
834 
~SceneViewer()835 SceneViewer::~SceneViewer() {
836   // notify FilmStripFrames and safely disconnect with this
837   emit aboutToBeDestroyed();
838 
839   if (m_fbo) delete m_fbo;
840 
841   // release all the registered context (once when exit the software)
842   std::set<TGlContext>::iterator ct, cEnd(l_contexts.end());
843   for (ct = l_contexts.begin(); ct != cEnd; ++ct)
844     TGLDisplayListsManager::instance()->releaseContext(*ct);
845   l_contexts.clear();
846 }
847 
848 //-------------------------------------------------------------------------------
849 
850 // Builds the view area, in camera reference
getPreviewRect() const851 TRectD SceneViewer::getPreviewRect() const {
852   TApp *app               = TApp::instance();
853   int row                 = app->getCurrentFrame()->getFrame();
854   TXsheet *xsh            = app->getCurrentXsheet()->getXsheet();
855   TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
856   double cameraZ          = xsh->getZ(cameraId, row);
857   TAffine cameraAff =
858       xsh->getPlacement(cameraId, row) * TScale((1000 + cameraZ) / 1000);
859 
860   TDimensionD cameraSize =
861       app->getCurrentScene()->getScene()->getCurrentCamera()->getSize();
862   TDimension cameraRes =
863       app->getCurrentScene()->getScene()->getCurrentCamera()->getRes();
864   TScale cameraScale(Stage::inch * cameraSize.lx / cameraRes.lx,
865                      Stage::inch * cameraSize.ly / cameraRes.ly);
866 
867   TRectD viewRect(-width() * 0.5, -height() * 0.5, width() * 0.5,
868                   height() * 0.5);
869 
870   return (getViewMatrix() * cameraAff * cameraScale).inv() * viewRect;
871 }
872 
873 //-------------------------------------------------------------------------------
874 
onRenderStarted(int frame)875 void SceneViewer::onRenderStarted(int frame) { emit previewStatusChanged(); }
876 
877 //-------------------------------------------------------------------------------
878 
onRenderCompleted(int frame)879 void SceneViewer::onRenderCompleted(int frame) {
880   invalidateAll();
881   emit previewStatusChanged();
882 }
883 
884 //-------------------------------------------------------------------------------
885 
onPreviewUpdate()886 void SceneViewer::onPreviewUpdate() {
887   update();
888   emit previewStatusChanged();
889 }
890 
891 //-----------------------------------------------------------------------------
892 
setReferenceMode(int referenceMode)893 void SceneViewer::setReferenceMode(int referenceMode) {
894   if (m_referenceMode == referenceMode) return;
895 
896   TApp *app = TApp::instance();
897   if (app->getCurrentFrame()->isEditingLevel())
898     app->getCurrentFrame()->setFrame(app->getCurrentFrame()->getFrame());
899   if (m_freezedStatus != NO_FREEZED) {
900     freeze(false);
901     emit freezeStateChanged(false);
902   }
903 
904   m_referenceMode = referenceMode;
905   invalidateAll();
906   emit onZoomChanged();
907 }
908 
909 //-------------------------------------------------------------------------------
910 
freeze(bool on)911 void SceneViewer::freeze(bool on) {
912   if (!on) {
913     m_viewGrabImage = TRaster32P();
914     m_freezedStatus = NO_FREEZED;
915   } else {
916     setCursor(Qt::ForbiddenCursor);
917     m_freezedStatus = UPDATE_FREEZED;
918   }
919   GLInvalidateAll();
920 }
921 
922 //-------------------------------------------------------------------------------
923 
enablePreview(int previewMode)924 void SceneViewer::enablePreview(int previewMode) {
925   if (m_previewMode == previewMode) return;
926 
927   TApp *app = TApp::instance();
928   if (app->getCurrentFrame()->isEditingLevel() && previewMode != NO_PREVIEW)
929     app->getCurrentFrame()->setFrame(app->getCurrentFrame()->getFrame());
930 
931   if (m_freezedStatus != NO_FREEZED) {
932     freeze(false);
933     emit freezeStateChanged(false);
934   }
935 
936   if (m_previewMode != NO_PREVIEW)
937     Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
938         ->removeListener(this);
939 
940   // Schedule as a listener to Previewer.
941   if (previewMode != NO_PREVIEW) {
942     Previewer *previewer =
943         Previewer::instance(previewMode == SUBCAMERA_PREVIEW);
944     previewer->addListener(this);
945     previewer->update();
946   }
947 
948   m_previewMode = previewMode;
949 
950   GLInvalidateAll();
951 
952   // for updating the title bar
953   emit previewToggled();
954 }
955 
956 //-----------------------------------------------------------------------------
957 
winToWorld(const QPointF & pos) const958 TPointD SceneViewer::winToWorld(const QPointF &pos) const {
959   // coordinate window (origine in alto a sinistra) -> coordinate colonna
960   // (origine al centro dell'immagine)
961   TPointD pp(pos.x() - (double)width() / 2.0,
962              -pos.y() + (double)height() / 2.0);
963   if (is3DView()) {
964     TXsheet *xsh            = TApp::instance()->getCurrentXsheet()->getXsheet();
965     TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
966     double z                = xsh->getStageObject(cameraId)->getZ(
967         TApp::instance()->getCurrentFrame()->getFrame());
968 
969     TPointD p(pp.x - m_pan3D.x, pp.y - m_pan3D.y);
970     p               = p * (1 / m_zoomScale3D);
971     double theta    = m_theta3D * M_PI_180;
972     double phi      = m_phi3D * M_PI_180;
973     double cs_phi   = cos(phi);
974     double sn_phi   = sin(phi);
975     double cs_theta = cos(theta);
976     double sn_theta = sin(theta);
977     TPointD a(cs_phi, sn_theta * sn_phi);   // proiezione di (1,0,0)
978     TPointD b(0, cs_theta);                 // proiezione di (0,1,0)
979     TPointD c(sn_phi, -sn_theta * cs_phi);  // proiezione di (0,0,1)
980     TPointD aa = rotate90(a);
981     TPointD bb = rotate90(b);
982 
983     double abb = a * bb;
984     double baa = b * aa;
985     if (fabs(abb) > 0.001 && fabs(baa) > 0.001) {
986       p -= c * z;
987       TPointD g((p * bb) / (a * bb), (p * aa) / (b * aa));
988       return TAffine() * g;
989     } else
990       return TAffine() * TPointD(0, 0);
991   }
992 
993   return getViewMatrix().inv() * pp;
994 }
995 
996 //-----------------------------------------------------------------------------
997 
winToWorld(const TPointD & winPos) const998 TPointD SceneViewer::winToWorld(const TPointD &winPos) const {
999   return winToWorld(QPointF(winPos.x, height() - winPos.y));
1000 }
1001 
1002 //-----------------------------------------------------------------------------
1003 
worldToPos(const TPointD & worldPos) const1004 TPointD SceneViewer::worldToPos(const TPointD &worldPos) const {
1005   TPointD p = getViewMatrix() * worldPos;
1006   return TPointD(width() / 2 + p.x, height() / 2 + p.y);
1007 }
1008 
1009 //-----------------------------------------------------------------------------
1010 
showEvent(QShowEvent *)1011 void SceneViewer::showEvent(QShowEvent *) {
1012   m_visualSettings.m_sceneProperties =
1013       TApp::instance()->getCurrentScene()->getScene()->getProperties();
1014 
1015   // Se il viewer e' show e il preview e' attivo aggiungo il listner al preview
1016   if (m_previewMode != NO_PREVIEW)
1017     Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->addListener(this);
1018 
1019   TApp *app = TApp::instance();
1020 
1021   TSceneHandle *sceneHandle = app->getCurrentScene();
1022   bool ret = connect(sceneHandle, SIGNAL(sceneSwitched()), this,
1023                      SLOT(resetSceneViewer()));
1024   ret      = ret && connect(sceneHandle, SIGNAL(sceneChanged()), this,
1025                        SLOT(onSceneChanged()));
1026   ret = ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)),
1027                        this, SLOT(onPreferenceChanged(const QString &)));
1028 
1029   TFrameHandle *frameHandle = app->getCurrentFrame();
1030   ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this,
1031                        SLOT(onFrameSwitched()));
1032 
1033   TPaletteHandle *paletteHandle =
1034       app->getPaletteController()->getCurrentLevelPalette();
1035   ret = ret && connect(paletteHandle, SIGNAL(colorStyleChanged(bool)), this,
1036                        SLOT(update()));
1037 
1038   ret = ret && connect(app->getCurrentObject(), SIGNAL(objectSwitched()), this,
1039                        SLOT(onObjectSwitched()));
1040   ret = ret && connect(app->getCurrentObject(), SIGNAL(objectChanged(bool)),
1041                        this, SLOT(update()));
1042 
1043   ret =
1044       ret && connect(app->getCurrentOnionSkin(), SIGNAL(onionSkinMaskChanged()),
1045                      this, SLOT(onOnionSkinMaskChanged()));
1046 
1047   ret = ret && connect(app->getCurrentLevel(), SIGNAL(xshLevelChanged()), this,
1048                        SLOT(update()));
1049   ret = ret && connect(app->getCurrentLevel(), SIGNAL(xshCanvasSizeChanged()),
1050                        this, SLOT(update()));
1051   // when level is switched, update m_dpiScale in order to show white background
1052   // for Ink&Paint work properly
1053   ret = ret &&
1054         connect(app->getCurrentLevel(), SIGNAL(xshLevelSwitched(TXshLevel *)),
1055                 this, SLOT(onLevelSwitched()));
1056 
1057   ret = ret && connect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()), this,
1058                        SLOT(onXsheetChanged()));
1059   ret = ret && connect(app->getCurrentXsheet(), SIGNAL(xsheetSwitched()), this,
1060                        SLOT(update()));
1061 
1062   // update tooltip when tool options are changed
1063   ret = ret && connect(app->getCurrentTool(), SIGNAL(toolChanged()), this,
1064                        SLOT(onToolChanged()));
1065   ret = ret && connect(app->getCurrentTool(), SIGNAL(toolCursorTypeChanged()),
1066                        this, SLOT(onToolChanged()));
1067 
1068   ret = ret &&
1069         connect(app, SIGNAL(tabletLeft()), this, SLOT(resetTabletStatus()));
1070 #if defined(x64)
1071   if (m_stopMotion) {
1072     ret = ret && connect(m_stopMotion, SIGNAL(newLiveViewImageReady()), this,
1073                          SLOT(onNewStopMotionImageReady()));
1074     ret = ret && connect(m_stopMotion, SIGNAL(liveViewStopped()), this,
1075                          SLOT(onStopMotionLiveViewStopped()));
1076   }
1077 #endif
1078   assert(ret);
1079 
1080   if (m_hRuler && m_vRuler) {
1081     if (!viewRulerToggle.getStatus()) {
1082       m_hRuler->hide();
1083       m_vRuler->hide();
1084     } else {
1085       m_hRuler->show();
1086       m_vRuler->show();
1087     }
1088   }
1089   if (m_shownOnce == false) {
1090     fitToCamera();
1091     m_shownOnce = true;
1092   }
1093   TApp::instance()->setActiveViewer(this);
1094 
1095   onPreferenceChanged("ColorCalibration");
1096   update();
1097 }
1098 
1099 //-----------------------------------------------------------------------------
1100 
hideEvent(QHideEvent *)1101 void SceneViewer::hideEvent(QHideEvent *) {
1102   // Se il viewer e' hide e il preview e' attivo rimuovo il listner dal preview
1103   if (m_previewMode != NO_PREVIEW)
1104     Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
1105         ->removeListener(this);
1106 
1107   TApp *app = TApp::instance();
1108 
1109   TSceneHandle *sceneHandle = app->getCurrentScene();
1110   if (sceneHandle) sceneHandle->disconnect(this);
1111 
1112   TFrameHandle *frameHandle = app->getCurrentFrame();
1113   if (frameHandle) frameHandle->disconnect(this);
1114 
1115   TPaletteHandle *paletteHandle =
1116       app->getPaletteController()->getCurrentLevelPalette();
1117   if (paletteHandle) paletteHandle->disconnect(this);
1118 
1119   TObjectHandle *objectHandle = app->getCurrentObject();
1120   if (objectHandle) objectHandle->disconnect(this);
1121 
1122   TOnionSkinMaskHandle *onionHandle = app->getCurrentOnionSkin();
1123   if (onionHandle) onionHandle->disconnect(this);
1124 
1125   TXshLevelHandle *levelHandle = app->getCurrentLevel();
1126   if (levelHandle) levelHandle->disconnect(this);
1127 
1128   TXsheetHandle *xsheetHandle = app->getCurrentXsheet();
1129   if (xsheetHandle) xsheetHandle->disconnect(this);
1130 
1131   ToolHandle *toolHandle = app->getCurrentTool();
1132   if (toolHandle) toolHandle->disconnect(this);
1133 
1134   disconnect(app, SIGNAL(tabletLeft()), this, SLOT(resetTabletStatus()));
1135 
1136 #if defined(x64)
1137   if (m_stopMotion) {
1138     disconnect(m_stopMotion, SIGNAL(newImageReady()), this,
1139                SLOT(onNewStopMotionImageReady()));
1140     disconnect(m_stopMotion, SIGNAL(liveViewStopped()), this,
1141                SLOT(onStopMotionLiveViewStopped()));
1142   }
1143 #endif
1144 
1145   // hide locator
1146   if (m_locator && m_locator->isVisible()) m_locator->hide();
1147 }
1148 
getVGuideCount()1149 int SceneViewer::getVGuideCount() {
1150   if (viewGuideToggle.getStatus())
1151     return m_vRuler->getGuideCount();
1152   else
1153     return 0;
1154 }
getHGuideCount()1155 int SceneViewer::getHGuideCount() {
1156   if (viewGuideToggle.getStatus())
1157     return m_hRuler->getGuideCount();
1158   else
1159     return 0;
1160 }
1161 
getVGuide(int index)1162 double SceneViewer::getVGuide(int index) { return m_vRuler->getGuide(index); }
getHGuide(int index)1163 double SceneViewer::getHGuide(int index) { return m_hRuler->getGuide(index); }
1164 
1165 #if defined(x64)
1166 //-----------------------------------------------------------------------------
1167 
onNewStopMotionImageReady()1168 void SceneViewer::onNewStopMotionImageReady() {
1169   if (m_stopMotion->m_hasLineUpImage) {
1170     // if (m_hasStopMotionLineUpImage) delete m_stopMotionLineUpImage;
1171     m_stopMotionLineUpImage =
1172         (TRasterImageP)m_stopMotion->m_lineUpImage->clone();
1173     m_stopMotionLineUpImage->setDpi(m_stopMotion->m_liveViewDpi.x,
1174                                     m_stopMotion->m_liveViewDpi.y);
1175     m_hasStopMotionLineUpImage = true;
1176   }
1177   if (m_stopMotion->m_hasLiveViewImage) {
1178     // if (m_hasStopMotionImage) delete m_stopMotionImage;
1179     m_stopMotionImage = m_stopMotion->m_liveViewImage->clone();
1180     m_stopMotionImage->setDpi(m_stopMotion->m_liveViewDpi.x,
1181                               m_stopMotion->m_liveViewDpi.y);
1182     m_hasStopMotionImage = true;
1183     if (m_stopMotion->m_canon->m_pickLiveViewZoom) {
1184       setToolCursor(this, ToolCursor::ZoomCursor);
1185     }
1186     onSceneChanged();
1187   }
1188 }
1189 
1190 //-----------------------------------------------------------------------------
1191 
onStopMotionLiveViewStopped()1192 void SceneViewer::onStopMotionLiveViewStopped() {
1193   m_hasStopMotionImage       = false;
1194   m_hasStopMotionLineUpImage = false;
1195   onSceneChanged();
1196 }
1197 
1198 #endif  // x64
1199 //-----------------------------------------------------------------------------
1200 
onPreferenceChanged(const QString & prefName)1201 void SceneViewer::onPreferenceChanged(const QString &prefName) {
1202   if (prefName == "ColorCalibration") {
1203     if (Preferences::instance()->isColorCalibrationEnabled()) {
1204       // if the window is so shriked that the gl widget is empty,
1205       // showEvent can be called before creating the context.
1206       if (!context()) return;
1207       makeCurrent();
1208       if (!m_lutCalibrator)
1209         m_lutCalibrator = new LutCalibrator();
1210       else
1211         m_lutCalibrator->cleanup();
1212       m_lutCalibrator->initialize();
1213       connect(context(), SIGNAL(aboutToBeDestroyed()), this,
1214               SLOT(onContextAboutToBeDestroyed()));
1215       if (m_lutCalibrator->isValid() && !m_fbo)
1216         m_fbo = new QOpenGLFramebufferObject(width(), height());
1217       doneCurrent();
1218     }
1219     update();
1220   }
1221 }
1222 
1223 //-----------------------------------------------------------------------------
initializeGL()1224 void SceneViewer::initializeGL() {
1225   initializeOpenGLFunctions();
1226 
1227   registerContext();
1228 
1229   // to be computed once through the software
1230   if (m_lutCalibrator && !m_lutCalibrator->isInitialized()) {
1231     m_lutCalibrator->initialize();
1232     connect(context(), SIGNAL(aboutToBeDestroyed()), this,
1233             SLOT(onContextAboutToBeDestroyed()));
1234   }
1235 
1236   // glClearColor(1.0,1.0,1.0,1);
1237   glClear(GL_COLOR_BUFFER_BIT);
1238 
1239   if (m_firstInitialized)
1240     m_firstInitialized = false;
1241   else {
1242     resizeGL(width(), height());
1243     update();
1244   }
1245   // re-computing the display list for the table
1246   m_tableDLId = -1;
1247 }
1248 
1249 //-----------------------------------------------------------------------------
1250 
resizeGL(int w,int h)1251 void SceneViewer::resizeGL(int w, int h) {
1252   w *= getDevPixRatio();
1253   h *= getDevPixRatio();
1254   glViewport(0, 0, w, h);
1255   glMatrixMode(GL_PROJECTION);
1256   glLoadIdentity();
1257   glOrtho(0, w, 0, h, -4000, 4000);
1258   m_projectionMatrix.setToIdentity();
1259   m_projectionMatrix.ortho(0, w, 0, h, -4000, 4000);
1260 
1261   glMatrixMode(GL_MODELVIEW);
1262   glLoadIdentity();
1263   glTranslatef(0.375, 0.375, 0.0);
1264   // make the center of the viewer = origin
1265   glTranslated(w * 0.5, h * 0.5, 0);
1266 
1267   if (m_freezedStatus == NORMAL_FREEZED) m_freezedStatus = UPDATE_FREEZED;
1268 
1269   if (m_previewMode != NO_PREVIEW) requestTimedRefresh();
1270 
1271   // remake fbo with new size
1272   if (m_lutCalibrator && m_lutCalibrator->isValid()) {
1273     if (m_fbo) delete m_fbo;
1274     m_fbo = new QOpenGLFramebufferObject(w, h);
1275   }
1276 
1277   // for updating the navigator in levelstrip
1278   emit refreshNavi();
1279 }
1280 
1281 //-----------------------------------------------------------------------------
1282 
drawBuildVars()1283 void SceneViewer::drawBuildVars() {
1284   TApp *app = TApp::instance();
1285 
1286   int frame    = app->getCurrentFrame()->getFrame();
1287   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1288 
1289   // Camera affine
1290   TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
1291   TStageObject *camera    = xsh->getStageObject(cameraId);
1292   TAffine cameraPlacement = camera->getPlacement(frame);
1293   double cameraZ          = camera->getZ(frame);
1294 
1295   m_drawCameraAff =
1296       getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
1297 
1298   // Table affine
1299   TStageObject *table    = xsh->getStageObject(TStageObjectId::TableId);
1300   TAffine tablePlacement = table->getPlacement(frame);
1301   double tableZ          = table->getZ(frame);
1302   TAffine placement;
1303 
1304   m_drawIsTableVisible = TStageObject::perspective(
1305       placement, cameraPlacement, cameraZ, tablePlacement, tableZ, 0);
1306   m_drawTableAff = getViewMatrix() * tablePlacement;
1307 
1308   // Camera test check
1309   m_drawCameraTest = CameraTestCheck::instance()->isEnabled();
1310 
1311   if (m_previewMode == NO_PREVIEW) {
1312     m_drawEditingLevel = app->getCurrentFrame()->isEditingLevel();
1313     m_viewMode         = m_drawEditingLevel ? LEVEL_VIEWMODE : SCENE_VIEWMODE;
1314     m_draw3DMode       = is3DView() && (m_previewMode != SUBCAMERA_PREVIEW);
1315   } else {
1316     m_drawEditingLevel = false;
1317     m_viewMode         = app->getCurrentFrame()->isEditingLevel();
1318     m_draw3DMode       = false;
1319   }
1320 
1321   // Clip rect
1322   if (!m_clipRect.isEmpty() && !m_draw3DMode) {
1323     m_actualClipRect = getActualClipRect(getViewMatrix());
1324     m_actualClipRect += TPoint(width() * 0.5, height() * 0.5);
1325   }
1326 
1327   TTool *tool = app->getCurrentTool()->getTool();
1328   if (tool && !m_isLocator) tool->setViewer(this);
1329 }
1330 
1331 //-----------------------------------------------------------------------------
1332 
drawEnableScissor()1333 void SceneViewer::drawEnableScissor() {
1334   if (!m_clipRect.isEmpty() && !m_draw3DMode) {
1335     glEnable(GL_SCISSOR_TEST);
1336     glScissor(m_actualClipRect.x0, m_actualClipRect.y0,
1337               m_actualClipRect.getLx(), m_actualClipRect.getLy());
1338   }
1339 }
1340 
1341 //-----------------------------------------------------------------------------
1342 
drawDisableScissor()1343 void SceneViewer::drawDisableScissor() {
1344   if (!m_clipRect.isEmpty() && !m_draw3DMode) {
1345     glDisable(GL_SCISSOR_TEST);
1346   }
1347   // clear the clipping rect
1348   m_clipRect.empty();
1349 }
1350 
1351 //-----------------------------------------------------------------------------
1352 
drawBackground()1353 void SceneViewer::drawBackground() {
1354   TApp *app         = TApp::instance();
1355   ToonzScene *scene = app->getCurrentScene()->getScene();
1356 
1357   if (m_visualSettings.m_colorMask == 0) {
1358     TPixel32 bgColor;
1359 
1360     if (isPreviewEnabled())
1361       bgColor = Preferences::instance()->getPreviewBgColor();
1362     else
1363       bgColor = Preferences::instance()->getViewerBgColor();
1364     glClearColor(bgColor.r / 255.0f, bgColor.g / 255.0f, bgColor.b / 255.0f,
1365                  1.0);
1366   } else
1367     glClearColor(0, 0, 0, 1.0);
1368 
1369   glClear(GL_COLOR_BUFFER_BIT);
1370   if (glGetError() == GL_INVALID_FRAMEBUFFER_OPERATION) {
1371     /* 起動時一回目になぜか GL_FRAMEBUFFER_COMPLETE なのに invalid operation
1372      * が出る  */
1373     GLenum status = 0;
1374 #ifdef _WIN32
1375     PROC proc = wglGetProcAddress("glCheckFramebufferStatusEXT");
1376     if (proc != nullptr)
1377       status = reinterpret_cast<PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC>(proc)(
1378           GL_FRAMEBUFFER);
1379 #else
1380     status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1381 #endif
1382     printf("GL_INVALID_FRAMEBUFFER_OPERATION: framebuffer:%d\n", status);
1383   }
1384 }
1385 
check_framebuffer_status()1386 static bool check_framebuffer_status() {
1387 #ifdef _WIN32
1388   PROC proc = wglGetProcAddress("glCheckFramebufferStatusEXT");
1389   if (proc == nullptr) return true;
1390   GLenum s = reinterpret_cast<PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC>(proc)(
1391       GL_FRAMEBUFFER);
1392 #else
1393   GLenum s = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1394 #endif
1395   if (s == GL_FRAMEBUFFER_UNDEFINED)
1396     printf("Warning: FB undefined: %d\n", s);
1397   else if (s == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
1398     printf("Warning: FB incomplete(attachment): %d\n", s);
1399   else if (s == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
1400     printf("Warning: FB incomplete(missing attachment): %d\n", s);
1401   else if (s == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
1402     printf("Warning: FB incomplete(draw buffer): %d\n", s);
1403   else if (s == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
1404     printf("Warning: FB incomplete(read buffer): %d\n", s);
1405   else if (s == GL_FRAMEBUFFER_UNSUPPORTED)
1406     printf("Warning: FB unsupported: %d\n", s);
1407   else if (s == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
1408     printf("Warning: FB incomplete(multi-sample): %d\n", s);
1409   else if (s == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS)
1410     printf("Warning: FB incomplete(multi-sample): %d\n", s);
1411   else if (s == GL_FRAMEBUFFER_COMPLETE)
1412     ;
1413   else
1414     printf("Warning: FB not complete(unknown cause): %d\n", s);
1415   return s == GL_FRAMEBUFFER_COMPLETE;
1416 }
1417 
1418 //-----------------------------------------------------------------------------
1419 
drawCameraStand()1420 void SceneViewer::drawCameraStand() {
1421   GLint e = glGetError();
1422   check_framebuffer_status();
1423   assert(e == GL_NO_ERROR);
1424 
1425   TXshSimpleLevel::m_rasterizePli = rasterizePliToggle.getStatus();
1426 
1427   // clear
1428   if (m_draw3DMode) {
1429     glEnable(GL_DEPTH_TEST);
1430     glDepthFunc(GL_ALWAYS);
1431     glClear(GL_DEPTH_BUFFER_BIT);
1432     glPushMatrix();
1433     mult3DMatrix();
1434   } else
1435     glDisable(GL_DEPTH_TEST);
1436 
1437   assert(glGetError() == GL_NO_ERROR);
1438 
1439   // draw table
1440   if (!m_draw3DMode && viewTableToggle.getStatus() && m_drawIsTableVisible &&
1441       m_visualSettings.m_colorMask == 0 && m_drawEditingLevel == false &&
1442       !m_drawCameraTest) {
1443     glPushMatrix();
1444     tglMultMatrix(m_drawTableAff);
1445     ViewerDraw::drawDisk(m_tableDLId);
1446     glPopMatrix();
1447   }
1448 
1449   // draw colorcard (with camera BG color)
1450   // Hide camera BG when level editing mode.
1451   if (m_drawEditingLevel == false && viewClcToggle.getStatus() &&
1452       !m_drawCameraTest) {
1453     glPushMatrix();
1454     tglMultMatrix(m_drawCameraAff);
1455     glEnable(GL_BLEND);
1456     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1457     ViewerDraw::drawColorcard(m_visualSettings.m_colorMask);
1458     glDisable(GL_BLEND);
1459     glPopMatrix();
1460   }
1461 
1462   // Show white background when level editing mode.
1463   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
1464   if (m_drawEditingLevel && tool && tool->isEnabled()) {
1465     if (!m_isLocator) tool->setViewer(this);
1466     glPushMatrix();
1467     if (m_referenceMode == CAMERA3D_REFERENCE) {
1468       mult3DMatrix();
1469       tglMultMatrix(tool->getMatrix());
1470     } else {
1471       tglMultMatrix(getViewMatrix() * tool->getMatrix());
1472     }
1473     glScaled(m_dpiScale.x, m_dpiScale.y, 1);
1474 
1475     TImageP image = tool->getImage(false);
1476 
1477     TToonzImageP ti  = image;
1478     TRasterImageP ri = image;
1479     if (ti) {
1480       TRect imgRect(0, 0, ti->getSize().lx - 1, ti->getSize().ly - 1);
1481       TRectD bbox = ToonzImageUtils::convertRasterToWorld(imgRect, ti);
1482 
1483       TPixel32 imgRectColor;
1484       // draw black rectangle instead, if the BlackBG check is ON
1485       if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg)
1486         imgRectColor = TPixel::Black;
1487       else
1488         imgRectColor = Preferences::instance()->getLevelEditorBoxColor();
1489       ToolUtils::fillRect(bbox * ti->getSubsampling(), imgRectColor);
1490     } else if (ri) {
1491       TRectD bbox = ri->getBBox();
1492       bbox.x0 -= ri->getBBox().getLx() * 0.5;
1493       bbox.x1 -= ri->getBBox().getLx() * 0.5;
1494       bbox.y0 -= ri->getBBox().getLy() * 0.5;
1495       bbox.y1 -= ri->getBBox().getLy() * 0.5;
1496 
1497       TPixel32 imgRectColor;
1498       // draw black rectangle instead, if the BlackBG check is ON
1499       if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg)
1500         imgRectColor = TPixel::Black;
1501       else
1502         imgRectColor = Preferences::instance()->getLevelEditorBoxColor();
1503       ToolUtils::fillRect(bbox * ri->getSubsampling(), imgRectColor);
1504     }
1505     glPopMatrix();
1506   }
1507 
1508   // draw 3dframe
1509   if (m_draw3DMode) {
1510     glDepthFunc(GL_LEQUAL);
1511     ViewerDraw::draw3DFrame(m_minZ, m_phi3D);
1512     glDepthFunc(GL_ALWAYS);
1513   }
1514 
1515   // draw scene
1516   assert(glGetError() == GL_NO_ERROR);
1517   drawScene();
1518   assert((glGetError()) == GL_NO_ERROR);
1519 }
1520 
1521 //-----------------------------------------------------------------------------
1522 
drawPreview()1523 void SceneViewer::drawPreview() {
1524   const double inch   = Stage::inch;
1525   TApp *app           = TApp::instance();
1526   int row             = app->getCurrentFrame()->getFrame();
1527   TCamera *currCamera = app->getCurrentScene()->getScene()->getCurrentCamera();
1528   TDimensionD cameraSize = currCamera->getSize();
1529 
1530   Previewer *previewer =
1531       Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW);
1532 
1533   TRasterP ras =
1534       previewer->getRaster(row, m_visualSettings.m_recomputeIfNeeded);
1535   if (ras) {
1536     TRectD previewStageRectD, cameraStageRectD = currCamera->getStageRect();
1537 
1538     TRect subCameraRect(currCamera->getInterestRect());
1539     if (m_previewMode == SUBCAMERA_PREVIEW && subCameraRect.getLx() > 0 &&
1540         subCameraRect.getLy() > 0)
1541       previewStageRectD = currCamera->getInterestStageRect();
1542     else
1543       previewStageRectD = cameraStageRectD;
1544 
1545     TAffine rasterToStageRef(
1546         previewStageRectD.getLx() / ras->getLx(), 0.0,
1547         previewStageRectD.x0 + 0.5 * previewStageRectD.getLx(), 0.0,
1548         previewStageRectD.getLy() / ras->getLy(),
1549         previewStageRectD.y0 + 0.5 * previewStageRectD.getLy());
1550 
1551     TDimension dim(width(), height());
1552     TAffine finalAff              = m_drawCameraAff * rasterToStageRef;
1553     m_visualSettings.m_useTexture = !Preferences::instance()->useDrawPixel();
1554     ImagePainter::paintImage(TRasterImageP(ras), ras->getSize(), dim, finalAff,
1555                              m_visualSettings, m_compareSettings, TRect());
1556   }
1557 
1558   glPushMatrix();
1559   tglMultMatrix(m_drawCameraAff);
1560 
1561   TRectD frameRect(cameraSize);
1562   frameRect.x1 *= inch;
1563   frameRect.y1 *= inch;
1564   frameRect -= 0.5 * (frameRect.getP00() + frameRect.getP11());
1565 
1566   if (m_visualSettings.m_blankColor != TPixel::Transparent) {
1567     tglColor(m_visualSettings.m_blankColor);
1568     tglFillRect(frameRect);
1569   }
1570 
1571   if (!previewer->isFrameReady(row) ||
1572       (app->getCurrentFrame()->isPlaying() && previewer->isBusy())) {
1573     glColor3d(1, 0, 0);
1574 
1575     tglDrawRect(frameRect);
1576     tglDrawRect(frameRect.enlarge(5));
1577   }
1578 
1579   glPopMatrix();
1580 }
1581 
1582 //-----------------------------------------------------------------------------
1583 
drawOverlay()1584 void SceneViewer::drawOverlay() {
1585   TApp *app = TApp::instance();
1586 
1587   // draw camera mask
1588   if (m_referenceMode == CAMERA_REFERENCE && !m_drawCameraTest) {
1589     glPushMatrix();
1590     tglMultMatrix(m_drawCameraAff);
1591     ViewerDraw::drawCameraMask(this);
1592     glPopMatrix();
1593   }
1594 
1595   // draw FieldGuide
1596   if (fieldGuideToggle.getStatus()) {
1597     glPushMatrix();
1598     tglMultMatrix(m_drawTableAff);
1599     ViewerDraw::drawFieldGuide();
1600     glPopMatrix();
1601   }
1602 
1603   if (!m_drawCameraTest) {
1604     // draw grid & guides
1605     if (viewGuideToggle.getStatus() &&
1606         ((m_vRuler && m_vRuler->getGuideCount()) ||
1607          (m_hRuler && m_hRuler->getGuideCount()))) {
1608       glPushMatrix();
1609       tglMultMatrix(getViewMatrix());
1610       ViewerDraw::drawGridAndGuides(
1611           this, (m_draw3DMode) ? m_zoomScale3D : m_viewAff[m_viewMode].det(),
1612           m_vRuler, m_hRuler, false);
1613       glPopMatrix();
1614     }
1615 
1616     // draw camera
1617     if (viewCameraToggle.getStatus() && m_drawEditingLevel == false) {
1618       unsigned long f = 0;
1619       if (m_referenceMode == CAMERA_REFERENCE)
1620         f |= ViewerDraw::CAMERA_REFERENCE;
1621       if (m_draw3DMode) f |= ViewerDraw::CAMERA_3D;
1622       if (m_previewMode == SUBCAMERA_PREVIEW || m_editPreviewSubCamera)
1623         f |= ViewerDraw::SUBCAMERA;
1624       if (m_draw3DMode)
1625         ViewerDraw::draw3DCamera(f, m_minZ, m_phi3D);
1626       else {
1627         glPushMatrix();
1628         tglMultMatrix(m_drawCameraAff);
1629         m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
1630         ViewerDraw::drawCamera(f, m_pixelSize);
1631         glPopMatrix();
1632       }
1633     }
1634 
1635 #ifdef WITH_CANON
1636     if (m_stopMotion->m_liveViewStatus == StopMotion::LiveViewOpen &&
1637         app->getCurrentFrame()->getFrame() ==
1638             m_stopMotion->getXSheetFrameNumber() - 1) {
1639       int x0, x1, y0, y1;
1640       rect().getCoords(&x0, &y0, &x1, &y1);
1641       x0 = (-(x1 / 2)) + 15;
1642       y0 = ((y1 / 2)) - 15;
1643       tglDrawDisk(TPointD(x0, y0), 10);
1644     }
1645 
1646     // draw Stop Motion Zoom Box
1647     if (m_stopMotion->m_liveViewStatus == 2 &&
1648         m_stopMotion->m_canon->m_pickLiveViewZoom) {
1649       glPushMatrix();
1650       tglMultMatrix(m_drawCameraAff);
1651       m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
1652       TRect rect  = m_stopMotion->m_canon->m_zoomRect;
1653 
1654       glColor3d(1.0, 0.0, 0.0);
1655 
1656       // border
1657       glBegin(GL_LINE_STRIP);
1658       glVertex2d(rect.x0, rect.y0);
1659       glVertex2d(rect.x0, rect.y1 - m_pixelSize);
1660       glVertex2d(rect.x1 - m_pixelSize, rect.y1 - m_pixelSize);
1661       glVertex2d(rect.x1 - m_pixelSize, rect.y0);
1662       glVertex2d(rect.x0, rect.y0);
1663       glEnd();
1664 
1665       glPopMatrix();
1666     }
1667 
1668 #endif
1669 
1670     // safe area
1671     if (safeAreaToggle.getStatus() && m_drawEditingLevel == false &&
1672         !is3DView()) {
1673       glPushMatrix();
1674       tglMultMatrix(m_drawCameraAff);
1675       ViewerDraw::drawSafeArea();
1676       glPopMatrix();
1677     }
1678 
1679     // record fps (frame per second)
1680     if (app->getCurrentFrame()->isPlaying())
1681       m_FPS = getActualFrameRate();
1682     else
1683       m_FPS = 0;
1684 
1685     if (m_freezedStatus != NO_FREEZED) {
1686       tglColor(TPixel32::Red);
1687       tglDrawText(TPointD(0, 0), "FROZEN");
1688     }
1689     assert(glGetError() == GL_NO_ERROR);
1690 
1691   }  //! cameraTest
1692 
1693   // draw 3d Top/Side Buttons
1694   if (m_draw3DMode && !m_isPicking) {
1695     tglColor(TPixel32::Black);
1696 
1697     GLdouble modelView3D[16];
1698     GLdouble projection3D[16];
1699     GLint viewport3D[4];
1700 
1701     glGetDoublev(GL_MODELVIEW_MATRIX, modelView3D);
1702     glGetDoublev(GL_PROJECTION_MATRIX, projection3D);
1703     glGetIntegerv(GL_VIEWPORT, viewport3D);
1704 
1705     if (m_phi3D > 0) {
1706       T3DPointD topRasterPos3D = computeNew3DPosition(
1707           T3DPointD(500, 500, 1000), TPointD(-10, -10), m_topRasterPos,
1708           modelView3D, projection3D, viewport3D, getDevPixRatio());
1709       glRasterPos3f(topRasterPos3D.x, topRasterPos3D.y, topRasterPos3D.z);
1710       glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1711       glDrawPixels(m_3DTop->getWrap(), m_3DTop->getLy(), TGL_FMT, TGL_TYPE,
1712                    m_3DTop->getRawData());
1713 
1714       T3DPointD sideRasterPos3D = computeNew3DPosition(
1715           T3DPointD(-500, -500, 1000), TPointD(-10, -10), m_sideRasterPos,
1716           modelView3D, projection3D, viewport3D, getDevPixRatio());
1717       glRasterPos3f(sideRasterPos3D.x, sideRasterPos3D.y, sideRasterPos3D.z);
1718       glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1719       glDrawPixels(m_3DSideR->getWrap(), m_3DSideR->getLy(), TGL_FMT, TGL_TYPE,
1720                    m_3DSideR->getRawData());
1721     } else {
1722       T3DPointD topRasterPos3D = computeNew3DPosition(
1723           T3DPointD(-500, 500, 1000), TPointD(-10, -10), m_topRasterPos,
1724           modelView3D, projection3D, viewport3D, getDevPixRatio());
1725       glRasterPos3f(topRasterPos3D.x, topRasterPos3D.y, topRasterPos3D.z);
1726       glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1727       glDrawPixels(m_3DTop->getWrap(), m_3DTop->getLy(), TGL_FMT, TGL_TYPE,
1728                    m_3DTop->getRawData());
1729 
1730       T3DPointD sideRasterPos3D = computeNew3DPosition(
1731           T3DPointD(500, -500, 1000), TPointD(-10, -10), m_sideRasterPos,
1732           modelView3D, projection3D, viewport3D, getDevPixRatio());
1733       glRasterPos3f(sideRasterPos3D.x, sideRasterPos3D.y, sideRasterPos3D.z);
1734       glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1735       glDrawPixels(m_3DSideL->getWrap(), m_3DSideL->getLy(), TGL_FMT, TGL_TYPE,
1736                    m_3DSideL->getRawData());
1737     }
1738   }
1739 
1740   if (m_draw3DMode) {
1741     glDisable(GL_DEPTH_TEST);
1742     glPopMatrix();
1743     assert(glGetError() == GL_NO_ERROR);
1744   }
1745 
1746   // draw tool gadgets
1747   TTool *tool         = app->getCurrentTool()->getTool();
1748   TXshSimpleLevel *sl = app->getCurrentLevel()->getSimpleLevel();
1749   // Call tool->draw() even if the level is read only (i.e. to show hooks)
1750   if (tool && (tool->isEnabled() || (sl && sl->isReadOnly()))) {
1751     // tool->setViewer(this);                            // Moved at
1752     // drawBuildVars(), before drawing anything
1753     glPushMatrix();
1754     if (m_draw3DMode) {
1755       mult3DMatrix();
1756       tglMultMatrix(tool->getMatrix());
1757     } else
1758       tglMultMatrix(getViewMatrix() * tool->getMatrix());
1759     if (tool->getToolType() & TTool::LevelTool &&
1760         !app->getCurrentObject()->isSpline())
1761       glScaled(m_dpiScale.x, m_dpiScale.y, 1);
1762     m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
1763     tool->draw();
1764     glPopMatrix();
1765     // Used (only in the T_RGBPicker tool) to notify and set the currentColor
1766     // outside the draw() methods:
1767     // using special style there was a conflict between the draw() methods of
1768     // the tool
1769     // and the genaration of the icon inside the style editor (makeIcon()) which
1770     // use
1771     // another glContext
1772     if (tool->getName() == "T_RGBPicker") tool->onImageChanged();
1773 
1774     // draw cross at the center of the locator window
1775     if (m_isLocator) {
1776       glColor3d(1.0, 0.0, 0.0);
1777       tglDrawSegment(TPointD(-4, 0), TPointD(5, 0));
1778       tglDrawSegment(TPointD(0, -4), TPointD(0, 5));
1779     }
1780   }
1781 }
1782 
1783 //-----------------------------------------------------------------------------
1784 
drawFpsGraph(int t0,int t1)1785 static void drawFpsGraph(int t0, int t1) {
1786   glDisable(GL_BLEND);
1787   static std::deque<std::pair<int, int>> times;
1788   times.push_back(std::make_pair(t0, t1));
1789   while (times.size() > 200) times.pop_front();
1790   double x0 = 10, y0 = 10;
1791   double x1 = x0 + 200;
1792   double y1 = y0 + 150;
1793   glPushMatrix();
1794   glLoadIdentity();
1795   glColor3d(0, 0, 0);
1796   glRectd(x0, y0, x1, y1);
1797   glColor3d(0, 0.5, 1);
1798   glBegin(GL_LINE_STRIP);
1799   glVertex2d(x0, y0);
1800   glVertex2d(x1, y0);
1801   glVertex2d(x1, y1);
1802   glVertex2d(x0, y1);
1803   glVertex2d(x0, y0);
1804   glEnd();
1805   glColor3d(0.5, 0.5, 0.5);
1806   glBegin(GL_LINES);
1807   for (int y = y0 + 5; y < y1; y += 20) {
1808     glVertex2d(x0, y);
1809     glVertex2d(x1, y);
1810   }
1811   for (int i = 0; i < (int)times.size(); i++) {
1812     double x = x1 - i;
1813     glColor3d(1, 0, 0);
1814     glVertex2d(x, y0);
1815     glVertex2d(x, y0 + 5 + times[i].first / 5);
1816     glColor3d(0, 1, 0);
1817     glVertex2d(x, y0 + 5 + times[i].first / 5);
1818     glVertex2d(x, y0 + 5 + times[i].second / 5);
1819   }
1820   glEnd();
1821   glPopMatrix();
1822 }
1823 
1824 //-----------------------------------------------------------------------------
1825 
1826 //#define FPS_HISTOGRAM
1827 
paintGL()1828 void SceneViewer::paintGL() {
1829 #ifdef _DEBUG
1830   if (!check_framebuffer_status()) {
1831     /* QGLWidget の widget 生成/削除のタイミングで(platform によって?)
1832      * GL_FRAMEBUFFER_UNDEFINED の状態で paintGL() が呼ばれてしまうようだ */
1833     return;
1834   }
1835 #endif
1836 #ifdef MACOSX
1837   // followin lines are necessary to solve a problem on iMac20
1838   // It seems that for some errors in the openGl implementation, buffers are not
1839   // set corretly.
1840   if (m_isMouseEntered && m_forceGlFlush) {
1841     m_isMouseEntered = false;
1842     m_forceGlFlush   = false;
1843     glDrawBuffer(GL_FRONT);
1844     glFlush();
1845     glDrawBuffer(GL_BACK);
1846   }
1847 #endif
1848 
1849 #ifdef FPS_HISTOGRAM
1850   QTime time;
1851   time.start();
1852 #endif
1853 
1854   if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
1855     m_fbo->bind();
1856 
1857   if (m_hRuler && m_vRuler) {
1858     if (!viewRulerToggle.getStatus() &&
1859         (m_hRuler->isVisible() || m_vRuler->isVisible())) {
1860       m_hRuler->hide();
1861       m_vRuler->hide();
1862     } else if (viewRulerToggle.getStatus() &&
1863                (!m_hRuler->isVisible() || !m_vRuler->isVisible())) {
1864       m_hRuler->show();
1865       m_vRuler->show();
1866     }
1867   }
1868 
1869   // Il freezed e' attivo ed e' in stato "normale": mostro l'immagine grabbata.
1870   if (m_freezedStatus == NORMAL_FREEZED) {
1871     assert(!!m_viewGrabImage);
1872     m_viewGrabImage->lock();
1873     glPushMatrix();
1874     glLoadIdentity();
1875 
1876     glRasterPos2d(0, 0);
1877     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1878 
1879     glDrawPixels(m_viewGrabImage->getLx(), m_viewGrabImage->getLy(), TGL_FMT,
1880                  TGL_TYPE, m_viewGrabImage->getRawData());
1881 
1882     glPopMatrix();
1883     m_viewGrabImage->unlock();
1884 
1885     if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
1886       m_lutCalibrator->onEndDraw(m_fbo);
1887 
1888     return;
1889   }
1890 
1891   drawBuildVars();
1892 
1893   // This seems not to be necessary for now.
1894   // copyFrontBufferToBackBuffer();
1895 
1896   drawEnableScissor();
1897   drawBackground();
1898 
1899   if (m_previewMode != FULL_PREVIEW) {
1900     drawCameraStand();
1901   }
1902 
1903   if (isPreviewEnabled()) drawPreview();
1904 
1905   drawOverlay();
1906 
1907   drawDisableScissor();
1908 
1909   // Il freezed e' attivo ed e' in stato "update": faccio il grab del viewer.
1910   if (m_freezedStatus == UPDATE_FREEZED) {
1911     m_viewGrabImage = rasterFromQImage(grabFramebuffer());
1912     m_freezedStatus = NORMAL_FREEZED;
1913   }
1914 
1915 #ifdef FPS_HISTOGRAM
1916   int t0 = time.elapsed();
1917   glFlush();
1918   glFinish();
1919   int t1 = time.elapsed();
1920   drawFpsGraph(t0, t1);
1921 #endif
1922   // TOfflineGL::setContextManager(0);
1923 
1924   if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
1925     m_lutCalibrator->onEndDraw(m_fbo);
1926 }
1927 
1928 //-----------------------------------------------------------------------------
1929 
drawScene()1930 void SceneViewer::drawScene() {
1931   TApp *app         = TApp::instance();
1932   ToonzScene *scene = app->getCurrentScene()->getScene();
1933   int frame         = app->getCurrentFrame()->getFrame();
1934   TXsheet *xsh      = app->getCurrentXsheet()->getXsheet();
1935   TRect clipRect    = getActualClipRect(getViewMatrix());
1936   clipRect += TPoint(width() * 0.5, height() * 0.5);
1937 
1938   ChildStack *childStack = scene->getChildStack();
1939   bool editInPlace       = editInPlaceToggle.getStatus() &&
1940                      !app->getCurrentFrame()->isEditingLevel();
1941 
1942   bool fillFullColorRaster = TXshSimpleLevel::m_fillFullColorRaster;
1943   TXshSimpleLevel::m_fillFullColorRaster = false;
1944 
1945   // Guided Drawing Check
1946   int useGuidedDrawing  = Preferences::instance()->getGuidedDrawingType();
1947   TTool *tool           = app->getCurrentTool()->getTool();
1948   int guidedFrontStroke = tool ? tool->getViewer()->getGuidedFrontStroke() : -1;
1949   int guidedBackStroke  = tool ? tool->getViewer()->getGuidedBackStroke() : -1;
1950 
1951   m_minZ = 0;
1952   if (is3DView()) {
1953     Stage::OpenGlPainter painter(getViewMatrix(), clipRect, m_visualSettings,
1954                                  true, false);
1955     painter.enableCamera3D(true);
1956     painter.setPhi(m_phi3D);
1957     int xsheetLevel = 0;
1958     std::pair<TXsheet *, int> xr;
1959     if (editInPlace) {
1960       xr          = childStack->getAncestor(frame);
1961       xsheetLevel = childStack->getAncestorCount();
1962     } else
1963       xr = std::make_pair(xsh, frame);
1964 
1965     Stage::VisitArgs args;
1966     args.m_scene       = scene;
1967     args.m_xsh         = xr.first;
1968     args.m_row         = xr.second;
1969     args.m_col         = app->getCurrentColumn()->getColumnIndex();
1970     OnionSkinMask osm  = app->getCurrentOnionSkin()->getOnionSkinMask();
1971     args.m_osm         = &osm;
1972     args.m_camera3d    = true;
1973     args.m_xsheetLevel = xsheetLevel;
1974     args.m_currentFrameId =
1975         app->getCurrentXsheet()
1976             ->getXsheet()
1977             ->getCell(app->getCurrentFrame()->getFrame(), args.m_col)
1978             .getFrameId();
1979     args.m_isGuidedDrawingEnabled = useGuidedDrawing;
1980     args.m_guidedFrontStroke      = guidedFrontStroke;
1981     args.m_guidedBackStroke       = guidedBackStroke;
1982 
1983     // args.m_currentFrameId = app->getCurrentFrame()->getFid();
1984     Stage::visit(painter, args);
1985 
1986     m_minZ = painter.getMinZ();
1987   } else {
1988     // camera 2D (normale)
1989     TDimension viewerSize(width(), height());
1990 
1991     TAffine viewAff = getViewMatrix();
1992 
1993     if (editInPlace) {
1994       TAffine aff;
1995       if (scene->getChildStack()->getAncestorAffine(aff, frame))
1996         viewAff = viewAff * aff.inv();
1997     }
1998 
1999     m_visualSettings.m_showBBox = viewBBoxToggle.getStatus();
2000 
2001     Stage::RasterPainter painter(viewerSize, viewAff, clipRect,
2002                                  m_visualSettings, true);
2003 
2004     // darken blended view mode for viewing the non-cleanuped and stacked
2005     // drawings
2006     painter.setRasterDarkenBlendedView(
2007         Preferences::instance()
2008             ->isShowRasterImagesDarkenBlendedInViewerEnabled());
2009 
2010     TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame();
2011     if (app->getCurrentFrame()->isEditingLevel()) {
2012       Stage::visit(painter, app->getCurrentLevel()->getLevel(),
2013                    app->getCurrentFrame()->getFid(),
2014                    app->getCurrentOnionSkin()->getOnionSkinMask(),
2015                    frameHandle->isPlaying(), useGuidedDrawing, guidedBackStroke,
2016                    guidedFrontStroke);
2017     } else {
2018       std::pair<TXsheet *, int> xr;
2019       int xsheetLevel = 0;
2020       if (editInPlace) {
2021         xr          = scene->getChildStack()->getAncestor(frame);
2022         xsheetLevel = scene->getChildStack()->getAncestorCount();
2023       } else
2024         xr = std::make_pair(xsh, frame);
2025 
2026       Stage::VisitArgs args;
2027       args.m_scene       = scene;
2028       args.m_xsh         = xr.first;
2029       args.m_row         = xr.second;
2030       args.m_col         = app->getCurrentColumn()->getColumnIndex();
2031       OnionSkinMask osm  = app->getCurrentOnionSkin()->getOnionSkinMask();
2032       args.m_osm         = &osm;
2033       args.m_xsheetLevel = xsheetLevel;
2034       args.m_isPlaying   = frameHandle->isPlaying();
2035       if (app->getCurrentLevel() && app->getCurrentLevel()->getLevel() &&
2036           !app->getCurrentLevel()->getLevel()->getSoundLevel())
2037         args.m_currentFrameId =
2038             app->getCurrentXsheet()
2039                 ->getXsheet()
2040                 ->getCell(app->getCurrentFrame()->getFrame(), args.m_col)
2041                 .getFrameId();
2042       args.m_isGuidedDrawingEnabled = useGuidedDrawing;
2043       args.m_guidedFrontStroke      = guidedFrontStroke;
2044       args.m_guidedBackStroke       = guidedBackStroke;
2045 
2046 #if defined(x64)
2047       if (m_stopMotion->m_alwaysUseLiveViewImages &&
2048           m_stopMotion->m_liveViewStatus > 0 &&
2049           frame != m_stopMotion->getXSheetFrameNumber() - 1 &&
2050           m_hasStopMotionImage && !m_stopMotion->m_reviewTimer->isActive()) {
2051         TRaster32P image;
2052         bool hasImage = m_stopMotion->loadLiveViewImage(frame, image);
2053         if (hasImage) {
2054           Stage::Player smPlayer;
2055           double dpiX, dpiY;
2056           m_stopMotionImage->getDpi(dpiX, dpiY);
2057           smPlayer.m_dpiAff    = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
2058           smPlayer.m_opacity   = 255;
2059           smPlayer.m_sl        = m_stopMotion->m_sl;
2060           args.m_liveViewImage = static_cast<TRasterImageP>(image);
2061           args.m_liveViewPlayer = smPlayer;
2062           // painter.onRasterImage(static_cast<TRasterImageP>(image).getPointer(),
2063           //                      smPlayer);
2064         }
2065       }
2066       if (  //! m_stopMotion->m_drawBeneathLevels &&
2067           m_stopMotion->m_liveViewStatus == 2 &&
2068           (  //! frameHandle->isPlaying() ||
2069               frame == m_stopMotion->getXSheetFrameNumber() - 1)) {
2070         if (m_hasStopMotionLineUpImage && m_stopMotion->m_showLineUpImage) {
2071           Stage::Player smPlayer;
2072           double dpiX, dpiY;
2073           m_stopMotionLineUpImage->getDpi(dpiX, dpiY);
2074           smPlayer.m_dpiAff   = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
2075           smPlayer.m_opacity  = 255;
2076           smPlayer.m_sl       = m_stopMotion->m_sl;
2077           args.m_lineupImage  = m_stopMotionLineUpImage;
2078           args.m_lineupPlayer = smPlayer;
2079           // painter.onRasterImage(m_stopMotionLineUpImage.getPointer(),
2080           // smPlayer);
2081         }
2082         if (m_hasStopMotionImage) {
2083           Stage::Player smPlayer;
2084           double dpiX, dpiY;
2085           m_stopMotionImage->getDpi(dpiX, dpiY);
2086           smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
2087           bool hide_opacity = false;
2088 #ifdef WITH_CANON
2089           hide_opacity = m_stopMotion->m_canon->m_zooming ||
2090                          m_stopMotion->m_canon->m_pickLiveViewZoom ||
2091                          !m_hasStopMotionLineUpImage;
2092 #endif
2093           smPlayer.m_opacity =
2094               hide_opacity ? 255.0 : m_stopMotion->getOpacity();
2095           smPlayer.m_sl         = m_stopMotion->m_sl;
2096           args.m_liveViewImage  = m_stopMotionImage;
2097           args.m_liveViewPlayer = smPlayer;
2098           // painter.onRasterImage(m_stopMotionImage.getPointer(), smPlayer);
2099         }
2100       }
2101 #endif  // x64
2102       Stage::visit(painter, args);
2103     }
2104 
2105     assert(glGetError() == 0);
2106     painter.flushRasterImages();
2107 
2108     TXshSimpleLevel::m_fillFullColorRaster = fillFullColorRaster;
2109 
2110     assert(glGetError() == 0);
2111     if (m_viewMode != LEVEL_VIEWMODE)
2112       drawSpline(getViewMatrix(), clipRect,
2113                  m_referenceMode == CAMERA3D_REFERENCE, m_pixelSize);
2114     assert(glGetError() == 0);
2115 
2116     // gather animated guide strokes' bounding boxes
2117     // it is used for updating viewer next time
2118     std::vector<TStroke *> guidedStrokes = painter.getGuidedStrokes();
2119     for (auto itr = guidedStrokes.begin(); itr != guidedStrokes.end(); ++itr) {
2120       m_guidedDrawingBBox += (*itr)->getBBox();
2121     }
2122   }
2123 }
2124 
2125 //------------------------------------------------------------------------------
2126 
mult3DMatrix()2127 void SceneViewer::mult3DMatrix() {
2128   glTranslated(m_pan3D.x, m_pan3D.y, 0);
2129   glScaled(m_zoomScale3D, m_zoomScale3D, 1);
2130   glRotated(m_theta3D, 1, 0, 0);
2131   glRotated(m_phi3D, 0, 1, 0);
2132 }
2133 
2134 //-----------------------------------------------------------------------------
2135 
projectToZ(const TPointD & delta)2136 double SceneViewer::projectToZ(const TPointD &delta) {
2137   glPushMatrix();
2138   mult3DMatrix();
2139   GLint viewport[4];
2140   double modelview[16], projection[16];
2141   glGetIntegerv(GL_VIEWPORT, viewport);
2142   for (int i = 0; i < 16; i++)
2143     projection[i] = (double)m_projectionMatrix.constData()[i];
2144   glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
2145 
2146   double ax, ay, az, bx, by, bz;
2147   gluProject(0, 0, 0, modelview, projection, viewport, &ax, &ay, &az);
2148   gluProject(0, 0, 1, modelview, projection, viewport, &bx, &by, &bz);
2149 
2150   glPopMatrix();
2151   TPointD zdir(bx - ax, by - ay);
2152   double zdirLength2 = norm2(zdir);
2153   if (zdirLength2 > 0.0) {
2154     double dz = (delta * zdir) / zdirLength2;
2155     return dz;
2156   } else
2157     return 0.0;
2158 }
2159 
2160 //-----------------------------------------------------------------------------
2161 
getActualClipRect(const TAffine & aff)2162 TRect SceneViewer::getActualClipRect(const TAffine &aff) {
2163   TDimensionD viewerSize(width(), height());
2164   TRectD clipRect(viewerSize);
2165 
2166   if (is3DView()) {
2167     TPointD p00 = winToWorld(clipRect.getP00());
2168     TPointD p01 = winToWorld(clipRect.getP01());
2169     TPointD p10 = winToWorld(clipRect.getP10());
2170     TPointD p11 = winToWorld(clipRect.getP11());
2171     clipRect = TRectD(TPointD(std::min(p00.x, p01.x), std::min(p00.y, p10.y)),
2172                       TPointD(std::max(p11.x, p10.x), std::max(p11.y, p01.y)));
2173   }
2174   // this condition will catch both cases of m_clipRect == empty and
2175   // m_clipRect == InvalidateAllRect
2176   else if (m_clipRect.isEmpty())
2177     clipRect -= TPointD(viewerSize.lx / 2, viewerSize.ly / 2);
2178   else {
2179     TRectD app = aff * (m_clipRect.enlarge(3));
2180     clipRect =
2181         TRectD(tceil(app.x0), tceil(app.y0), tfloor(app.x1), tfloor(app.y1));
2182   }
2183 
2184   return convert(clipRect);
2185 }
2186 
2187 //-----------------------------------------------------------------------------
2188 
getViewMatrix() const2189 TAffine SceneViewer::getViewMatrix() const {
2190   int viewMode = TApp::instance()->getCurrentFrame()->isEditingLevel()
2191                      ? LEVEL_VIEWMODE
2192                      : SCENE_VIEWMODE;
2193   if (is3DView()) return TAffine();
2194   if (m_referenceMode == CAMERA_REFERENCE) {
2195     int frame    = TApp::instance()->getCurrentFrame()->getFrame();
2196     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2197     TAffine aff  = xsh->getCameraAff(frame);
2198     return m_viewAff[viewMode] * aff.inv();
2199   } else
2200     return m_viewAff[viewMode];
2201 }
2202 
2203 //-----------------------------------------------------------------------------
2204 
getSceneMatrix() const2205 TAffine SceneViewer::getSceneMatrix() const {
2206   int viewMode = TApp::instance()->getCurrentFrame()->isEditingLevel()
2207                      ? LEVEL_VIEWMODE
2208                      : SCENE_VIEWMODE;
2209   if (is3DView()) return TAffine();
2210   return m_viewAff[viewMode];
2211 }
2212 
2213 //-----------------------------------------------------------------------------
2214 
setViewMatrix(const TAffine & aff,int viewMode)2215 void SceneViewer::setViewMatrix(const TAffine &aff, int viewMode) {
2216   m_viewAff[viewMode] = aff;
2217   // In case the previewer is on, request a delayed update
2218   if (m_previewMode != NO_PREVIEW) requestTimedRefresh();
2219 }
2220 
2221 //-----------------------------------------------------------------------------
2222 
is3DView() const2223 bool SceneViewer::is3DView() const {
2224   bool isCameraTest = CameraTestCheck::instance()->isEnabled();
2225   return (m_referenceMode == CAMERA3D_REFERENCE && !isCameraTest);
2226 }
2227 
2228 //-----------------------------------------------------------------------------
2229 
invalidateAll()2230 void SceneViewer::invalidateAll() {
2231   m_clipRect = InvalidateAllRect;
2232   update();
2233   if (m_vRuler) m_vRuler->update();
2234   if (m_hRuler) m_hRuler->update();
2235 }
2236 
2237 //-----------------------------------------------------------------------------
2238 /*! Pan the viewer by using "navigator" (red rectangle) in level strip
2239  */
navigatorPan(const QPoint & delta)2240 void SceneViewer::navigatorPan(const QPoint &delta) {
2241   panQt(delta);
2242   m_pos += delta;
2243 }
2244 
2245 //-----------------------------------------------------------------------------
2246 
GLInvalidateAll()2247 void SceneViewer::GLInvalidateAll() {
2248   m_clipRect = InvalidateAllRect;
2249   update();
2250   if (m_vRuler) m_vRuler->update();
2251   if (m_hRuler) m_hRuler->update();
2252 }
2253 
2254 //-----------------------------------------------------------------------------
2255 
GLInvalidateRect(const TRectD & rect)2256 void SceneViewer::GLInvalidateRect(const TRectD &rect) {
2257   // in case that GLInvalidateAll is called just before coming here,
2258   // ignore the clip rect and refresh entire viewer
2259   if (m_clipRect == InvalidateAllRect)
2260     return;
2261   else if (rect.isEmpty())
2262     m_clipRect = InvalidateAllRect;
2263   else {
2264     m_clipRect += rect;
2265     if (!m_guidedDrawingBBox.isEmpty()) {
2266       TTool *tool         = TApp::instance()->getCurrentTool()->getTool();
2267       TPointD topLeft     = tool->getMatrix() * m_guidedDrawingBBox.getP00();
2268       TPointD bottomRight = tool->getMatrix() * m_guidedDrawingBBox.getP11();
2269       m_clipRect += TRectD(topLeft, bottomRight);
2270     }
2271   }
2272   update();
2273   if (m_vRuler) m_vRuler->update();
2274   if (m_hRuler) m_hRuler->update();
2275   m_guidedDrawingBBox.empty();
2276 }
2277 //-----------------------------------------------------------------------------
2278 
2279 // delta.x: right panning, pixel; delta.y: down panning, pixel
panQt(const QPointF & delta)2280 void SceneViewer::panQt(const QPointF &delta) {
2281   if (delta == QPointF()) return;
2282   if (is3DView())
2283     m_pan3D += TPointD(delta.x(), -delta.y());
2284   else {
2285     // TAffine &viewAff = m_viewAff[m_viewMode];
2286     // viewAff = TTranslation(delta.x(), -delta.y()) * viewAff;
2287     setViewMatrix(TTranslation(delta.x(), -delta.y()) * m_viewAff[m_viewMode],
2288                   m_viewMode);
2289   }
2290   invalidateAll();
2291   emit refreshNavi();
2292 }
2293 
2294 //-----------------------------------------------------------------------------
2295 
zoomQt(bool forward,bool reset)2296 void SceneViewer::zoomQt(bool forward, bool reset) {
2297   TPointD delta(m_lastMousePos.x() - width() / 2,
2298                 -m_lastMousePos.y() + height() / 2);
2299 
2300   if (is3DView()) {
2301     if (reset || ((m_zoomScale3D < 500 || !forward) &&
2302                   (m_zoomScale3D > 0.01 || forward))) {
2303       double oldZoomScale = m_zoomScale3D;
2304       m_zoomScale3D =
2305           reset ? 1
2306                 : ImageUtils::getQuantizedZoomFactor(m_zoomScale3D, forward);
2307 
2308       m_pan3D = -(m_zoomScale3D / oldZoomScale) * -m_pan3D;
2309     }
2310   } else {
2311     // a factor for getting pixel-based zoom ratio
2312     double dpiFactor = getDpiFactor();
2313     // when showing the viewer with full-screen mode,
2314     // add a zoom factor which can show image fitting with the screen size
2315     double zoomScaleFittingWithScreen = 0.0f;
2316     if (dpiFactor != 1.0) {
2317       // check if the viewer is in full screen mode
2318       ImageUtils::FullScreenWidget *fsWidget =
2319           dynamic_cast<ImageUtils::FullScreenWidget *>(parentWidget());
2320       if (fsWidget && (fsWidget->windowState() & Qt::WindowFullScreen) != 0)
2321         zoomScaleFittingWithScreen = getZoomScaleFittingWithScreen();
2322     }
2323 
2324     int i;
2325 
2326     for (i = 0; i < 2; i++) {
2327       TAffine &viewAff = m_viewAff[i];
2328       if (m_isFlippedX) viewAff = viewAff * TScale(-1, 1);
2329       if (m_isFlippedX) viewAff = viewAff * TScale(1, -1);
2330       double scale2 = std::abs(viewAff.det());
2331       if (m_isFlippedX) viewAff = viewAff * TScale(-1, 1);
2332       if (m_isFlippedX) viewAff = viewAff * TScale(1, -1);
2333       if (reset || ((scale2 < 100000 || !forward) &&
2334                     (scale2 > 0.001 * 0.05 || forward))) {
2335         double oldZoomScale = sqrt(scale2) * dpiFactor;
2336         double zoomScale =
2337             reset ? 1
2338                   : ImageUtils::getQuantizedZoomFactor(oldZoomScale, forward);
2339 
2340         // threshold value -0.001 is intended to absorb the error of calculation
2341         if ((oldZoomScale - zoomScaleFittingWithScreen) *
2342                 (zoomScale - zoomScaleFittingWithScreen) <
2343             -0.001)
2344           zoomScale = zoomScaleFittingWithScreen;
2345         // zoom center = viewer center
2346         if (Preferences::instance()->getViewerZoomCenter())
2347           setViewMatrix(TScale(zoomScale / oldZoomScale) * viewAff, i);
2348         // zoom center = mouse pos
2349         else
2350           setViewMatrix(TTranslation(delta) * TScale(zoomScale / oldZoomScale) *
2351                             TTranslation(-delta) * viewAff,
2352                         i);
2353       }
2354     }
2355   }
2356 
2357   GLInvalidateAll();
2358   emit onZoomChanged();
2359 }
2360 
2361 //-----------------------------------------------------------------------------
2362 /*! a factor for getting pixel-based zoom ratio
2363  */
getDpiFactor()2364 double SceneViewer::getDpiFactor() {
2365   // When the current unit is "pixels", always use a standard dpi
2366   double cameraDpi = TApp::instance()
2367                          ->getCurrentScene()
2368                          ->getScene()
2369                          ->getCurrentCamera()
2370                          ->getDpi()
2371                          .x;
2372   if (Preferences::instance()->getPixelsOnly()) {
2373     return Stage::inch / Stage::standardDpi;
2374   }
2375 
2376   // When preview mode, use a camera DPI
2377   else if (isPreviewEnabled()) {
2378     return Stage::inch / cameraDpi;
2379   }
2380   // When level editing mode, use an image DPI
2381   else if (TApp::instance()->getCurrentFrame()->isEditingLevel()) {
2382     TXshSimpleLevel *sl;
2383     sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
2384     if (!sl) return Stage::inch / cameraDpi;
2385     if (sl->getType() == PLI_XSHLEVEL) return Stage::inch / cameraDpi;
2386     if (sl->getImageDpi() != TPointD())
2387       return Stage::inch / sl->getImageDpi().x;
2388     if (sl->getDpi() != TPointD()) return Stage::inch / sl->getDpi().x;
2389     // no valid dpi, use camera dpi
2390     return Stage::inch / cameraDpi;
2391   }
2392   // When the special case in the scene editing mode:
2393   // If the option "ActualPixelViewOnSceneEditingMode" is ON,
2394   // use  current level's DPI set in the level settings.
2395   else if (Preferences::instance()
2396                ->isActualPixelViewOnSceneEditingModeEnabled()) {
2397     if (CleanupPreviewCheck::instance()->isEnabled() ||
2398         CameraTestCheck::instance()->isEnabled()) {
2399       double cleanupCameraDpi = TApp::instance()
2400                                     ->getCurrentScene()
2401                                     ->getScene()
2402                                     ->getProperties()
2403                                     ->getCleanupParameters()
2404                                     ->m_camera.getDpi()
2405                                     .x;
2406       return Stage::inch / cleanupCameraDpi;
2407     } else {
2408       TXshSimpleLevel *sl;
2409       sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
2410       if (!sl) return Stage::inch / cameraDpi;
2411       if (sl->getType() == PLI_XSHLEVEL) return Stage::inch / cameraDpi;
2412       if (sl->getDpi() == TPointD()) return Stage::inch / cameraDpi;
2413       // use default value for the argument of getDpi() (=TFrameId::NO_FRAME)
2414       // so that the dpi of the first frame in the level will be returned.
2415       return Stage::inch / sl->getDpi().x;
2416     }
2417   }
2418   // When the scene editing mode without any option, use the camera dpi
2419   else {
2420     return Stage::inch / cameraDpi;
2421   }
2422 }
2423 
2424 //-----------------------------------------------------------------------------
2425 /*! when showing the viewer with full-screen mode,
2426         add a zoom factor which can show image fitting with the screen size
2427 */
2428 
getZoomScaleFittingWithScreen()2429 double SceneViewer::getZoomScaleFittingWithScreen() {
2430   TDimension imgSize;
2431 
2432   // get the image size to be rendered
2433 
2434   if (isPreviewEnabled())
2435     imgSize = TApp::instance()
2436                   ->getCurrentScene()
2437                   ->getScene()
2438                   ->getCurrentCamera()
2439                   ->getRes();
2440   else if (TApp::instance()->getCurrentFrame()->isEditingLevel()) {
2441     TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
2442     if (!sl || sl->getType() == PLI_XSHLEVEL || sl->getImageDpi() == TPointD())
2443       return 0.0;
2444 
2445     imgSize = sl->getResolution();
2446   } else if (Preferences::instance()
2447                  ->isActualPixelViewOnSceneEditingModeEnabled() &&
2448              !CleanupPreviewCheck::instance()->isEnabled() &&
2449              !CameraTestCheck::instance()->isEnabled()) {
2450     TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
2451     if (!sl || sl->getType() == PLI_XSHLEVEL || sl->getDpi() == TPointD())
2452       return 0.0;
2453     imgSize = sl->getResolution();
2454 
2455   } else  // SCENE_VIEWMODE
2456     return 0.0;
2457 
2458   // add small margin on the edge of the image
2459   int margin = 20;
2460   // get the desktop resolution
2461   QRect rec = QApplication::desktop()->screenGeometry();
2462 
2463   // fit to either direction
2464   int moni_x = rec.width() - (margin * 2);
2465   int moni_y = rec.height() - (margin * 2);
2466   return std::min((double)moni_x / (double)imgSize.lx,
2467                   (double)moni_y / (double)imgSize.ly);
2468 }
2469 
2470 //-----------------------------------------------------------------------------
2471 
2472 // center: window coordinate, pixels, topleft origin
zoomQt(const QPoint & center,double factor)2473 void SceneViewer::zoomQt(const QPoint &center, double factor) {
2474   if (factor == 1.0) return;
2475   TPointD delta(center.x() - width() / 2, -center.y() + height() / 2);
2476   double oldZoomScale = m_zoomScale3D;
2477 
2478   if (is3DView()) {
2479     if ((m_zoomScale3D < 500 || factor < 1) &&
2480         (m_zoomScale3D > 0.01 || factor > 1)) {
2481       m_zoomScale3D *= factor;
2482       m_pan3D = -(m_zoomScale3D / oldZoomScale) * (delta - m_pan3D) + delta;
2483     }
2484   } else {
2485     int i;
2486     for (i = 0; i < 2; i++) {
2487       TAffine &viewAff = m_viewAff[i];
2488       double scale2    = fabs(viewAff.det());
2489       if ((scale2 < 100000 || factor < 1) &&
2490           (scale2 > 0.001 * 0.05 || factor > 1)) {
2491         if (i == m_viewMode) {
2492           // viewAff = TTranslation(delta) * TScale(factor) *
2493           // TTranslation(-delta) * viewAff;
2494           setViewMatrix(TTranslation(delta) * TScale(factor) *
2495                             TTranslation(-delta) * viewAff,
2496                         i);
2497         } else {
2498           // viewAff = TScale(factor) * viewAff;
2499           setViewMatrix(TScale(factor) * viewAff, i);
2500         }
2501       }
2502     }
2503   }
2504 
2505   GLInvalidateAll();
2506   emit onZoomChanged();
2507 }
2508 
zoom(const TPointD & center,double factor)2509 void SceneViewer::zoom(const TPointD &center, double factor) {
2510   zoomQt(QPoint(center.x, height() - center.y), factor);
2511 }
2512 
2513 //-----------------------------------------------------------------------------
2514 
flipX()2515 void SceneViewer::flipX() {
2516   double flipAngle0 = (m_rotationAngle[0] * -1) * 2;
2517   double flipAngle1 = (m_rotationAngle[1] * -1) * 2;
2518   m_rotationAngle[0] += flipAngle0;
2519   m_rotationAngle[1] += flipAngle1;
2520   if (m_isFlippedX != m_isFlippedY) {
2521     flipAngle0 = -flipAngle0;
2522     flipAngle1 = -flipAngle1;
2523   }
2524   m_viewAff[0] = m_viewAff[0] * TRotation(flipAngle0) * TScale(-1, 1);
2525   m_viewAff[1] = m_viewAff[1] * TRotation(flipAngle1) * TScale(-1, 1);
2526   m_viewAff[0].a13 *= -1;
2527   m_viewAff[1].a13 *= -1;
2528   m_isFlippedX = !m_isFlippedX;
2529   invalidateAll();
2530   emit onZoomChanged();
2531   emit onFlipHChanged(m_isFlippedX);
2532 }
2533 
2534 //-----------------------------------------------------------------------------
2535 
flipY()2536 void SceneViewer::flipY() {
2537   double flipAngle0 = (m_rotationAngle[0] * -1) * 2;
2538   double flipAngle1 = (m_rotationAngle[1] * -1) * 2;
2539   m_rotationAngle[0] += flipAngle0;
2540   m_rotationAngle[1] += flipAngle1;
2541   if (m_isFlippedX != m_isFlippedY) {
2542     flipAngle0 = -flipAngle0;
2543     flipAngle1 = -flipAngle1;
2544   }
2545   m_viewAff[0] = m_viewAff[0] * TRotation(flipAngle0) * TScale(1, -1);
2546   m_viewAff[1] = m_viewAff[1] * TRotation(flipAngle1) * TScale(1, -1);
2547   m_viewAff[0].a23 *= -1;
2548   m_viewAff[1].a23 *= -1;
2549   m_isFlippedY = !m_isFlippedY;
2550   invalidateAll();
2551   emit onZoomChanged();
2552   emit onFlipVChanged(m_isFlippedY);
2553 }
2554 
2555 //-----------------------------------------------------------------------------
2556 
zoomIn()2557 void SceneViewer::zoomIn() {
2558   m_lastMousePos = rect().center();
2559   zoomQt(true, false);
2560 }
2561 
2562 //-----------------------------------------------------------------------------
2563 
zoomOut()2564 void SceneViewer::zoomOut() {
2565   m_lastMousePos = rect().center();
2566   zoomQt(false, false);
2567 }
2568 
2569 //-----------------------------------------------------------------------------
2570 
rotate(const TPointD & center,double angle)2571 void SceneViewer::rotate(const TPointD &center, double angle) {
2572   if (angle == 0) return;
2573   if (m_isFlippedX != m_isFlippedY) angle = -angle;
2574   m_rotationAngle[m_viewMode] += angle;
2575   TPointD realCenter = m_viewAff[m_viewMode] * center;
2576   setViewMatrix(TRotation(realCenter, angle) * m_viewAff[m_viewMode],
2577                 m_viewMode);
2578   invalidateAll();
2579 }
2580 
2581 //-----------------------------------------------------------------------------
2582 
rotate3D(double dPhi,double dTheta)2583 void SceneViewer::rotate3D(double dPhi, double dTheta) {
2584   if (dPhi == 0 && dTheta == 0) return;
2585   m_phi3D   = (float)tcrop(m_phi3D + dPhi, -90.0, 90.0);
2586   m_theta3D = (float)tcrop(m_theta3D + dTheta, 0.0, 90.0);
2587   invalidateAll();
2588 }
2589 
2590 //-----------------------------------------------------------------------------
2591 
regeneratePreview()2592 void SceneViewer::regeneratePreview() {
2593   Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->clear();
2594   update();
2595 }
2596 
2597 //-----------------------------------------------------------------------------
2598 
regeneratePreviewFrame()2599 void SceneViewer::regeneratePreviewFrame() {
2600   Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
2601       ->clear(TApp::instance()->getCurrentFrame()->getFrame());
2602 
2603   update();
2604 }
2605 
2606 //-----------------------------------------------------------------------------
2607 
swapCompared()2608 void SceneViewer::swapCompared() {
2609   m_compareSettings.m_swapCompared = !m_compareSettings.m_swapCompared;
2610   update();
2611 }
2612 
2613 //-----------------------------------------------------------------------------
2614 
fitToCamera()2615 void SceneViewer::fitToCamera() {
2616   // Reset the view scale to 1:1.
2617   bool tempIsFlippedX = m_isFlippedX;
2618   bool tempIsFlippedY = m_isFlippedY;
2619   resetSceneViewer();
2620 
2621   m_isFlippedX = tempIsFlippedX;
2622   m_isFlippedY = tempIsFlippedY;
2623 
2624   TXsheet *xsh            = TApp::instance()->getCurrentXsheet()->getXsheet();
2625   int frame               = TApp::instance()->getCurrentFrame()->getFrame();
2626   TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
2627   TStageObject *camera    = xsh->getStageObject(cameraId);
2628   TAffine cameraPlacement = camera->getPlacement(frame);
2629   double cameraZ          = camera->getZ(frame);
2630   TAffine cameraAff =
2631       getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
2632 
2633   QRect viewRect    = rect();
2634   TRectD cameraRect = ViewerDraw::getCameraRect();
2635   TPointD P00       = cameraAff * cameraRect.getP00();
2636   TPointD P10       = cameraAff * cameraRect.getP10();
2637   TPointD P01       = cameraAff * cameraRect.getP01();
2638   TPointD P11       = cameraAff * cameraRect.getP11();
2639   TPointD p0        = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}),
2640                        std::min({P00.y, P01.y, P10.y, P11.y}));
2641   TPointD p1        = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}),
2642                        std::max({P00.y, P01.y, P10.y, P11.y}));
2643   cameraRect        = TRectD(p0.x, p0.y, p1.x, p1.y);
2644 
2645   // Pan
2646   if (!is3DView()) {
2647     TPointD cameraCenter = (cameraRect.getP00() + cameraRect.getP11()) * 0.5;
2648     panQt(QPoint(-cameraCenter.x, cameraCenter.y));
2649   }
2650 
2651   double xratio = (double)viewRect.width() / cameraRect.getLx();
2652   double yratio = (double)viewRect.height() / cameraRect.getLy();
2653   double ratio  = std::min(xratio, yratio);
2654   if (ratio == 0.0) return;
2655   if (tempIsFlippedX)
2656     setViewMatrix(TScale(-1, 1) * m_viewAff[m_viewMode], m_viewMode);
2657   if (tempIsFlippedY)
2658     setViewMatrix(TScale(1, -1) * m_viewAff[m_viewMode], m_viewMode);
2659   // Scale and center on the center of \a rect.
2660   QPoint c = viewRect.center();
2661   zoom(TPointD(c.x(), c.y()), ratio);
2662   emit onFlipHChanged(m_isFlippedX);
2663   emit onFlipVChanged(m_isFlippedY);
2664 }
2665 
2666 //-----------------------------------------------------------------------------
2667 
fitToCameraOutline()2668 void SceneViewer::fitToCameraOutline() {
2669   TXsheet *xsh            = TApp::instance()->getCurrentXsheet()->getXsheet();
2670   int frame               = TApp::instance()->getCurrentFrame()->getFrame();
2671   TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
2672   TStageObject *camera    = xsh->getStageObject(cameraId);
2673   TAffine cameraPlacement = camera->getPlacement(frame);
2674   double cameraZ          = camera->getZ(frame);
2675   TAffine cameraAff =
2676       getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
2677 
2678   QRect viewRect    = rect();
2679   TRectD cameraRect = ViewerDraw::getCameraRect();
2680   TPointD P00       = cameraAff * cameraRect.getP00();
2681   TPointD P10       = cameraAff * cameraRect.getP10();
2682   TPointD P01       = cameraAff * cameraRect.getP01();
2683   TPointD P11       = cameraAff * cameraRect.getP11();
2684   TPointD p0        = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}),
2685                        std::min({P00.y, P01.y, P10.y, P11.y}));
2686   TPointD p1        = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}),
2687                        std::max({P00.y, P01.y, P10.y, P11.y}));
2688   cameraRect        = TRectD(p0.x, p0.y, p1.x, p1.y);
2689 
2690   // Pan
2691   if (!is3DView()) {
2692     TPointD cameraCenter = (cameraRect.getP00() + cameraRect.getP11()) * 0.5;
2693     panQt(QPoint(-cameraCenter.x, cameraCenter.y));
2694   }
2695 
2696   double xratio = (double)viewRect.width() / cameraRect.getLx();
2697   double yratio = (double)viewRect.height() / cameraRect.getLy();
2698   double ratio  = std::min(xratio, yratio);
2699   if (ratio == 0.0) return;
2700 
2701   // Scale and center on the center of \a rect.
2702   QPoint c = viewRect.center();
2703   zoom(TPointD(c.x(), c.y()), ratio);
2704   zoom(TPointD(c.x(), c.y()), 0.95);
2705 }
2706 
2707 //-----------------------------------------------------------------------------
2708 
resetSceneViewer()2709 void SceneViewer::resetSceneViewer() {
2710   m_visualSettings.m_sceneProperties =
2711       TApp::instance()->getCurrentScene()->getScene()->getProperties();
2712 
2713   for (int i = 0; i < m_viewAff.size(); ++i) {
2714     setViewMatrix(getNormalZoomScale(), i);
2715     m_rotationAngle[i] = 0.0;
2716   }
2717 
2718   m_pos         = QPoint(0, 0);
2719   m_pan3D       = TPointD(0, 0);
2720   m_zoomScale3D = 0.1;
2721   m_theta3D     = 20;
2722   m_phi3D       = 30;
2723   m_isFlippedX  = false;
2724   m_isFlippedY  = false;
2725   fitToCameraOutline();
2726   emit onZoomChanged();
2727   emit onFlipHChanged(m_isFlippedX);
2728   emit onFlipVChanged(m_isFlippedY);
2729   invalidateAll();
2730 }
2731 
2732 //-----------------------------------------------------------------------------
2733 
resetZoom()2734 void SceneViewer::resetZoom() {
2735   TPointD realCenter(m_viewAff[m_viewMode].a13, m_viewAff[m_viewMode].a23);
2736   TAffine aff =
2737       getNormalZoomScale() * TRotation(realCenter, m_rotationAngle[m_viewMode]);
2738   aff.a13 = realCenter.x;
2739   aff.a23 = realCenter.y;
2740   if (m_isFlippedX) aff = aff * TScale(-1, 1);
2741   if (m_isFlippedY) aff = aff * TScale(1, -1);
2742   setViewMatrix(aff, m_viewMode);
2743   invalidateAll();
2744   emit onZoomChanged();
2745 }
2746 
2747 //-----------------------------------------------------------------------------
2748 
resetRotation()2749 void SceneViewer::resetRotation() {
2750   double reverseRotatation = m_rotationAngle[m_viewMode] * -1;
2751   if (m_isFlippedX) reverseRotatation *= -1;
2752   if (m_isFlippedY) reverseRotatation *= -1;
2753   TTool *tool    = TApp::instance()->getCurrentTool()->getTool();
2754   TPointD center = m_viewAff[m_viewMode].inv() * TPointD(0, 0);
2755   if (tool->getName() == "T_Rotate" &&
2756       tool->getProperties(0)
2757               ->getProperty("Rotate On Camera Center")
2758               ->getValueAsString() == "1")
2759     center = TPointD(0, 0);
2760   rotate(center, reverseRotatation);
2761 }
2762 
2763 //-----------------------------------------------------------------------------
2764 
resetPosition()2765 void SceneViewer::resetPosition() {
2766   m_viewAff[m_viewMode].a13 = 0.0;
2767   m_viewAff[m_viewMode].a23 = 0.0;
2768   invalidateAll();
2769 }
2770 
2771 //-----------------------------------------------------------------------------
2772 
setActualPixelSize()2773 void SceneViewer::setActualPixelSize() {
2774   TApp *app           = TApp::instance();
2775   TXshLevel *l        = app->getCurrentLevel()->getLevel();
2776   TXshSimpleLevel *sl = l ? l->getSimpleLevel() : 0;
2777   if (!sl) return;
2778 
2779   TFrameId fid(app->getCurrentFrame()->getFid());
2780 
2781   TPointD dpi;
2782   if (CleanupPreviewCheck::instance()->isEnabled()) {
2783     // The previewed cleanup image has no image infos yet - retrieve the dpi
2784     // through
2785     // the cleanup camera data
2786     CleanupParameters *cleanupParams = app->getCurrentScene()
2787                                            ->getScene()
2788                                            ->getProperties()
2789                                            ->getCleanupParameters();
2790 
2791     TDimension dim(0, 0);
2792     cleanupParams->getOutputImageInfo(dim, dpi.x, dpi.y);
2793   } else
2794     dpi = sl->getDpi(fid);
2795 
2796   const double inch = Stage::inch;
2797   TAffine tempAff   = getNormalZoomScale();
2798   if (m_isFlippedX) tempAff = tempAff * TScale(-1, 1);
2799   if (m_isFlippedY) tempAff = tempAff * TScale(1, -1);
2800   TPointD tempScale = dpi;
2801   if (m_isFlippedX) tempScale.x = -tempScale.x;
2802   if (m_isFlippedY) tempScale.y = -tempScale.y;
2803   for (int i = 0; i < m_viewAff.size(); ++i)
2804     setViewMatrix(dpi == TPointD(0, 0)
2805                       ? tempAff
2806                       : TScale(tempScale.x / inch, tempScale.y / inch),
2807                   i);
2808 
2809   m_pos         = QPoint(0, 0);
2810   m_pan3D       = TPointD(0, 0);
2811   m_zoomScale3D = 0.1;
2812   m_theta3D     = 20;
2813   m_phi3D       = 30;
2814   emit onZoomChanged();
2815 
2816   invalidateAll();
2817 }
2818 
2819 //-----------------------------------------------------------------------------
2820 
onLevelChanged()2821 void SceneViewer::onLevelChanged() {
2822   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
2823   if (tool) {
2824     TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
2825     if (level && level->getSimpleLevel())
2826       m_dpiScale =
2827           getCurrentDpiScale(level->getSimpleLevel(), tool->getCurrentFid());
2828     else
2829       m_dpiScale = TPointD(1, 1);
2830   }
2831 }
2832 //-----------------------------------------------------------------------------
2833 /*! when level is switched, update m_dpiScale in order to show white background
2834  * for Ink&Paint work properly
2835  */
onLevelSwitched()2836 void SceneViewer::onLevelSwitched() {
2837   invalidateToolStatus();
2838   TApp *app        = TApp::instance();
2839   TTool *tool      = app->getCurrentTool()->getTool();
2840   TXshLevel *level = app->getCurrentLevel()->getLevel();
2841   if (level && level->getSimpleLevel())
2842     m_dpiScale =
2843         getCurrentDpiScale(level->getSimpleLevel(), tool->getCurrentFid());
2844   else
2845     m_dpiScale = TPointD(1, 1);
2846 }
2847 
2848 //-----------------------------------------------------------------------------
2849 
onXsheetChanged()2850 void SceneViewer::onXsheetChanged() {
2851   m_forceGlFlush = true;
2852   TTool *tool    = TApp::instance()->getCurrentTool()->getTool();
2853   if (tool && tool->isEnabled()) tool->updateMatrix();
2854   onLevelChanged();
2855   GLInvalidateAll();
2856 }
2857 
2858 //-----------------------------------------------------------------------------
2859 
onObjectSwitched()2860 void SceneViewer::onObjectSwitched() {
2861   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
2862   if (tool && tool->isEnabled()) tool->updateMatrix();
2863   onLevelChanged();
2864   GLInvalidateAll();
2865 }
2866 
2867 //-----------------------------------------------------------------------------
2868 
onSceneChanged()2869 void SceneViewer::onSceneChanged() {
2870   onLevelChanged();
2871   GLInvalidateAll();
2872 }
2873 
2874 //-----------------------------------------------------------------------------
2875 
onFrameSwitched()2876 void SceneViewer::onFrameSwitched() {
2877   invalidateToolStatus();
2878 
2879   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
2880   if (tool && tool->isEnabled()) {
2881     tool->setViewer(this);
2882     tool->updateMatrix();
2883     tool->onEnter();
2884   }
2885 
2886   GLInvalidateAll();
2887 }
2888 
2889 //-----------------------------------------------------------------------------
2890 /*! when tool options are changed, update tooltip immediately
2891  */
onToolChanged()2892 void SceneViewer::onToolChanged() {
2893   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
2894   if (tool) setToolCursor(this, tool->getCursorId());
2895   GLInvalidateAll();
2896 }
2897 
2898 //-----------------------------------------------------------------------------
2899 
pick(const TPointD & point)2900 int SceneViewer::pick(const TPointD &point) {
2901   // pick is typically called in a mouse event handler.
2902   // QGLWidget::makeCurrent() is not automatically called in these events.
2903   // (to exploit the bug: open the FxEditor preview and then select the edit
2904   // tool)
2905   m_isPicking = true;
2906   makeCurrent();
2907   assert(glGetError() == GL_NO_ERROR);
2908   GLint viewport[4];
2909   glGetIntegerv(GL_VIEWPORT, viewport);
2910   std::array<GLuint, 512> selectBuffer;
2911   glSelectBuffer(selectBuffer.size(), selectBuffer.data());
2912   glRenderMode(GL_SELECT);
2913 
2914   // definisco la matrice di proiezione
2915   glMatrixMode(GL_PROJECTION);
2916   GLdouble mat[16];
2917   glGetDoublev(GL_PROJECTION_MATRIX, mat);
2918   glPushMatrix();
2919   glLoadIdentity();
2920   gluPickMatrix(point.x, point.y, 5, 5, viewport);
2921   glMultMatrixd(mat);
2922   assert(glGetError() == GL_NO_ERROR);
2923 
2924   // disegno la scena
2925   glMatrixMode(GL_MODELVIEW);
2926   glPushMatrix();
2927   glInitNames();
2928   assert(glGetError() == GL_NO_ERROR);
2929 
2930   // WARNING: We have to draw the scene in CAMERASTAND mode. Observe that the
2931   // preview mode may
2932   // invoke event processing - therefore triggering other pick events while in
2933   // GL_SELECT
2934   // render mode...
2935   int previewMode = m_previewMode;
2936   m_previewMode   = NO_PREVIEW;
2937 
2938   //   OPTIMIZATION / QUICK FIX
2939   // A 1x1 clipping rect around the picked pos can very well be used instead of
2940   // redrawing
2941   // the *entire viewer*.
2942   // This is needed especially since some graphic cards (all NVidias we have
2943   // tested) are
2944   // very slow otherwise. This could be due to this particular rendering mode -
2945   // or because
2946   // we could be painting OUTSIDE a paintEvent()...
2947 
2948   TRectD oldClipRect(m_clipRect);
2949   m_clipRect = TRectD(point.x, point.y, point.x + 1, point.y + 1);
2950 
2951   paintGL();  // draw identifiable objects
2952 
2953   m_clipRect = oldClipRect;
2954 
2955   m_previewMode = previewMode;
2956 
2957   assert(glGetError() == GL_NO_ERROR);
2958   glPopMatrix();
2959 
2960   // rimetto a posto la matrice di proiezione
2961   glMatrixMode(GL_PROJECTION);
2962   glPopMatrix();
2963   glMatrixMode(GL_MODELVIEW);
2964 
2965   assert(glGetError() == GL_NO_ERROR);
2966 
2967   // conto gli hits
2968   int ret      = -1;
2969   int hitCount = glRenderMode(GL_RENDER);
2970   GLuint *p    = selectBuffer.data();
2971   for (int i = 0; i < hitCount; ++i) {
2972     GLuint nameCount = *p++;
2973     GLuint zmin      = *p++;
2974     GLuint zmax      = *p++;
2975     if (nameCount > 0) {
2976       GLuint name = *p;
2977       ret         = name;  // items.push_back(PickItem(name, zmin, zmax));
2978     }
2979     p += nameCount;
2980   }
2981   assert(glGetError() == GL_NO_ERROR);
2982   m_isPicking = false;
2983   return ret;
2984 }
2985 
2986 //-----------------------------------------------------------------------------
2987 
posToColumnIndex(const TPointD & p,double distance,bool includeInvisible) const2988 int SceneViewer::posToColumnIndex(const TPointD &p, double distance,
2989                                   bool includeInvisible) const {
2990   std::vector<int> ret;
2991   posToColumnIndexes(p, ret, distance, includeInvisible);
2992   return ret.empty() ? -1 : ret.back();
2993 }
2994 
2995 //-----------------------------------------------------------------------------
2996 
posToColumnIndexes(const TPointD & p,std::vector<int> & indexes,double distance,bool includeInvisible) const2997 void SceneViewer::posToColumnIndexes(const TPointD &p,
2998                                      std::vector<int> &indexes, double distance,
2999                                      bool includeInvisible) const {
3000   int oldRasterizePli    = TXshSimpleLevel::m_rasterizePli;
3001   TApp *app              = TApp::instance();
3002   ToonzScene *scene      = app->getCurrentScene()->getScene();
3003   TXsheet *xsh           = app->getCurrentXsheet()->getXsheet();
3004   int frame              = app->getCurrentFrame()->getFrame();
3005   int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
3006   OnionSkinMask osm      = app->getCurrentOnionSkin()->getOnionSkinMask();
3007 
3008   TPointD pos = TPointD(p.x - width() / 2, p.y - height() / 2);
3009   Stage::Picker picker(getViewMatrix(), pos, m_visualSettings,
3010                        getDevPixRatio());
3011   picker.setMinimumDistance(distance);
3012 
3013   TXshSimpleLevel::m_rasterizePli = 0;
3014 
3015   Stage::VisitArgs args;
3016   args.m_scene       = scene;
3017   args.m_xsh         = xsh;
3018   args.m_row         = frame;
3019   args.m_col         = currentColumnIndex;
3020   args.m_osm         = &osm;
3021   args.m_onlyVisible = includeInvisible;
3022 
3023   Stage::visit(picker, args); /*
3024 picker,
3025 scene,
3026 xsh,
3027 frame,
3028 currentColumnIndex,
3029 osm,
3030 false,
3031 0,
3032 false,
3033 includeInvisible);
3034 */
3035 
3036   TXshSimpleLevel::m_rasterizePli = oldRasterizePli;
3037   picker.getColumnIndexes(indexes);
3038 }
3039 
3040 //-----------------------------------------------------------------------------
3041 
posToRow(const TPointD & p,double distance,bool includeInvisible,bool currentColumnOnly) const3042 int SceneViewer::posToRow(const TPointD &p, double distance,
3043                           bool includeInvisible, bool currentColumnOnly) const {
3044   int oldRasterizePli = TXshSimpleLevel::m_rasterizePli;
3045   TApp *app           = TApp::instance();
3046   OnionSkinMask osm   = app->getCurrentOnionSkin()->getOnionSkinMask();
3047 
3048   TPointD pos = TPointD(p.x - width() / 2, p.y - height() / 2);
3049   Stage::Picker picker(getViewMatrix(), pos, m_visualSettings,
3050                        getDevPixRatio());
3051   picker.setMinimumDistance(distance);
3052 
3053   if (app->getCurrentFrame()->isEditingLevel()) {
3054     Stage::visit(picker, app->getCurrentLevel()->getLevel(),
3055                  app->getCurrentFrame()->getFid(), osm,
3056                  app->getCurrentFrame()->isPlaying(), false);
3057   } else {
3058     ToonzScene *scene      = app->getCurrentScene()->getScene();
3059     TXsheet *xsh           = app->getCurrentXsheet()->getXsheet();
3060     int frame              = app->getCurrentFrame()->getFrame();
3061     int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
3062 
3063     TXshSimpleLevel::m_rasterizePli = 0;
3064 
3065     Stage::VisitArgs args;
3066     args.m_scene       = scene;
3067     args.m_xsh         = xsh;
3068     args.m_row         = frame;
3069     args.m_col         = currentColumnIndex;
3070     args.m_osm         = &osm;
3071     args.m_onlyVisible = includeInvisible;
3072 
3073     if (currentColumnOnly) picker.setCurrentColumnIndex(currentColumnIndex);
3074 
3075     Stage::visit(picker, args);
3076   }
3077   TXshSimpleLevel::m_rasterizePli = oldRasterizePli;
3078   return picker.getRow();
3079 }
3080 
3081 //-----------------------------------------------------------------------------
3082 
drawSpline(const TAffine & viewMatrix,const TRect & clipRect,bool camera3d,double pixelsize)3083 void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d,
3084                 double pixelsize) {
3085   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
3086 
3087   TStageObjectId objId = TApp::instance()->getCurrentObject()->getObjectId();
3088 
3089   TStageObject *pegbar =
3090       objId != TStageObjectId::NoneId ? xsh->getStageObject(objId) : 0;
3091   const TStroke *stroke = 0;
3092   if (pegbar && pegbar->getSpline()) stroke = pegbar->getSpline()->getStroke();
3093   if (!stroke) return;
3094 
3095   int frame = TApp::instance()->getCurrentFrame()->getFrame();
3096 
3097   TAffine aff;
3098   double objZ = 0, objNoScaleZ = 0;
3099   if (objId != TStageObjectId::NoneId) {
3100     aff         = xsh->getParentPlacement(objId, frame);
3101     objZ        = xsh->getZ(objId, frame);
3102     objNoScaleZ = xsh->getStageObject(objId)->getGlobalNoScaleZ();
3103   }
3104 
3105   glPushMatrix();
3106   if (camera3d) {
3107     tglMultMatrix(aff);
3108     aff = TAffine();
3109     glTranslated(0, 0, objZ);
3110   } else {
3111     TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
3112     double camZ             = xsh->getZ(cameraId, frame);
3113     TAffine camAff          = xsh->getPlacement(cameraId, frame);
3114     TAffine tmp;
3115     TStageObject::perspective(tmp, camAff, camZ, aff, objZ, objNoScaleZ);
3116     aff = viewMatrix * tmp;
3117   }
3118 
3119   if (TApp::instance()->getCurrentObject()->isSpline()) {
3120     glColor3d(1.0, 0.5, 0);
3121     glLineStipple(1, 0x18FF);
3122   } else {
3123     glLineStipple(1, 0xCCCC);
3124     glColor3d(1, 0, 1);
3125   }
3126 
3127   glEnable(GL_LINE_STIPPLE);
3128   tglMultMatrix(aff);
3129 
3130   double pixelSize    = std::max(0.1, pixelsize);
3131   double strokeLength = stroke->getLength();
3132   int n               = (int)(5 + (strokeLength / pixelSize) * 0.1);
3133 
3134   glBegin(GL_LINE_STRIP);
3135   for (int i = 0; i < n; i++)
3136     tglVertex(stroke->getPoint((double)i / (double)(n - 1)));
3137   glEnd();
3138   glDisable(GL_LINE_STIPPLE);
3139   int cpCount = stroke->getControlPointCount();
3140   for (int i = 0; i * 4 < cpCount; i++) {
3141     double t    = stroke->getParameterAtControlPoint(i * 4);
3142     TPointD pos = stroke->getPoint(t);
3143     tglDrawText(pos, QString::number(i).toStdString().c_str());
3144   }
3145 
3146   if (pegbar) {
3147     TAffine parentAff = xsh->getParentPlacement(objId, frame);
3148     TAffine aff       = xsh->getPlacement(objId, frame);
3149     TPointD center    = Stage::inch * xsh->getCenter(objId, frame);
3150     glPushMatrix();
3151     tglMultMatrix(parentAff.inv() * TTranslation(aff * center));
3152     center = TPointD();
3153 
3154     // draw center
3155     // tglDrawDisk(center,pixelSize*5);
3156     tglDrawDisk(center, sqrt(tglGetPixelSize2()) * 5);
3157 
3158     glPopMatrix();
3159   }
3160 
3161   glPopMatrix();
3162 }
3163 
3164 //-----------------------------------------------------------------------------
3165 
resetInputMethod()3166 void SceneViewer::resetInputMethod() {
3167 #if QT_VERSION >= 0x050000
3168   QGuiApplication::inputMethod()->reset();
3169 #else
3170   qApp->inputContext()->reset();
3171 #endif
3172 }
3173 
3174 //-----------------------------------------------------------------------------
3175 
set3DLeftSideView()3176 void SceneViewer::set3DLeftSideView() {
3177   m_phi3D   = -90;
3178   m_theta3D = 0;
3179   invalidateAll();
3180 }
3181 
3182 //-----------------------------------------------------------------------------
3183 
set3DRightSideView()3184 void SceneViewer::set3DRightSideView() {
3185   m_phi3D   = 90;
3186   m_theta3D = 0;
3187   invalidateAll();
3188 }
3189 
3190 //-----------------------------------------------------------------------------
3191 
set3DTopView()3192 void SceneViewer::set3DTopView() {
3193   m_phi3D   = 0;
3194   m_theta3D = 90;
3195   invalidateAll();
3196 }
3197 
3198 //-----------------------------------------------------------------------------
3199 
canSwapCompared() const3200 bool SceneViewer::canSwapCompared() const {
3201   return m_visualSettings.m_doCompare && m_previewMode != NO_PREVIEW;
3202 }
3203 
3204 //-----------------------------------------------------------------------------
3205 
getNormalZoomScale()3206 TAffine SceneViewer::getNormalZoomScale() {
3207   return TScale(getDpiFactor()).inv();
3208 }
3209 
3210 //-----------------------------------------------------------------------------
3211 
invalidateToolStatus()3212 void SceneViewer::invalidateToolStatus() {
3213   TTool *tool = TApp::instance()->getCurrentTool()->getTool();
3214   if (tool) {
3215     m_toolDisableReason = tool->updateEnabled();
3216     if (tool->isEnabled()) {
3217       setToolCursor(this, tool->getCursorId());
3218       tool->setViewer(this);
3219       tool->updateMatrix();
3220     } else
3221       setCursor(Qt::ForbiddenCursor);
3222   } else
3223     setCursor(Qt::ForbiddenCursor);
3224 }
3225 
3226 //-----------------------------------------------------------------------------
3227 /*! return the viewer geometry in order to avoid picking the style outside of
3228    the viewer
3229     when using the stylepicker and the finger tools
3230 */
3231 
getGeometry() const3232 TRectD SceneViewer::getGeometry() const {
3233   int devPixRatio = getDevPixRatio();
3234   TTool *tool     = TApp::instance()->getCurrentTool()->getTool();
3235   TPointD topLeft =
3236       tool->getMatrix().inv() * winToWorld(geometry().topLeft() * devPixRatio);
3237   TPointD bottomRight = tool->getMatrix().inv() *
3238                         winToWorld(geometry().bottomRight() * devPixRatio);
3239 
3240   TObjectHandle *objHandle = TApp::instance()->getCurrentObject();
3241   if (tool->getToolType() & TTool::LevelTool && !objHandle->isSpline()) {
3242     topLeft.x /= m_dpiScale.x;
3243     topLeft.y /= m_dpiScale.y;
3244     bottomRight.x /= m_dpiScale.x;
3245     bottomRight.y /= m_dpiScale.y;
3246   }
3247 
3248   return TRectD(topLeft, bottomRight);
3249 }
3250 
3251 //-----------------------------------------------------------------------------
3252 /*! delete preview - subcamera executed from context menu
3253  */
doDeleteSubCamera()3254 void SceneViewer::doDeleteSubCamera() {
3255   PreviewSubCameraManager::instance()->deleteSubCamera(this);
3256 }
3257 
3258 //-----------------------------------------------------------------------------
3259 
bindFBO()3260 void SceneViewer::bindFBO() {
3261   if (m_fbo) m_fbo->bind();
3262 }
3263 
3264 //-----------------------------------------------------------------------------
3265 
releaseFBO()3266 void SceneViewer::releaseFBO() {
3267   if (m_fbo) m_fbo->release();
3268 }
3269 
3270 //-----------------------------------------------------------------------------
3271 
onContextAboutToBeDestroyed()3272 void SceneViewer::onContextAboutToBeDestroyed() {
3273   if (!m_lutCalibrator) return;
3274   makeCurrent();
3275   m_lutCalibrator->cleanup();
3276   doneCurrent();
3277   disconnect(context(), SIGNAL(aboutToBeDestroyed()), this,
3278              SLOT(onContextAboutToBeDestroyed()));
3279 }
3280 
3281 //-----------------------------------------------------------------------------
3282 // called from SceneViewer::initializeGL()
3283 
registerContext()3284 void SceneViewer::registerContext() {
3285   // release the old context, if any
3286   // this will be happen when dock / float the viewer panel.
3287   bool hasOldContext;
3288 #ifdef _WIN32
3289   hasOldContext =
3290       (m_currentContext.first != nullptr && m_currentContext.second != nullptr);
3291 #else
3292   hasOldContext = m_currentContext != nullptr;
3293 #endif
3294   if (hasOldContext) {
3295     int ret = l_contexts.erase(m_currentContext);
3296     if (ret)
3297       TGLDisplayListsManager::instance()->releaseContext(m_currentContext);
3298   }
3299 
3300   // then, register context and the space Id correspondent to it.
3301   int displayListId;
3302   if (TApp::instance()->getMainWindow() &&
3303       TApp::instance()->getMainWindow()->isAncestorOf(this) &&
3304       QThread::currentThread() == qGuiApp->thread()) {
3305     // obtain displaySpaceId for main thread
3306     if (l_mainDisplayListsSpaceId == -1)
3307       l_mainDisplayListsSpaceId =
3308           TGLDisplayListsManager::instance()->storeProxy(new DummyProxy);
3309 
3310     displayListId = l_mainDisplayListsSpaceId;
3311   }
3312   // for the other cases (such as for floating viewer), it can't share the
3313   // context so
3314   // obtain different id
3315   else
3316     displayListId =
3317         TGLDisplayListsManager::instance()->storeProxy(new DummyProxy);
3318   TGlContext tglContext(tglGetCurrentContext());
3319   TGLDisplayListsManager::instance()->attachContext(displayListId, tglContext);
3320   l_contexts.insert(tglContext);
3321 }
3322