1
2
3 #include "imageviewer.h"
4
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "menubarcommandids.h"
8 #include "flipbook.h"
9 #include "histogrampopup.h"
10 #include "sceneviewer.h" // ToggleCommandHandler
11 #include "mainwindow.h" //RecentFiles
12
13 // TnzTools includes
14 #include "tools/toolcommandids.h"
15 #include "tools/cursors.h"
16 #include "tools/cursormanager.h"
17 #include "tools/stylepicker.h"
18
19 // TnzQt includes
20 #include "toonzqt/menubarcommand.h"
21 #include "toonzqt/viewcommandids.h"
22 #include "toonzqt/imageutils.h"
23 #include "toonzqt/lutcalibrator.h"
24
25 // TnzLib includes
26 #include "toonz/tscenehandle.h"
27 #include "toonz/toonzscene.h"
28 #include "toonz/sceneproperties.h"
29 #include "toonz/palettecontroller.h"
30 #include "toonz/tpalettehandle.h"
31 #include "toonz/preferences.h"
32
33 // TnzCore includes
34 #include "tgl.h"
35
36 // Qt includes
37 #include <QMenu>
38 #include <QAction>
39 #include <QMouseEvent>
40 #include <QWheelEvent>
41 #include <QOpenGLFramebufferObject>
42 #include <QGestureEvent>
43
44 //===================================================================================
45
46 extern ToggleCommandHandler safeAreaToggle;
47 extern void getSafeAreaData(double &_smallSize, double &_largeSize);
48 // enable to choose safe area from a list, and enable to draw multiple lines
49 extern void getSafeAreaSizeList(QList<QList<double>> &_sizeList);
50
51 //===================================================================================
52
53 //==============================
54 // Local namespace stuff
55 //------------------------------
56
57 namespace {
58 // enable to choose safe area from a list, and enable to draw multiple lines
drawSafeArea(const TRectD box)59 void drawSafeArea(const TRectD box) {
60 glPushMatrix();
61 glLoadIdentity();
62
63 tglColor(TPixel32::Red);
64 glLineStipple(1, 0xCCCC);
65 glEnable(GL_LINE_STIPPLE);
66
67 tglDrawRect(box);
68
69 QList<QList<double>> sizeList;
70
71 getSafeAreaSizeList(sizeList);
72
73 for (int i = 0; i < sizeList.size(); i++) {
74 QList<double> curSize = sizeList.at(i);
75 if (curSize.size() == 5)
76 tglColor(
77 TPixel((int)curSize.at(2), (int)curSize.at(3), (int)curSize.at(4)));
78 else
79 tglColor(TPixel32::Red);
80
81 double facX = -0.5 * (1 - curSize.at(0) / 100.0);
82 double facY = -0.5 * (1 - curSize.at(1) / 100.0);
83 tglDrawRect(box.enlarge(facX * box.getLx(), facY * box.getLy()));
84 }
85
86 glDisable(GL_LINE_STIPPLE);
87 glPopMatrix();
88 }
89
90 //-----------------------------------------------------------------------------
91
getImageBounds(const TImageP & img)92 inline TRect getImageBounds(const TImageP &img) {
93 if (TRasterImageP ri = img)
94 return ri->getRaster()->getBounds();
95 else if (TToonzImageP ti = img)
96 return ti->getRaster()->getBounds();
97 else {
98 TVectorImageP vi = img;
99 return convert(vi->getBBox());
100 }
101 }
102
103 //-----------------------------------------------------------------------------
104
getImageBoundsD(const TImageP & img)105 inline TRectD getImageBoundsD(const TImageP &img) {
106 if (TRasterImageP ri = img)
107 return TRectD(0, 0, ri->getRaster()->getLx(), ri->getRaster()->getLy());
108 else if (TToonzImageP ti = img)
109 return TRectD(0, 0, ti->getSize().lx, ti->getSize().ly);
110 else {
111 TVectorImageP vi = img;
112 return vi->getBBox();
113 }
114 }
115
116 //-----------------------------------------------------------------------------
117
118 class FlipZoomer final : public ImageUtils::ShortcutZoomer {
119 public:
FlipZoomer(ImageViewer * parent)120 FlipZoomer(ImageViewer *parent) : ShortcutZoomer(parent) {}
121
zoom(bool zoomin,bool resetView)122 bool zoom(bool zoomin, bool resetView) override {
123 static_cast<ImageViewer *>(getWidget())->zoomQt(zoomin, resetView);
124 return true;
125 }
126
fit()127 bool fit() override {
128 static_cast<ImageViewer *>(getWidget())->fitView();
129 return true;
130 }
131
resetZoom()132 bool resetZoom() override {
133 static_cast<ImageViewer *>(getWidget())->resetZoom();
134 return true;
135 }
136
toggleFullScreen(bool quit)137 bool toggleFullScreen(bool quit) override {
138 if (ImageUtils::FullScreenWidget *fsWidget =
139 dynamic_cast<ImageUtils::FullScreenWidget *>(
140 getWidget()->parentWidget()))
141 return fsWidget->toggleFullScreen(quit);
142
143 return false;
144 }
145 };
146
147 //-----------------------------------------------------------------------------
148
149 class ImageViewerShortcutReceiver {
150 FlipBook *m_flipbook;
151
152 public:
ImageViewerShortcutReceiver(FlipBook * flipbook)153 ImageViewerShortcutReceiver(FlipBook *flipbook) : m_flipbook(flipbook) {}
~ImageViewerShortcutReceiver()154 ~ImageViewerShortcutReceiver() {}
155
exec(QKeyEvent * event)156 bool exec(QKeyEvent *event) {
157 CommandManager *cManager = CommandManager::instance();
158
159 if (event->key() == cManager->getKeyFromId(MI_FreezePreview)) {
160 m_flipbook->isFreezed() ? m_flipbook->unfreezePreview()
161 : m_flipbook->freezePreview();
162 return true;
163 }
164
165 if (event->key() == cManager->getKeyFromId(MI_ClonePreview)) {
166 m_flipbook->clonePreview();
167 return true;
168 }
169
170 if (event->key() == cManager->getKeyFromId(MI_RegeneratePreview)) {
171 m_flipbook->regenerate();
172 return true;
173 }
174
175 if (event->key() == cManager->getKeyFromId(MI_RegenerateFramePr)) {
176 m_flipbook->regenerateFrame();
177 return true;
178 }
179
180 return false;
181 }
182 };
183 } // namespace
184
185 //=============================================================================
186
187 //==========================
188 // ImageViewer class
189 //--------------------------
190
191 /*! \class ImageViewer
192 \brief The ImageViewer class provides to view an image.
193
194 Inherits \b QOpenGLWidget.
195
196 The object allows also to manage pan and zoom event. It's
197 possible to set a
198 color mask TRop::ColorMask to image view.
199 */
200 /*! \fn void ImageViewer::setColorMask(UCHAR colorMask)
201 Set current TRop::ColorMask color mask to \b colorMask.
202 */
203 /*! \fn TImageP ImageViewer::getImage()
204 Return current image viewer.
205 */
206 /*! \fn TAffine ImageViewer::getViewAff()
207 Return current viewer matrix trasformation.
208 */
209
ImageViewer(QWidget * parent,FlipBook * flipbook,bool showHistogram)210 ImageViewer::ImageViewer(QWidget *parent, FlipBook *flipbook,
211 bool showHistogram)
212 : GLWidgetForHighDpi(parent)
213 , m_pressedMousePos(0, 0)
214 , m_mouseButton(Qt::NoButton)
215 , m_draggingZoomSelection(false)
216 , m_image()
217 , m_FPS(0)
218 , m_viewAff()
219 , m_pos(0, 0)
220 , m_visualSettings()
221 , m_compareSettings()
222 , m_isHistogramEnable(showHistogram)
223 , m_flipbook(flipbook)
224 , m_isColorModel(false)
225 , m_histogramPopup(0)
226 , m_isRemakingPreviewFx(false)
227 , m_rectRGBPick(false)
228 , m_firstImage(true) {
229 m_visualSettings.m_sceneProperties =
230 TApp::instance()->getCurrentScene()->getScene()->getProperties();
231 m_visualSettings.m_drawExternalBG = true;
232 setAttribute(Qt::WA_KeyCompression);
233 setFocusPolicy(Qt::StrongFocus);
234
235 setMouseTracking(true);
236
237 setAttribute(Qt::WA_AcceptTouchEvents);
238 grabGesture(Qt::SwipeGesture);
239 grabGesture(Qt::PanGesture);
240 grabGesture(Qt::PinchGesture);
241
242 if (m_isHistogramEnable)
243 m_histogramPopup = new HistogramPopup(tr("Flipbook Histogram"));
244
245 if (Preferences::instance()->isColorCalibrationEnabled())
246 m_lutCalibrator = new LutCalibrator();
247 }
248
249 //-----------------------------------------------------------------------------
250
contextMenuEvent(QContextMenuEvent * event)251 void ImageViewer::contextMenuEvent(QContextMenuEvent *event) {
252 QAction *action;
253
254 if (m_isColorModel) {
255 event->ignore();
256 return;
257 }
258
259 QMenu *menu = new QMenu(this);
260
261 if (m_flipbook) {
262 if (m_flipbook->getPreviewedFx()) {
263 if (!(windowState() & Qt::WindowFullScreen)) {
264 action = menu->addAction(tr("Clone Preview"));
265 action->setShortcut(QKeySequence(
266 CommandManager::instance()->getKeyFromId(MI_ClonePreview)));
267 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(clonePreview()));
268 }
269
270 if (m_flipbook->isFreezed()) {
271 action = menu->addAction(tr("Unfreeze Preview"));
272 action->setShortcut(QKeySequence(
273 CommandManager::instance()->getKeyFromId(MI_FreezePreview)));
274 connect(action, SIGNAL(triggered()), m_flipbook,
275 SLOT(unfreezePreview()));
276 } else {
277 action = menu->addAction(tr("Freeze Preview"));
278 action->setShortcut(QKeySequence(
279 CommandManager::instance()->getKeyFromId(MI_FreezePreview)));
280 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(freezePreview()));
281 }
282
283 action = menu->addAction(tr("Regenerate Preview"));
284 action->setShortcut(QKeySequence(
285 CommandManager::instance()->getKeyFromId(MI_RegeneratePreview)));
286 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(regenerate()));
287
288 action = menu->addAction(tr("Regenerate Frame Preview"));
289 action->setShortcut(QKeySequence(
290 CommandManager::instance()->getKeyFromId(MI_RegenerateFramePr)));
291 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(regenerateFrame()));
292
293 menu->addSeparator();
294 }
295
296 action = menu->addAction(tr("Load / Append Images"));
297 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(loadImages()));
298
299 // history of the loaded paths of flipbook
300 action = CommandManager::instance()->getAction(MI_LoadRecentImage);
301 menu->addAction(action);
302 action->setParent(m_flipbook);
303
304 menu->addSeparator();
305 }
306
307 QAction *reset = menu->addAction(tr("Reset View"));
308 reset->setShortcut(
309 QKeySequence(CommandManager::instance()->getKeyFromId(V_ViewReset)));
310 connect(reset, SIGNAL(triggered()), SLOT(resetView()));
311
312 QAction *fit = menu->addAction(tr("Fit To Window"));
313 fit->setShortcut(
314 QKeySequence(CommandManager::instance()->getKeyFromId(V_ZoomFit)));
315 connect(fit, SIGNAL(triggered()), SLOT(fitView()));
316
317 if (m_flipbook) {
318 #ifdef _WIN32
319 if (ImageUtils::FullScreenWidget *fsWidget =
320 dynamic_cast<ImageUtils::FullScreenWidget *>(parentWidget())) {
321 bool isFullScreen = (fsWidget->windowState() & Qt::WindowFullScreen) != 0;
322
323 action = menu->addAction(isFullScreen ? tr("Exit Full Screen Mode")
324 : tr("Full Screen Mode"));
325
326 action->setShortcut(QKeySequence(
327 CommandManager::instance()->getKeyFromId(V_ShowHideFullScreen)));
328 connect(action, SIGNAL(triggered()), fsWidget, SLOT(toggleFullScreen()));
329 }
330
331 #endif
332
333 bool addedSep = false;
334
335 if (m_isHistogramEnable &&
336 visibleRegion().contains(event->pos() * getDevPixRatio())) {
337 menu->addSeparator();
338 addedSep = true;
339 action = menu->addAction(tr("Show Histogram"));
340 connect(action, SIGNAL(triggered()), SLOT(showHistogram()));
341 }
342
343 if (m_visualSettings.m_doCompare) {
344 if (!addedSep) {
345 menu->addSeparator();
346 addedSep = true;
347 }
348 action = menu->addAction(tr("Swap Compared Images"));
349 connect(action, SIGNAL(triggered()), SLOT(swapCompared()));
350 }
351
352 if (m_flipbook->isSavable()) {
353 if (!addedSep) menu->addSeparator();
354 action = menu->addAction(tr("Save Images"));
355 connect(action, SIGNAL(triggered()), m_flipbook, SLOT(saveImages()));
356 }
357 }
358
359 menu->exec(event->globalPos());
360
361 if (m_flipbook) {
362 action = CommandManager::instance()->getAction(MI_LoadRecentImage);
363 action->setParent(0);
364 }
365
366 delete menu;
367 update();
368 }
369
370 //-----------------------------------------------------------------------------
371
setVisual(const ImagePainter::VisualSettings & settings)372 void ImageViewer::setVisual(const ImagePainter::VisualSettings &settings) {
373 m_visualSettings = settings;
374 m_visualSettings.m_sceneProperties =
375 TApp::instance()->getCurrentScene()->getScene()->getProperties();
376 m_visualSettings.m_drawExternalBG = true;
377 }
378
379 //-----------------------------------------------------------------------------
380
~ImageViewer()381 ImageViewer::~ImageViewer() {
382 // iwsw commented out temporarily
383 /*
384 if (m_ghibli3DLutUtil)
385 {
386 m_ghibli3DLutUtil->onEnd();
387 delete m_ghibli3DLutUtil;
388 }
389 */
390 if (m_fbo) delete m_fbo;
391 }
392
393 //-----------------------------------------------------------------------------
394 /*! Set current image to \b image and update. If Histogram is visible set its
395 * image.
396 */
setImage(TImageP image)397 void ImageViewer::setImage(TImageP image) {
398 m_image = image;
399
400 if (m_image && m_firstImage) {
401 m_firstImage = false;
402 fitView();
403 // when the viewer size is large enough, limit the zoom ratio to 100% so
404 // that the image is shown in actual pixel size without jaggies due to
405 // resampling.
406 if (fabs(m_viewAff.det()) > 1.0) resetView();
407 }
408
409 if (m_isHistogramEnable && m_histogramPopup->isVisible())
410 m_histogramPopup->setImage(image);
411 if (!isColorModel())
412 repaint();
413 else
414 update();
415 }
416
417 //-------------------------------------------------------------------
418
setHistogramTitle(QString levelName)419 void ImageViewer::setHistogramTitle(QString levelName) {
420 if (m_isHistogramEnable && m_histogramPopup->isVisible()) {
421 QString histogramTitle = QString("Histogram :: ") + levelName;
422 m_histogramPopup->setTitle(histogramTitle);
423 }
424 }
425 //-------------------------------------------------------------------
426
setHistogramEnable(bool enable)427 void ImageViewer::setHistogramEnable(bool enable) {
428 m_isHistogramEnable = enable;
429 if (m_isHistogramEnable && !m_histogramPopup)
430 m_histogramPopup = new HistogramPopup(tr("Flipbook Histogram"));
431 }
432
433 //-----------------------------------------------------------------------------
434
hideHistogram()435 void ImageViewer::hideHistogram() {
436 if (!m_isHistogramEnable) return;
437 m_histogramPopup->setImage(TImageP());
438 if (m_histogramPopup->isVisible()) {
439 m_histogramPopup->hide();
440 m_histogramPopup->setTitle(tr("Flipbook Histogram"));
441 }
442 }
443
444 //-------------------------------------------------------------------
445
initializeGL()446 void ImageViewer::initializeGL() {
447 initializeOpenGLFunctions();
448
449 // to be computed once through the software
450 if (m_lutCalibrator && !m_lutCalibrator->isInitialized()) {
451 m_lutCalibrator->initialize();
452 connect(context(), SIGNAL(aboutToBeDestroyed()), this,
453 SLOT(onContextAboutToBeDestroyed()));
454 }
455
456 // glClearColor(1.0,1.0,1.0,1);
457 glClear(GL_COLOR_BUFFER_BIT);
458
459 if (m_firstInitialized)
460 m_firstInitialized = false;
461 else {
462 resizeGL(width(), height());
463 update();
464 }
465 }
466
467 //-----------------------------------------------------------------------------
468
resizeGL(int w,int h)469 void ImageViewer::resizeGL(int w, int h) {
470 w *= getDevPixRatio();
471 h *= getDevPixRatio();
472 glViewport(0, 0, w, h);
473 glMatrixMode(GL_PROJECTION);
474 glLoadIdentity();
475 double nearPlane = 100;
476 double farPlane = 2500;
477 double centerPlane = 1100;
478 glOrtho(0, w, 0, h, -4000, 4000);
479 glMatrixMode(GL_MODELVIEW);
480 glLoadIdentity();
481 glTranslatef(0.375, 0.375, 0.0);
482 glTranslated(w * 0.5, h * 0.5, 0);
483
484 // remake fbo with new size
485 if (m_lutCalibrator && m_lutCalibrator->isValid()) {
486 if (m_fbo) delete m_fbo;
487 m_fbo = new QOpenGLFramebufferObject(w, h);
488 }
489 }
490
491 //-----------------------------------------------------------------------------
492
paintGL()493 void ImageViewer::paintGL() {
494 if (m_lutCalibrator && m_lutCalibrator->isValid()) m_fbo->bind();
495
496 TDimension viewerSize(width(), height());
497 TAffine aff = m_viewAff;
498
499 // if (!m_visualSettings.m_defineLoadbox && m_flipbook &&
500 // m_flipbook->getLoadbox()!=TRect())
501 // offs =
502 // convert(m_flipbook->getLoadbox().getP00())-TPointD(m_flipbook->getImageSize().lx/2.0,
503 // m_flipbook->getImageSize().ly/2.0);
504
505 TDimension imageSize;
506 TRect loadbox;
507
508 if (m_flipbook) {
509 QString title =
510 (!m_image)
511 ? m_flipbook->getTitle() + tr(" :: Zoom : ") +
512 QString::number(tround(sqrt(m_viewAff.det()) * 100)) + " %"
513 : m_flipbook->getLevelZoomTitle() + tr(" :: Zoom : ") +
514 QString::number(tround(sqrt(m_viewAff.det()) * 100)) + " %";
515 m_flipbook->parentWidget()->setWindowTitle(title);
516 imageSize = m_flipbook->getImageSize();
517 if (m_visualSettings.m_useLoadbox && m_flipbook->getLoadbox() != TRect())
518 loadbox = m_flipbook->getLoadbox();
519 }
520 m_visualSettings.m_sceneProperties =
521 TApp::instance()->getCurrentScene()->getScene()->getProperties();
522 // enable checks only in the color model
523 m_visualSettings.m_useChecks = m_isColorModel;
524 ImagePainter::paintImage(m_image, imageSize, viewerSize, aff,
525 m_visualSettings, m_compareSettings, loadbox);
526
527 // when fx parameter is modified with showing the fx preview,
528 // a flipbook shows a red border line before the rendered result is shown.
529 if (m_isRemakingPreviewFx) {
530 glPushMatrix();
531 glLoadIdentity();
532 glColor3d(1.0, 0.0, 0.0);
533
534 glBegin(GL_LINE_LOOP);
535 glVertex2d(5, 5);
536 glVertex2d(5, height() - 5);
537 glVertex2d(width() - 5, height() - 5);
538 glVertex2d(width() - 5, 5);
539 glEnd();
540
541 glBegin(GL_LINE_LOOP);
542 glVertex2d(10, 10);
543 glVertex2d(10, height() - 10);
544 glVertex2d(width() - 10, height() - 10);
545 glVertex2d(width() - 10, 10);
546 glEnd();
547 glPopMatrix();
548 }
549
550 if (!m_image) {
551 if (m_lutCalibrator && m_lutCalibrator->isValid())
552 m_lutCalibrator->onEndDraw(m_fbo);
553 return;
554 }
555
556 if (safeAreaToggle.getStatus() && !m_isColorModel) {
557 TRasterImageP rimg = (TRasterImageP)m_image;
558 TVectorImageP vimg = (TVectorImageP)m_image;
559 TToonzImageP timg = (TToonzImageP)m_image;
560 TRect bbox;
561
562 TPointD centerD;
563 TRect bounds;
564 if (rimg) {
565 centerD = rimg->getRaster()->getCenterD();
566 bounds = rimg->getRaster()->getBounds();
567 } else if (timg) {
568 centerD = timg->getRaster()->getCenterD();
569 bounds = timg->getRaster()->getBounds();
570 }
571
572 if (!vimg) {
573 TAffine aff = TTranslation(viewerSize.lx * 0.5, viewerSize.ly * 0.5) *
574 m_viewAff * TTranslation(-centerD);
575 TRectD bbox = aff * TRectD(0, 0, bounds.getLx() - 1, bounds.getLy() - 1);
576 drawSafeArea(bbox);
577 }
578 }
579 TPoint fromPos, toPos;
580
581 if (m_visualSettings.m_defineLoadbox && m_flipbook) {
582 TRect loadbox =
583 convert(getImgToWidgetAffine() * convert(m_flipbook->getLoadbox()));
584 if (loadbox != TRect()) {
585 TPoint p00 = loadbox.getP00();
586 TPoint p11 = loadbox.getP11();
587 fromPos =
588 TPoint(p00.x - width() * 0.5,
589 height() * 0.5 - p00.y); // m_flipbook->getLoadbox().getP00();
590 toPos =
591 TPoint(p11.x - width() * 0.5,
592 height() * 0.5 - p11.y); // m_flipbook->getLoadbox().getP11();
593 }
594 } else if (m_draggingZoomSelection || m_rectRGBPick) {
595 fromPos = TPoint(m_pressedMousePos.x - width() * 0.5,
596 height() * 0.5 - m_pressedMousePos.y);
597 toPos = TPoint(m_pos.x() - width() * 0.5, height() * 0.5 - m_pos.y());
598 }
599 if (fromPos != TPoint() || toPos != TPoint()) {
600 if (m_rectRGBPick) {
601 tglColor(TPixel32::Red);
602 // TODO: glLineStipple is deprecated in the latest OpenGL. Need to be
603 // replaced. (shun_iwasawa 2015/12/25)
604 glLineStipple(1, 0x3F33);
605 glEnable(GL_LINE_STIPPLE);
606
607 glBegin(GL_LINE_STRIP);
608 // do not draw the rect around the mouse cursor
609 int margin = (fromPos.y < toPos.y) ? -3 : 3;
610 glVertex2i(toPos.x, toPos.y + margin);
611 glVertex2i(toPos.x, fromPos.y);
612 glVertex2i(fromPos.x, fromPos.y);
613 glVertex2i(fromPos.x, toPos.y);
614 margin = (fromPos.x < toPos.x) ? -3 : 3;
615 glVertex2i(toPos.x + margin, toPos.y);
616 glEnd();
617 glDisable(GL_LINE_STIPPLE);
618 } else {
619 tglColor(m_draggingZoomSelection ? TPixel32::Red : TPixel32::Blue);
620 glBegin(GL_LINE_STRIP);
621 glVertex2i(fromPos.x, fromPos.y);
622 glVertex2i(fromPos.x, toPos.y);
623 glVertex2i(toPos.x, toPos.y);
624 glVertex2i(toPos.x, fromPos.y);
625 glVertex2i(fromPos.x, fromPos.y);
626 glEnd();
627 }
628 }
629
630 if (m_lutCalibrator && m_lutCalibrator->isValid())
631 m_lutCalibrator->onEndDraw(m_fbo);
632 }
633
634 //------------------------------------------------------------------------------
635 /*! Add to current trasformation matrix a \b delta traslation.
636 */
panQt(const QPoint & delta)637 void ImageViewer::panQt(const QPoint &delta) {
638 if (delta == QPoint()) return;
639
640 // stop panning when the image is at the edge of window
641 QPoint delta_(delta.x(), delta.y());
642
643 TToonzImageP timg = (TToonzImageP)m_image;
644 TRasterImageP rimg = (TRasterImageP)m_image;
645 if (timg || rimg) {
646 bool isXPlus = delta.x() > 0;
647 bool isYPlus = delta.y() > 0;
648
649 TDimension imgSize((timg) ? timg->getSize() : rimg->getRaster()->getSize());
650 int subSampling = (timg) ? timg->getSubsampling() : rimg->getSubsampling();
651
652 TPointD cornerPos = TPointD(imgSize.lx * ((isXPlus) ? -1 : 1),
653 imgSize.ly * ((isYPlus) ? 1 : -1)) *
654 (0.5 / (double)subSampling);
655 cornerPos = m_viewAff * cornerPos;
656
657 if ((cornerPos.x > 0) == isXPlus) delta_.setX(0);
658 if ((cornerPos.y < 0) == isYPlus) delta_.setY(0);
659 }
660
661 setViewAff(TTranslation(delta_.x(), -delta_.y()) * m_viewAff);
662
663 update();
664 }
665
666 //-----------------------------------------------------------------------------
667 /*! Add to current trasformation matrix a \b center traslation matched with a
668 scale of factor \b factor. Apply a zoom of factor \b factor with
669 center
670 \b center.
671 */
zoomQt(const QPoint & center,double factor)672 void ImageViewer::zoomQt(const QPoint ¢er, double factor) {
673 if (factor == 1.0) return;
674 TPointD delta(center.x(), center.y());
675 double scale2 = fabs(m_viewAff.det());
676 if ((scale2 < 100000 || factor < 1) && (scale2 > 0.001 * 0.05 || factor > 1))
677 setViewAff(TTranslation(delta) * TScale(factor) * TTranslation(-delta) *
678 m_viewAff);
679 update();
680 }
681
682 //-----------------------------------------------------------------------------
683
zoomQt(bool forward,bool reset)684 void ImageViewer::zoomQt(bool forward, bool reset) {
685 double scale2 = m_viewAff.det();
686 if (reset)
687 setViewAff(TAffine());
688 else if (((scale2 < 100000 || !forward) &&
689 (scale2 > 0.001 * 0.05 || forward))) {
690 double oldZoomScale = sqrt(scale2);
691 double zoomScale =
692 reset ? 1 : ImageUtils::getQuantizedZoomFactor(oldZoomScale, forward);
693 setViewAff(TScale(zoomScale / oldZoomScale) * m_viewAff);
694 }
695 update();
696 }
697
698 //-----------------------------------------------------------------------------
699
resetZoom()700 void ImageViewer::resetZoom() {
701 double oldZoomScale = sqrt(m_viewAff.det());
702 setViewAff(TScale(1.0 / oldZoomScale) * m_viewAff);
703 update();
704 }
705
706 //-----------------------------------------------------------------------------
707
dragCompare(const QPoint & dp)708 void ImageViewer::dragCompare(const QPoint &dp) {
709 if (m_compareSettings.m_dragCompareX)
710 m_compareSettings.m_compareX += ((double)dp.x()) / width();
711 else if (m_compareSettings.m_dragCompareY)
712 m_compareSettings.m_compareY -= ((double)dp.y()) / height();
713
714 m_compareSettings.m_compareX = tcrop(m_compareSettings.m_compareX, 0.0, 1.0);
715 m_compareSettings.m_compareY = tcrop(m_compareSettings.m_compareY, 0.0, 1.0);
716
717 update();
718 }
719
720 //-----------------------------------------------------------------------------
721
updateLoadbox(const TPoint & curPos)722 void ImageViewer::updateLoadbox(const TPoint &curPos) {
723 TAffine aff = getImgToWidgetAffine();
724 TRect r =
725 m_flipbook
726 ->getLoadbox(); // convert(aff*convert(m_flipbook->getLoadbox()));
727 if (m_dragType == eDrawRect)
728 r = convert(aff.inv() *
729 convert(TRect(m_pressedMousePos,
730 curPos))); // TRect(m_pressedMousePos, curPos);
731 else if (m_dragType == eMoveRect)
732 r = r + (TPoint(curPos.x, -curPos.y) - TPoint(m_pos.x(), -m_pos.y()));
733 else {
734 double fac = sqrt(1.0 / fabs(aff.det()));
735
736 if (m_dragType & eMoveLeft) r.x0 += fac * (curPos.x - m_pos.x());
737 if (m_dragType & eMoveRight) r.x1 += fac * (curPos.x - m_pos.x());
738 if (m_dragType & eMoveDown) r.y1 -= fac * (curPos.y - m_pos.y());
739 if (m_dragType & eMoveUp) r.y0 -= fac * (curPos.y - m_pos.y());
740 }
741 m_flipbook->setLoadbox(r); // convert(aff.inv() * convert(r)));
742 }
743
744 //--------------------------------------------------------
updateCursor(const TPoint & curPos)745 void ImageViewer::updateCursor(const TPoint &curPos) {
746 int dragType = getDragType(
747 curPos,
748 convert(getImgToWidgetAffine() * convert(m_flipbook->getLoadbox())));
749
750 switch (dragType) {
751 case eMoveRect:
752 setCursor(Qt::SizeAllCursor);
753 break;
754 case eMoveLeft:
755 case eMoveRight:
756 setCursor(Qt::SizeHorCursor);
757 break;
758 case eMoveDown:
759 case eMoveUp:
760 setCursor(Qt::SizeVerCursor);
761 break;
762 case eMoveLeft | eMoveUp:
763 case eMoveRight | eMoveDown:
764 setCursor(Qt::SizeBDiagCursor);
765 break;
766 case eMoveLeft | eMoveDown:
767 case eMoveRight | eMoveUp:
768 setCursor(Qt::SizeFDiagCursor);
769 break;
770 default:
771 setCursor(Qt::ArrowCursor);
772 break;
773 }
774 }
775
776 //---------------------------------------------------------------------------------------------
777
showEvent(QShowEvent *)778 void ImageViewer::showEvent(QShowEvent *) {
779 TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
780 bool ret = connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)),
781 this, SLOT(onPreferenceChanged(const QString &)));
782 onPreferenceChanged("ColorCalibration");
783 assert(ret);
784 }
785
786 //---------------------------------------------------------------------------------------------
787
hideEvent(QHideEvent *)788 void ImageViewer::hideEvent(QHideEvent *) {
789 TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
790 if (sceneHandle) sceneHandle->disconnect(this);
791 }
792
793 //---------------------------------------------------------------------------------------------
794 /*! If middle button is pressed pan the image. Update current mouse position.
795 */
mouseMoveEvent(QMouseEvent * event)796 void ImageViewer::mouseMoveEvent(QMouseEvent *event) {
797 if (!m_image) return;
798
799 if (m_gestureActive && m_touchDevice == QTouchDevice::TouchScreen &&
800 !m_stylusUsed) {
801 return;
802 }
803
804 QPoint curQPos = event->pos() * getDevPixRatio();
805
806 TPoint curPos = TPoint(curQPos.x(), curQPos.y());
807
808 if (m_visualSettings.m_defineLoadbox && m_flipbook) {
809 if (m_mouseButton == Qt::LeftButton)
810 updateLoadbox(curPos);
811 else if (m_mouseButton == Qt::MidButton)
812 panQt(curQPos - m_pos);
813 else
814 updateCursor(curPos);
815 update();
816 event->ignore();
817 m_pos = curQPos;
818 return;
819 }
820
821 // setting the cursors
822 if (!m_isColorModel) {
823 // when the histogram window is opened, switch to the RGB picker tool
824 if (m_isHistogramEnable && m_histogramPopup->isVisible())
825 setToolCursor(this, ToolCursor::PickerRGB);
826 else
827 setCursor(Qt::ArrowCursor);
828 }
829
830 if (m_visualSettings.m_doCompare && m_mouseButton == Qt::NoButton) {
831 if (fabs(curPos.x - width() * m_compareSettings.m_compareX) < 20)
832 setToolCursor(this, ToolCursor::ScaleHCursor);
833 else if (fabs((height() - curPos.y) -
834 height() * m_compareSettings.m_compareY) < 20)
835 setToolCursor(this, ToolCursor::ScaleVCursor);
836 }
837
838 if (m_compareSettings.m_dragCompareX || m_compareSettings.m_dragCompareY)
839 dragCompare(curQPos - m_pos);
840 else if (m_mouseButton == Qt::MidButton)
841 panQt(curQPos - m_pos);
842
843 m_pos = curQPos;
844
845 // pick the color if the histogram popup is opened
846 if (m_isHistogramEnable && m_histogramPopup->isVisible() && !m_isColorModel) {
847 // Rect Pick
848 if (m_mouseButton == Qt::LeftButton &&
849 (event->modifiers() & Qt::ControlModifier)) {
850 if (!m_rectRGBPick && !m_visualSettings.m_defineLoadbox &&
851 (abs(m_pos.x() - m_pressedMousePos.x) > 10 ||
852 abs(m_pos.y() - m_pressedMousePos.y) > 10) &&
853 !(m_compareSettings.m_dragCompareX ||
854 m_compareSettings.m_dragCompareY) &&
855 m_flipbook)
856 m_rectRGBPick = true;
857
858 if (m_rectRGBPick) {
859 update();
860 rectPickColor();
861 }
862 }
863
864 update();
865 pickColor(event);
866 return;
867 }
868
869 if (m_mouseButton == Qt::LeftButton && !m_isColorModel &&
870 (event->modifiers() & Qt::AltModifier)) {
871 if (!m_draggingZoomSelection && !m_visualSettings.m_defineLoadbox &&
872 (abs(m_pos.x() - m_pressedMousePos.x) > 10 ||
873 abs(m_pos.y() - m_pressedMousePos.y) > 10) &&
874 !(m_compareSettings.m_dragCompareX ||
875 m_compareSettings.m_dragCompareY) &&
876 m_flipbook)
877 m_draggingZoomSelection = true;
878
879 if (m_draggingZoomSelection) update();
880 } else
881 m_draggingZoomSelection = false;
882
883 if (m_isColorModel && m_mouseButton == Qt::LeftButton) {
884 event->ignore();
885 }
886 }
887
888 //---------------------------------------------------------------------------------------------
889 /*! notify the color picked by rgb picker to palette controller
890 */
setPickedColorToStyleEditor(const TPixel32 & color)891 void ImageViewer::setPickedColorToStyleEditor(const TPixel32 &color) {
892 // do not modify the style #0
893 TPaletteHandle *ph =
894 TApp::instance()->getPaletteController()->getCurrentPalette();
895 if (ph->getStyleIndex() == 0) return;
896
897 TApp::instance()->getPaletteController()->setColorSample(color);
898 }
899
900 //---------------------------------------------------------------------------------------------
901
getPickedImage(QPointF mousePos)902 TImageP ImageViewer::getPickedImage(QPointF mousePos) {
903 bool cursorIsInSnapShot = false;
904 if (m_visualSettings.m_doCompare) {
905 if (m_compareSettings.m_compareY == ImagePainter::DefaultCompareValue)
906 cursorIsInSnapShot =
907 m_compareSettings.m_swapCompared ==
908 (mousePos.x() > width() * m_compareSettings.m_compareX);
909 else
910 cursorIsInSnapShot =
911 m_compareSettings.m_swapCompared ==
912 (height() - mousePos.y() > height() * m_compareSettings.m_compareY);
913 }
914 if (cursorIsInSnapShot)
915 return TImageCache::instance()->get(QString("TnzCompareImg"), false);
916 else
917 return m_image;
918 }
919
920 //---------------------------------------------------------------------------------------------
921 /*! rgb picking
922 */
pickColor(QMouseEvent * event,bool putValueToStyleEditor)923 void ImageViewer::pickColor(QMouseEvent *event, bool putValueToStyleEditor) {
924 if (!m_isHistogramEnable) return;
925 if (!m_histogramPopup->isVisible()) return;
926
927 QPointF curPos = event->localPos() * getDevPixRatio();
928
929 // avoid to pick outside of the flip
930 if ((!m_image) || !rect().contains(curPos.toPoint())) {
931 // throw transparent color
932 m_histogramPopup->updateInfo(TPixel32::Transparent, TPointD(-1, -1));
933 return;
934 }
935
936 TImageP img = getPickedImage(curPos);
937 if (!img) {
938 m_histogramPopup->updateInfo(TPixel32::Transparent, TPointD(-1, -1));
939 return;
940 }
941
942 StylePicker picker(img);
943
944 TPointD mousePos = TPointD(curPos.x(), height() - 1 - curPos.y());
945 TRectD area = TRectD(mousePos.x, mousePos.y, mousePos.x, mousePos.y);
946
947 TPointD pos =
948 getViewAff().inv() * TPointD(curPos.x() - (qreal)(width()) / 2,
949 -curPos.y() + (qreal)(height()) / 2);
950
951 TRectD imgRect = (img->raster()) ? convert(TRect(img->raster()->getSize()))
952 : img->getBBox();
953 TPointD imagePos =
954 TPointD(0.5 * imgRect.getLx() + pos.x, 0.5 * imgRect.getLy() + pos.y);
955
956 TPixel32 pix;
957 if (m_lutCalibrator && m_lutCalibrator->isValid()) {
958 // for specifiying pixel range on picking vector
959 double scale2 = getViewAff().det();
960 pix = picker.pickColor(pos + TPointD(-0.5, -0.5), 10.0, scale2);
961 } else
962 pix = picker.pickColor(area);
963
964 if (!img->raster() || imgRect.contains(imagePos)) {
965 // throw the picked color to the histogram
966 m_histogramPopup->updateInfo(pix, imagePos);
967 // throw it to the style editor as well
968 if (putValueToStyleEditor) setPickedColorToStyleEditor(pix);
969 } else {
970 // throw transparent color if picking outside of the image
971 m_histogramPopup->updateInfo(TPixel32::Transparent, TPointD(-1, -1));
972 }
973 }
974
975 //---------------------------------------------------------------------------------------------
976 /*! rectangular rgb picking. The picked color will be an average of pixels in
977 * specified rectangle
978 */
rectPickColor(bool putValueToStyleEditor)979 void ImageViewer::rectPickColor(bool putValueToStyleEditor) {
980 auto isRas32 = [this](TImageP img) -> bool {
981 TRasterImageP ri = img;
982 if (!ri) return false;
983 TRaster32P ras32 = ri->getRaster();
984 if (!ras32) return false;
985 return true;
986 };
987
988 if (!m_isHistogramEnable) return;
989 if (!m_histogramPopup->isVisible()) return;
990
991 TImageP img = getPickedImage(m_pos);
992 if (!img) {
993 m_histogramPopup->updateAverageColor(TPixel32::Transparent);
994 return;
995 }
996
997 StylePicker picker(img);
998
999 TPoint startPos =
1000 TPoint(m_pressedMousePos.x, height() - 1 - m_pressedMousePos.y);
1001 TPoint endPos = TPoint(m_pos.x(), height() - 1 - m_pos.y());
1002 TRectD area = TRectD(convert(startPos), convert(endPos));
1003 area = area.enlarge(-1, -1);
1004 if (area.getLx() < 2 || area.getLy() < 2) {
1005 m_histogramPopup->updateAverageColor(TPixel32::Transparent);
1006 return;
1007 }
1008
1009 TPixel32 pix;
1010 if (m_lutCalibrator && m_lutCalibrator->isValid() && isRas32(img)) {
1011 TPointD start = getViewAff().inv() * TPointD(startPos.x - width() / 2,
1012 startPos.y - height() / 2);
1013 TPointD end = getViewAff().inv() *
1014 TPointD(endPos.x - width() / 2, endPos.y - height() / 2);
1015 pix = picker.pickAverageColor(TRectD(start, end));
1016 } else
1017 pix = picker.pickColor(area.enlarge(-1, -1));
1018
1019 // throw the picked color to the histogram
1020 m_histogramPopup->updateAverageColor(pix);
1021 // throw it to the style editor as well
1022 if (putValueToStyleEditor) setPickedColorToStyleEditor(pix);
1023 }
1024
1025 //-----------------------------------------------------------------------------
1026 /*! Set current position and current mouse button event.
1027 Ignore event, so access to parent mousePressEvent.
1028 */
1029
getDragType(const TPoint & pos,const TRect & loadbox)1030 int ImageViewer::getDragType(const TPoint &pos, const TRect &loadbox) {
1031 if (loadbox == TRect()) return eDrawRect;
1032
1033 int ret = 0, dx = std::min(abs(loadbox.x0 - pos.x), abs(loadbox.x1 - pos.x)),
1034 dy = std::min(abs(loadbox.y0 - pos.y), abs(loadbox.y1 - pos.y));
1035
1036 if (dx > 10 && dy > 10)
1037 return (loadbox.contains(pos)) ? eMoveRect : eDrawRect;
1038
1039 if (dx <= 10 && pos.y >= loadbox.y0 - 10 && pos.y <= loadbox.y1 + 10) {
1040 if (dx == abs(loadbox.x0 - pos.x))
1041 ret = eMoveLeft;
1042 else if (dx == abs(loadbox.x1 - pos.x))
1043 ret = eMoveRight;
1044 } else if (dy <= 10 && pos.x >= loadbox.x0 - 10 && pos.x <= loadbox.x1 + 10) {
1045 if (dy == abs(loadbox.y0 - pos.y))
1046 return eMoveDown;
1047 else
1048 return eMoveUp;
1049 } else
1050 return eDrawRect;
1051
1052 if (dy > 10) return ret;
1053
1054 return ret | (dy == (abs(loadbox.y0 - pos.y)) ? eMoveDown : eMoveUp);
1055 }
1056
1057 //-------------------------------------------------------------------------------
mouseDoubleClickEvent(QMouseEvent * event)1058 void ImageViewer::mouseDoubleClickEvent(QMouseEvent *event) {
1059 if (!m_image) return;
1060 // qDebug() << "[mouseDoubleClickEvent]";
1061 if (m_gestureActive && !m_stylusUsed) {
1062 m_gestureActive = false;
1063 fitView();
1064 return;
1065 }
1066
1067 if (m_visualSettings.m_defineLoadbox && m_flipbook) {
1068 m_flipbook->setLoadbox(TRect());
1069 update();
1070 event->ignore();
1071 }
1072 }
1073
1074 //------------------------------------------------------------------------------
1075
mousePressEvent(QMouseEvent * event)1076 void ImageViewer::mousePressEvent(QMouseEvent *event) {
1077 if (!m_image) return;
1078
1079 // qDebug() << "[mousePressEvent]";
1080 if (m_gestureActive && m_touchDevice == QTouchDevice::TouchScreen &&
1081 !m_stylusUsed) {
1082 return;
1083 }
1084
1085 m_pos = event->pos() * getDevPixRatio();
1086 m_pressedMousePos = TPoint(m_pos.x(), m_pos.y());
1087 m_mouseButton = event->button();
1088 m_draggingZoomSelection = false;
1089
1090 if (m_mouseButton != Qt::LeftButton) {
1091 event->ignore();
1092 return;
1093 }
1094 if (m_visualSettings.m_defineLoadbox && m_flipbook)
1095 m_dragType = getDragType(
1096 m_pressedMousePos,
1097 convert(getImgToWidgetAffine() * convert(m_flipbook->getLoadbox())));
1098 else if (m_visualSettings.m_doCompare) {
1099 if (fabs(m_pos.x() - width() * m_compareSettings.m_compareX) < 20) {
1100 m_compareSettings.m_dragCompareX = true;
1101 m_compareSettings.m_dragCompareY = false;
1102 m_compareSettings.m_compareY = ImagePainter::DefaultCompareValue;
1103 update();
1104 } else if (fabs((height() - m_pos.y()) -
1105 height() * m_compareSettings.m_compareY) < 20) {
1106 m_compareSettings.m_dragCompareY = true;
1107 m_compareSettings.m_dragCompareX = false;
1108 m_compareSettings.m_compareX = ImagePainter::DefaultCompareValue;
1109 update();
1110 } else
1111 m_compareSettings.m_dragCompareX = m_compareSettings.m_dragCompareY =
1112 false;
1113 }
1114
1115 // pick color and throw it to the style editor
1116 if (m_isHistogramEnable && m_histogramPopup->isVisible() && !m_isColorModel &&
1117 !(event->modifiers() & Qt::ControlModifier)) // if ctrl button is
1118 // pressed, which means
1119 // rectangular pick
1120 {
1121 update();
1122 pickColor(event, true);
1123 }
1124
1125 event->ignore();
1126 }
1127
1128 //-----------------------------------------------------------------------------
1129 /*! Reset current mouse position and current mouse button event.
1130 */
mouseReleaseEvent(QMouseEvent * event)1131 void ImageViewer::mouseReleaseEvent(QMouseEvent *event) {
1132 if (!m_image) return;
1133 if (m_draggingZoomSelection && !m_visualSettings.m_defineLoadbox) {
1134 m_draggingZoomSelection = false;
1135
1136 int left, right, top, bottom;
1137 if (m_pos.x() < m_pressedMousePos.x)
1138 left = m_pos.x(), right = m_pressedMousePos.x;
1139 else
1140 right = m_pos.x(), left = m_pressedMousePos.x;
1141 if (m_pos.y() < m_pressedMousePos.y)
1142 top = m_pos.y(), bottom = m_pressedMousePos.y;
1143 else
1144 bottom = m_pos.y(), top = m_pressedMousePos.y;
1145
1146 adaptView(QRect(QPoint(left, top), QPoint(right, bottom)));
1147 }
1148
1149 if (m_rectRGBPick) {
1150 // pick color in the rectangular region
1151 rectPickColor(true);
1152 // release the flag
1153 m_rectRGBPick = false;
1154 update();
1155 }
1156
1157 m_pos = QPoint(0, 0);
1158 m_mouseButton = Qt::NoButton;
1159 m_compareSettings.m_dragCompareX = m_compareSettings.m_dragCompareY = false;
1160
1161 m_gestureActive = false;
1162 m_zooming = false;
1163 m_panning = false;
1164 m_stylusUsed = false;
1165
1166 event->ignore();
1167 }
1168
1169 //-----------------------------------------------------------------------------
1170 /*! Apply zoom.
1171 */
wheelEvent(QWheelEvent * event)1172 void ImageViewer::wheelEvent(QWheelEvent *event) {
1173 if (!m_image) return;
1174 if (event->orientation() == Qt::Horizontal) return;
1175 int delta = 0;
1176 switch (event->source()) {
1177 case Qt::MouseEventNotSynthesized: {
1178 if (event->modifiers() & Qt::AltModifier)
1179 delta = event->angleDelta().x();
1180 else
1181 delta = event->angleDelta().y();
1182 break;
1183 }
1184
1185 case Qt::MouseEventSynthesizedBySystem: {
1186 QPoint numPixels = event->pixelDelta();
1187 QPoint numDegrees = event->angleDelta() / 8;
1188 if (!numPixels.isNull()) {
1189 delta = event->pixelDelta().y();
1190 } else if (!numDegrees.isNull()) {
1191 QPoint numSteps = numDegrees / 15;
1192 delta = numSteps.y();
1193 }
1194 break;
1195 }
1196
1197 default: // Qt::MouseEventSynthesizedByQt,
1198 // Qt::MouseEventSynthesizedByApplication
1199 {
1200 std::cout << "not supported event: Qt::MouseEventSynthesizedByQt, "
1201 "Qt::MouseEventSynthesizedByApplication"
1202 << std::endl;
1203 break;
1204 }
1205
1206 } // end switch
1207
1208 if (abs(delta) > 0) {
1209 if ((m_gestureActive == true &&
1210 m_touchDevice == QTouchDevice::TouchScreen) ||
1211 m_gestureActive == false) {
1212 int delta = event->delta() > 0 ? 120 : -120;
1213 QPoint center(event->pos().x() * getDevPixRatio() - width() / 2,
1214 -event->pos().y() * getDevPixRatio() + height() / 2);
1215 zoomQt(center, exp(0.001 * delta));
1216 }
1217 }
1218 event->accept();
1219 }
1220
1221 //-----------------------------------------------------------------------------
1222
swapCompared()1223 void ImageViewer::swapCompared() {
1224 m_compareSettings.m_swapCompared = !m_compareSettings.m_swapCompared;
1225 update();
1226 }
1227
1228 //-----------------------------------------------------------------------------
1229
setViewAff(TAffine viewAff)1230 void ImageViewer::setViewAff(TAffine viewAff) {
1231 m_viewAff = viewAff;
1232 update();
1233 if (m_flipbook && m_flipbook->getPreviewedFx())
1234 // Update the observable fx region
1235 m_flipbook->schedulePreviewedFxUpdate();
1236 }
1237
1238 //-----------------------------------------------------------------------------
1239
resetView()1240 void ImageViewer::resetView() { zoomQt(false, true); }
1241
1242 //-----------------------------------------------------------------------------
1243
fitView()1244 void ImageViewer::fitView() {
1245 if (!m_image) return;
1246 TRect imgBounds(getImageBounds(m_image));
1247 adaptView(imgBounds, imgBounds);
1248 }
1249
1250 //-----------------------------------------------------------------------------
1251 /*! Update image viewer, reset current matrix trasformation, current position
1252 and update view.
1253 */
updateImageViewer()1254 void ImageViewer::updateImageViewer() {
1255 setViewAff(TAffine());
1256 m_pos = QPoint(0, 0);
1257 update();
1258 }
1259
1260 //-----------------------------------------------------------------------------
1261
getImgToWidgetAffine() const1262 TAffine ImageViewer::getImgToWidgetAffine() const {
1263 assert(m_image);
1264 return getImgToWidgetAffine(getImageBoundsD(m_image));
1265 }
1266
1267 //-----------------------------------------------------------------------------
1268
1269 //! Returns the affine transform from image reference to widget's pixel one.
getImgToWidgetAffine(const TRectD & geom) const1270 TAffine ImageViewer::getImgToWidgetAffine(const TRectD &geom) const {
1271 TPointD geomCenter((geom.x0 + geom.x1) * 0.5, (geom.y0 + geom.y1) * 0.5);
1272
1273 QRect widGeom(rect());
1274
1275 TPointD viewerCenter((widGeom.left() + widGeom.right() + 1) * 0.5,
1276 (widGeom.top() + widGeom.bottom() + 1) * 0.5);
1277
1278 return TAffine(TAffine(1.0, 0.0, 0.0, 0.0, -1.0, height()) *
1279 TTranslation(viewerCenter) * m_viewAff *
1280 TTranslation(-geomCenter));
1281 }
1282
1283 //-----------------------------------------------------------------------------
1284
1285 //! Adapts image viewer's affine to display the passed image rect at maximized
1286 //! ratio
adaptView(const TRect & imgRect,const TRect & viewRect)1287 void ImageViewer::adaptView(const TRect &imgRect, const TRect &viewRect) {
1288 QRect viewerRect(rect());
1289
1290 if (viewerRect.isEmpty()) return;
1291
1292 double imageScale = std::min(viewerRect.width() / (double)viewRect.getLx(),
1293 viewerRect.height() / (double)viewRect.getLy());
1294
1295 TPointD viewRectCenter((viewRect.x0 + viewRect.x1 + 1) * 0.5,
1296 (viewRect.y0 + viewRect.y1 + 1) * 0.5);
1297 TPointD imgRectCenter((imgRect.x0 + imgRect.x1 + 1) * 0.5,
1298 (imgRect.y0 + imgRect.y1 + 1) * 0.5);
1299
1300 TAffine newViewAff(TScale(imageScale, imageScale) *
1301 TTranslation(imgRectCenter - viewRectCenter));
1302 setViewAff(newViewAff);
1303 }
1304
1305 //-----------------------------------------------------------------------------
1306
1307 //! Adapts image viewer's affine to display the passed viewer rect at maximized
1308 //! ratio
adaptView(const QRect & geomRect)1309 void ImageViewer::adaptView(const QRect &geomRect) {
1310 if (!m_image) return;
1311
1312 // Retrieve the rect in image reference and call the associated adaptView
1313 TRect imgBounds(getImageBounds(m_image));
1314 TRectD imgBoundsD(imgBounds.x0, imgBounds.y0, imgBounds.x1 + 1,
1315 imgBounds.y1 + 1);
1316
1317 TRectD geomRectD(geomRect.left(), geomRect.top(), geomRect.right() + 1,
1318 geomRect.bottom() + 1);
1319 TRectD viewRectD(getImgToWidgetAffine().inv() * geomRectD);
1320 TRect viewRect(tfloor(viewRectD.x0), tfloor(viewRectD.y0),
1321 tceil(viewRectD.x1) - 1, tceil(viewRectD.y1) - 1);
1322
1323 adaptView(imgBounds, viewRect);
1324 }
1325
doSwapBuffers()1326 void ImageViewer::doSwapBuffers() { glFlush(); }
1327
changeSwapBehavior(bool enable)1328 void ImageViewer::changeSwapBehavior(bool enable) {
1329 // do nothing for now as setUpdateBehavior is not available with QGLWidget
1330 // setUpdateBehavior(enable ? PartialUpdate : NoPartialUpdate);
1331 }
1332
1333 //-----------------------------------------------------------------------------
1334
keyPressEvent(QKeyEvent * event)1335 void ImageViewer::keyPressEvent(QKeyEvent *event) {
1336 if (FlipZoomer(this).exec(event)) return;
1337
1338 ImageViewerShortcutReceiver(m_flipbook).exec(event);
1339 }
1340
1341 //-----------------------------------------------------------------------------
1342
onContextAboutToBeDestroyed()1343 void ImageViewer::onContextAboutToBeDestroyed() {
1344 if (!m_lutCalibrator) return;
1345 makeCurrent();
1346 m_lutCalibrator->cleanup();
1347 doneCurrent();
1348 disconnect(context(), SIGNAL(aboutToBeDestroyed()), this,
1349 SLOT(onContextAboutToBeDestroyed()));
1350 }
1351
1352 //-----------------------------------------------------------------------------
1353
onPreferenceChanged(const QString & prefName)1354 void ImageViewer::onPreferenceChanged(const QString &prefName) {
1355 if (prefName == "ColorCalibration") {
1356 if (Preferences::instance()->isColorCalibrationEnabled()) {
1357 // if the window is so shriked that the gl widget is empty,
1358 // showEvent can be called before creating the context.
1359 if (!context()) return;
1360 makeCurrent();
1361 if (!m_lutCalibrator)
1362 m_lutCalibrator = new LutCalibrator();
1363 else
1364 m_lutCalibrator->cleanup();
1365 m_lutCalibrator->initialize();
1366 connect(context(), SIGNAL(aboutToBeDestroyed()), this,
1367 SLOT(onContextAboutToBeDestroyed()));
1368 if (m_lutCalibrator->isValid() && !m_fbo)
1369 m_fbo = new QOpenGLFramebufferObject(width(), height());
1370 doneCurrent();
1371 }
1372 update();
1373 }
1374 }
1375
1376 //------------------------------------------------------------------
1377
tabletEvent(QTabletEvent * e)1378 void ImageViewer::tabletEvent(QTabletEvent *e) {
1379 // qDebug() << "[tabletEvent]";
1380 if (e->type() == QTabletEvent::TabletPress) {
1381 m_stylusUsed = e->pointerType() ? true : false;
1382 } else if (e->type() == QTabletEvent::TabletRelease) {
1383 m_stylusUsed = false;
1384 }
1385
1386 e->accept();
1387 }
1388
1389 //------------------------------------------------------------------
1390
gestureEvent(QGestureEvent * e)1391 void ImageViewer::gestureEvent(QGestureEvent *e) {
1392 // qDebug() << "[gestureEvent]";
1393 m_gestureActive = false;
1394 if (QGesture *swipe = e->gesture(Qt::SwipeGesture)) {
1395 m_gestureActive = true;
1396 } else if (QGesture *pan = e->gesture(Qt::PanGesture)) {
1397 m_gestureActive = true;
1398 }
1399 if (QGesture *pinch = e->gesture(Qt::PinchGesture)) {
1400 QPinchGesture *gesture = static_cast<QPinchGesture *>(pinch);
1401 QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
1402 QPoint firstCenter = gesture->centerPoint().toPoint();
1403 if (m_touchDevice == QTouchDevice::TouchScreen)
1404 firstCenter = mapFromGlobal(firstCenter);
1405
1406 if (gesture->state() == Qt::GestureStarted) {
1407 m_gestureActive = true;
1408 } else if (gesture->state() == Qt::GestureFinished) {
1409 m_gestureActive = false;
1410 m_zooming = false;
1411 m_scaleFactor = 0.0;
1412 } else {
1413 if (changeFlags & QPinchGesture::ScaleFactorChanged) {
1414 double scaleFactor = gesture->scaleFactor();
1415 // the scale factor makes for too sensitive scaling
1416 // divide the change in half
1417 if (scaleFactor > 1) {
1418 double decimalValue = scaleFactor - 1;
1419 decimalValue /= 1.5;
1420 scaleFactor = 1 + decimalValue;
1421 } else if (scaleFactor < 1) {
1422 double decimalValue = 1 - scaleFactor;
1423 decimalValue /= 1.5;
1424 scaleFactor = 1 - decimalValue;
1425 }
1426 if (!m_zooming) {
1427 double delta = scaleFactor - 1;
1428 m_scaleFactor += delta;
1429 if (m_scaleFactor > .2 || m_scaleFactor < -.2) {
1430 m_zooming = true;
1431 }
1432 }
1433 if (m_zooming) {
1434 const QPoint center(
1435 firstCenter.x() * getDevPixRatio() - width() / 2,
1436 -firstCenter.y() * getDevPixRatio() + height() / 2);
1437 zoomQt(center, scaleFactor);
1438 m_panning = false;
1439 }
1440 m_gestureActive = true;
1441 }
1442
1443 if (changeFlags & QPinchGesture::CenterPointChanged) {
1444 QPointF centerDelta = (gesture->centerPoint() * getDevPixRatio()) -
1445 (gesture->lastCenterPoint() * getDevPixRatio());
1446 if (centerDelta.manhattanLength() > 1) {
1447 // panQt(centerDelta.toPoint());
1448 }
1449 m_gestureActive = true;
1450 }
1451 }
1452 }
1453 e->accept();
1454 }
1455
touchEvent(QTouchEvent * e,int type)1456 void ImageViewer::touchEvent(QTouchEvent *e, int type) {
1457 // qDebug() << "[touchEvent]";
1458 if (type == QEvent::TouchBegin) {
1459 m_touchActive = true;
1460 m_firstPanPoint = e->touchPoints().at(0).pos();
1461 // obtain device type
1462 m_touchDevice = e->device()->type();
1463 } else if (m_touchActive) {
1464 // touchpads must have 2 finger panning for tools and navigation to be
1465 // functional on other devices, 1 finger panning is preferred
1466 if ((e->touchPoints().count() == 2 &&
1467 m_touchDevice == QTouchDevice::TouchPad) ||
1468 (e->touchPoints().count() == 1 &&
1469 m_touchDevice == QTouchDevice::TouchScreen)) {
1470 QTouchEvent::TouchPoint panPoint = e->touchPoints().at(0);
1471 if (!m_panning) {
1472 QPointF deltaPoint = panPoint.pos() - m_firstPanPoint;
1473 // minimize accidental and jerky zooming/rotating during 2 finger
1474 // panning
1475 if ((deltaPoint.manhattanLength() > 100) && !m_zooming) {
1476 m_panning = true;
1477 }
1478 }
1479 if (m_panning) {
1480 QPoint curPos = panPoint.pos().toPoint() * getDevPixRatio();
1481 QPoint lastPos = panPoint.lastPos().toPoint() * getDevPixRatio();
1482 QPoint centerDelta = curPos - lastPos;
1483 panQt(centerDelta);
1484 }
1485 }
1486 }
1487 if (type == QEvent::TouchEnd || type == QEvent::TouchCancel) {
1488 m_touchActive = false;
1489 m_panning = false;
1490 }
1491 e->accept();
1492 }
1493
event(QEvent * e)1494 bool ImageViewer::event(QEvent *e) {
1495 /*
1496 switch (e->type()) {
1497 case QEvent::TabletPress: {
1498 QTabletEvent *te = static_cast<QTabletEvent *>(e);
1499 qDebug() << "[event] TabletPress: pointerType(" << te->pointerType()
1500 << ") device(" << te->device() << ")";
1501 } break;
1502 case QEvent::TabletRelease:
1503 qDebug() << "[event] TabletRelease";
1504 break;
1505 case QEvent::TouchBegin:
1506 qDebug() << "[event] TouchBegin";
1507 break;
1508 case QEvent::TouchEnd:
1509 qDebug() << "[event] TouchEnd";
1510 break;
1511 case QEvent::TouchCancel:
1512 qDebug() << "[event] TouchCancel";
1513 break;
1514 case QEvent::MouseButtonPress:
1515 qDebug() << "[event] MouseButtonPress";
1516 break;
1517 case QEvent::MouseButtonDblClick:
1518 qDebug() << "[event] MouseButtonDblClick";
1519 break;
1520 case QEvent::MouseButtonRelease:
1521 qDebug() << "[event] MouseButtonRelease";
1522 break;
1523 case QEvent::Gesture:
1524 qDebug() << "[event] Gesture";
1525 break;
1526 default:
1527 break;
1528 }
1529 */
1530
1531 if (e->type() == QEvent::Gesture && CommandManager::instance()
1532 ->getAction(MI_TouchGestureControl)
1533 ->isChecked()) {
1534 gestureEvent(static_cast<QGestureEvent *>(e));
1535 return true;
1536 }
1537 if ((e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchEnd ||
1538 e->type() == QEvent::TouchCancel || e->type() == QEvent::TouchUpdate) &&
1539 CommandManager::instance()
1540 ->getAction(MI_TouchGestureControl)
1541 ->isChecked()) {
1542 touchEvent(static_cast<QTouchEvent *>(e), e->type());
1543 m_gestureActive = true;
1544 return true;
1545 }
1546 return GLWidgetForHighDpi::event(e);
1547 }
1548
1549 //-----------------------------------------------------------------------------
1550 /*! load image from history
1551 */
1552 class LoadRecentFlipbookImagesCommandHandler final : public MenuItemHandler {
1553 public:
LoadRecentFlipbookImagesCommandHandler()1554 LoadRecentFlipbookImagesCommandHandler()
1555 : MenuItemHandler(MI_LoadRecentImage) {}
execute()1556 void execute() override {
1557 QAction *act = CommandManager::instance()->getAction(MI_LoadRecentImage);
1558
1559 /*--- 右クリックで呼ばれないとここにWidgetが入らない ---*/
1560 FlipBook *flip = qobject_cast<FlipBook *>(act->parentWidget());
1561 if (!flip) return;
1562
1563 DVMenuAction *menu = dynamic_cast<DVMenuAction *>(act->menu());
1564 int index = menu->getTriggeredActionIndex();
1565 QString path =
1566 RecentFiles::instance()->getFilePath(index, RecentFiles::Flip);
1567
1568 TFilePath fp(path.toStdWString());
1569
1570 /*--- shrinkは1でロードする ---*/
1571 ::viewFile(fp, -1, -1, -1, 1, 0, flip, false);
1572
1573 RecentFiles::instance()->moveFilePath(index, 0, RecentFiles::Flip);
1574 }
1575 } loadRecentFlipbookImagesCommandHandler;
1576
1577 //-----------------------------------------------------------------------------
1578 /*! clear the history
1579 */
1580 class ClearRecentFlipbookImagesCommandHandler final : public MenuItemHandler {
1581 public:
ClearRecentFlipbookImagesCommandHandler()1582 ClearRecentFlipbookImagesCommandHandler()
1583 : MenuItemHandler(MI_ClearRecentImage) {}
execute()1584 void execute() override {
1585 RecentFiles::instance()->clearRecentFilesList(RecentFiles::Flip);
1586 }
1587 } clearRecentFlipbookImagesCommandHandler;
1588