File indexing completed on 2025-03-09 03:52:05
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-02-11 0007 * Description : a tool to show image using an OpenGL interface. 0008 * 0009 * SPDX-FileCopyrightText: 2007-2008 by Markus Leuthold <kusi at forum dot titlis dot org> 0010 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "glviewerwidget.h" 0017 0018 // Qt includes 0019 0020 #include <QApplication> 0021 #include <QScreen> 0022 #include <QWindow> 0023 #include <QIcon> 0024 #include <QPointer> 0025 #include <QMimeType> 0026 #include <QMimeDatabase> 0027 #include <QStandardPaths> 0028 0029 // KDE includes 0030 0031 #include <klocalizedstring.h> 0032 0033 // Local includes 0034 0035 #include "glviewertimer.h" 0036 #include "dpluginaboutdlg.h" 0037 #include "digikam_debug.h" 0038 0039 // OpenGL headers is not included automatically with ARM targets 0040 0041 #ifdef Q_PROCESSOR_ARM 0042 # include <GL/gl.h> 0043 #endif 0044 0045 #ifndef GL_TEXTURE_RECTANGLE_ARB 0046 # define GL_TEXTURE_RECTANGLE_ARB 0x84F5 0047 #endif 0048 0049 #ifndef GL_TEXTURE_RECTANGLE_NV 0050 # define GL_TEXTURE_RECTANGLE_NV 0x84F5 0051 #endif 0052 0053 namespace DigikamGenericGLViewerPlugin 0054 { 0055 0056 class Q_DECL_HIDDEN GLViewerWidget::Private 0057 { 0058 public: 0059 0060 struct Cache 0061 { 0062 int file_index = 0; 0063 GLViewerTexture* texture = nullptr; 0064 }; 0065 0066 public: 0067 0068 enum WheelAction 0069 { 0070 zoomImage, 0071 changeImage 0072 }; 0073 0074 public: 0075 0076 explicit Private() 0077 : 0078 file_idx (0), ///< index of picture to be displayed 0079 0080 texture (nullptr), 0081 ratio_view_y (0.0F), 0082 ratio_view_x (0.0F), 0083 delta (0.0F), 0084 vertex_height (0.0F), 0085 vertex_width (0.0F), 0086 vertex_left (0.0F), 0087 vertex_top (0.0F), 0088 vertex_right (0.0F), 0089 vertex_bottom (0.0F), 0090 wheelAction (zoomImage), 0091 firstImage (true), 0092 0093 /** 0094 * while zooming is performed, the image is downsampled to d->zoomsize. This seems to 0095 * be the optimal way for a PentiumM 1.4G, Nvidia FX5200. For a faster setup, this might 0096 * not be necessary anymore 0097 */ 0098 zoomsize (QSize(1024, 768)), 0099 0100 /// load cursors for zooming and panning 0101 moveCursor (QCursor(Qt::PointingHandCursor)), 0102 zoomCursor (QCursor(QIcon::fromTheme(QLatin1String("zoom-in")).pixmap(64))), 0103 0104 /// define zoomfactors for one zoom step 0105 zoomfactor_scrollwheel (1.1F), 0106 zoomfactor_mousemove (1.03F), 0107 zoomfactor_keyboard (1.05F), 0108 0109 /// get path of nullImage in case QImage can't load the image 0110 nullImage (QIcon::fromTheme(QLatin1String("image-jpeg")).pixmap(256)), 0111 0112 iface (nullptr), 0113 plugin (nullptr) 0114 { 0115 for (int i = 0 ; i < CACHESIZE ; ++i) 0116 { 0117 cache[i].file_index = 0; 0118 cache[i].texture = nullptr; 0119 } 0120 } 0121 0122 QStringList files; 0123 unsigned int file_idx; 0124 Cache cache[CACHESIZE]; 0125 GLViewerTexture* texture; 0126 0127 float ratio_view_y; 0128 float ratio_view_x; 0129 float delta; 0130 float vertex_height; 0131 float vertex_width; 0132 float vertex_left; 0133 float vertex_top; 0134 float vertex_right; 0135 float vertex_bottom; 0136 0137 QPoint startdrag; 0138 QPoint previous_pos; 0139 WheelAction wheelAction; 0140 bool firstImage; 0141 QSize zoomsize; 0142 QTimer timerMouseMove; 0143 QCursor moveCursor; 0144 QCursor zoomCursor; 0145 float zoomfactor_scrollwheel; 0146 float zoomfactor_mousemove; 0147 float zoomfactor_keyboard; 0148 QPixmap nullImage; 0149 QSize screenSize; 0150 0151 DInfoInterface* iface; 0152 DPlugin* plugin; 0153 }; 0154 0155 GLViewerWidget::GLViewerWidget(DPlugin* const plugin, DInfoInterface* const iface, 0156 const QList<QUrl>& myfiles, const QString& selectedImage) 0157 : QOpenGLWidget(), 0158 d (new Private) 0159 { 0160 setAttribute(Qt::WA_DeleteOnClose); 0161 0162 d->plugin = plugin; 0163 d->iface = iface; 0164 0165 // Determine screen size for isReallyFullScreen 0166 0167 QScreen* screen = qApp->primaryScreen(); 0168 0169 if (QWidget* const widget = qApp->activeWindow()) 0170 { 0171 if (QWindow* const window = widget->windowHandle()) 0172 { 0173 screen = window->screen(); 0174 } 0175 } 0176 0177 d->screenSize = screen->size(); 0178 0179 // populate QStringList::d->files 0180 0181 int foundNumber = 0; 0182 0183 Q_FOREACH (const QUrl& url, myfiles) 0184 { 0185 // find selected image in album in order to determine the first displayed image 0186 // in case one image was selected and the entire album was loaded 0187 0188 QString s = url.toLocalFile(); 0189 0190 if (s == selectedImage) 0191 { 0192 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "selected img" << selectedImage << "has idx=" << foundNumber; 0193 d->file_idx = foundNumber; 0194 } 0195 0196 // only add images to d->files 0197 0198 QString mimeTypeName = QMimeDatabase().mimeTypeForUrl(QUrl::fromLocalFile(s)).name(); 0199 bool isImage = mimeTypeName.contains(QString::fromLatin1("image"), Qt::CaseInsensitive); 0200 0201 if (isImage) 0202 { 0203 d->files.append(s); 0204 foundNumber++; // counter for searching the start image in case one image is selected 0205 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << s << "type=" << mimeTypeName; 0206 } 0207 } 0208 0209 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << d->files.count() << "images loaded"; 0210 0211 showFullScreen(); // krazy:exclude=qmethods 0212 0213 // let the cursor disappear after 2sec of inactivity 0214 0215 connect(&d->timerMouseMove, SIGNAL(timeout()), 0216 this, SLOT(slotTimeoutMouseMove())); 0217 0218 d->timerMouseMove.start(2000); 0219 setMouseTracking(true); 0220 0221 // other initialisations 0222 0223 d->wheelAction = GLViewerWidget::Private::changeImage; 0224 } 0225 0226 GLViewerWidget::~GLViewerWidget() 0227 { 0228 for (int i = 0 ; i < CACHESIZE ; ++i) 0229 { 0230 d->cache[i].file_index = EMPTY; 0231 delete d->cache[i].texture; 0232 } 0233 0234 delete d; 0235 } 0236 0237 /** 0238 * \todo blending 0239 */ 0240 void GLViewerWidget::initializeGL() 0241 { 0242 glEnable(GL_TEXTURE_RECTANGLE_ARB); 0243 0244 // Clear The Background Color 0245 glClearColor(0.0, 0.0, 0.0, 1.0f); 0246 0247 // Turn Blending On 0248 glEnable(GL_BLEND); 0249 0250 // Blending Function For Translucency Based On Source Alpha Value 0251 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 0252 0253 // Enable perspective vision 0254 glClearDepth(1.0f); 0255 0256 // initialize cache 0257 for (int i = 0 ; i < CACHESIZE ; ++i) 0258 { 0259 d->cache[i].file_index = EMPTY; 0260 d->cache[i].texture = new GLViewerTexture(d->iface, this); 0261 } 0262 } 0263 0264 void GLViewerWidget::paintGL() 0265 { 0266 // this test has to be performed here since QWidget::width() is only updated now 0267 /* 0268 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "enter paintGL: isReallyFullscreen=" << isReallyFullScreen(); 0269 */ 0270 // prepare 1st image 0271 0272 if (d->firstImage && isReallyFullScreen()) 0273 { 0274 /* 0275 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "first image"; 0276 */ 0277 d->texture = loadImage(d->file_idx); 0278 d->texture->reset(); 0279 downloadTexture(d->texture); 0280 /* 0281 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "width=" << width(); 0282 */ 0283 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 0284 glLoadIdentity(); 0285 glTranslatef(0.0f, 0.0f, -5.0f); 0286 drawImage(d->texture); 0287 0288 // trigger a redraw NOW. the user wants to see a picture as soon as possible 0289 // only load the second image after the first is displayed 0290 0291 glFlush(); 0292 0293 // preload the 2nd image 0294 0295 if (d->firstImage) 0296 { 0297 if (d->file_idx < (unsigned int)(d->files.count() - 1)) 0298 { 0299 loadImage(d->file_idx+1); 0300 } 0301 0302 d->firstImage = false; 0303 } 0304 } 0305 0306 if (!d->firstImage) 0307 { 0308 /* 0309 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "width=" << width(); 0310 */ 0311 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 0312 glLoadIdentity(); 0313 glTranslatef(0.0f, 0.0f, -5.0f); 0314 drawImage(d->texture); 0315 } 0316 /* 0317 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "exit paintGL"; 0318 */ 0319 } 0320 0321 void GLViewerWidget::resizeGL(int w, int h) 0322 { 0323 /* 0324 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "resizeGL,w=" << w; 0325 */ 0326 glViewport(0, 0, (GLint)w, (GLint)h); 0327 glMatrixMode(GL_PROJECTION); 0328 glLoadIdentity(); 0329 0330 if (h > w) 0331 { 0332 d->ratio_view_x = 1.0; 0333 d->ratio_view_y = h / float(w); 0334 } 0335 else 0336 { 0337 d->ratio_view_x = w / float(h); 0338 d->ratio_view_y = 1.0; 0339 } 0340 0341 glFrustum(-d->ratio_view_x, d->ratio_view_x, -d->ratio_view_y, d->ratio_view_y, 5, 5000.0); 0342 glMatrixMode(GL_MODELVIEW); 0343 glLoadIdentity(); 0344 0345 if (d->texture == nullptr) 0346 { 0347 return; 0348 } 0349 0350 if (d->firstImage) 0351 { 0352 d->texture->setViewport(w, h); 0353 } 0354 } 0355 0356 /** 0357 * render the image 0358 */ 0359 void GLViewerWidget::drawImage(GLViewerTexture* const tex) 0360 { 0361 /* 0362 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "enter drawImage: target=" << d->texture->textureId() 0363 << "dim=" << d->texture->height() << d->texture->width(); 0364 */ 0365 glBindTexture(GL_TEXTURE_RECTANGLE_NV, tex->textureId()); 0366 glBegin(GL_QUADS); 0367 glTexCoord2f(0, 0); 0368 glVertex3f(tex->vertex_left(), tex->vertex_bottom(), 0); 0369 0370 glTexCoord2f(tex->width(), 0); 0371 glVertex3f(d->texture->vertex_right(), tex->vertex_bottom(), 0); 0372 0373 glTexCoord2f(tex->width(), tex->height()); 0374 glVertex3f(tex->vertex_right(), tex->vertex_top(), 0); 0375 0376 glTexCoord2f(0, tex->height()); 0377 glVertex3f(tex->vertex_left(), tex->vertex_top(), 0); 0378 glEnd(); 0379 } 0380 0381 /** 0382 * Handle all keyboard events. All events which are not handled trigger 0383 * a help window. 0384 */ 0385 void GLViewerWidget::keyPressEvent(QKeyEvent* e) 0386 { 0387 QPoint middlepoint; 0388 0389 switch (e->key()) 0390 { 0391 // next image 0392 0393 case Qt::Key_N: 0394 case Qt::Key_Right: 0395 case Qt::Key_Down: 0396 case Qt::Key_PageDown: 0397 case Qt::Key_Space: 0398 { 0399 nextImage(); 0400 break; 0401 } 0402 0403 // previous image 0404 0405 case Qt::Key_P: 0406 case Qt::Key_Left: 0407 case Qt::Key_Up: 0408 case Qt::Key_PageUp: 0409 { 0410 prevImage(); 0411 break; 0412 } 0413 0414 // rotate image 0415 0416 case Qt::Key_R: 0417 { 0418 d->texture->rotate(); 0419 downloadTexture(d->texture); 0420 update(); 0421 break; 0422 } 0423 0424 // terminate image viewer 0425 0426 case Qt::Key_Escape: 0427 { 0428 // clean up: where does this have to be done? 0429 0430 close(); 0431 break; 0432 } 0433 0434 // full screen 0435 0436 case Qt::Key_F: 0437 { 0438 // according to QT documentation, showFullScreen() has some 0439 // serious issues on window managers that do not follow modern 0440 // post-ICCCM specifications 0441 0442 if (isFullScreen()) 0443 { 0444 d->texture->reset(); 0445 showNormal(); // krazy:exclude=qmethods 0446 } 0447 else 0448 { 0449 d->texture->reset(); 0450 showFullScreen(); // krazy:exclude=qmethods 0451 } 0452 0453 break; 0454 } 0455 0456 // reset size and redraw 0457 0458 case Qt::Key_Z: 0459 { 0460 d->texture->reset(true); 0461 update(); 0462 0463 break; 0464 } 0465 0466 // toggle permanent between "show next image" and "zoom" on mousewheel change 0467 0468 case Qt::Key_C: 0469 { 0470 if (d->wheelAction == GLViewerWidget::Private::zoomImage) 0471 { 0472 d->wheelAction = GLViewerWidget::Private::changeImage; 0473 } 0474 else 0475 { 0476 d->wheelAction = GLViewerWidget::Private::zoomImage; 0477 } 0478 0479 break; 0480 } 0481 0482 // zoom in 0483 0484 case Qt::Key_Plus: 0485 { 0486 middlepoint = QPoint(width() / 2, height() / 2); 0487 0488 if (d->texture && d->texture->setNewSize(d->zoomsize)) 0489 { 0490 downloadTexture(d->texture); // load full resolution image 0491 } 0492 0493 zoom(-1, middlepoint, d->zoomfactor_keyboard); 0494 0495 break; 0496 } 0497 0498 // zoom out 0499 0500 case Qt::Key_Minus: 0501 { 0502 middlepoint = QPoint(width() / 2, height() / 2); 0503 0504 if (d->texture && d->texture->setNewSize(d->zoomsize)) 0505 { 0506 downloadTexture(d->texture); // load full resolution image 0507 } 0508 0509 zoom(1, middlepoint, d->zoomfactor_keyboard); 0510 0511 break; 0512 } 0513 0514 // zoom to original size 0515 0516 case Qt::Key_O: 0517 { 0518 if (d->texture) 0519 { 0520 d->texture->loadFullSize(); 0521 0522 if (d->texture->setNewSize(QSize(0, 0))) 0523 { 0524 downloadTexture(d->texture); // load full resolution image 0525 } 0526 0527 d->texture->zoomToOriginal(); 0528 } 0529 0530 update(); 0531 0532 break; 0533 } 0534 0535 // toggle temporarily between "show next image" and "zoom" on mousewheel change 0536 0537 case Qt::Key_Control: 0538 { 0539 if (d->wheelAction == GLViewerWidget::Private::zoomImage) 0540 { 0541 // scrollwheel changes to the next image 0542 0543 d->wheelAction = GLViewerWidget::Private::changeImage; 0544 } 0545 else 0546 { 0547 // scrollwheel does zoom 0548 0549 d->wheelAction = GLViewerWidget::Private::zoomImage; 0550 setCursor(d->zoomCursor); 0551 d->timerMouseMove.stop(); 0552 } 0553 0554 break; 0555 } 0556 0557 // F1 show help dialog to enlighten the user 0558 0559 case Qt::Key_F1: 0560 { 0561 QPointer<DPluginAboutDlg> help = new DPluginAboutDlg(d->plugin); 0562 help->exec(); 0563 break; 0564 } 0565 0566 default: 0567 { 0568 break; 0569 } 0570 } 0571 } 0572 0573 void GLViewerWidget::keyReleaseEvent(QKeyEvent* e) 0574 { 0575 switch (e->key()) 0576 { 0577 case Qt::Key_Plus: 0578 case Qt::Key_Minus: 0579 { 0580 if (!e->isAutoRepeat()) 0581 { 0582 unsetCursor(); 0583 0584 if (d->texture && d->texture->setNewSize(QSize(0, 0))) 0585 { 0586 downloadTexture(d->texture); // load full resolution image 0587 } 0588 0589 update(); 0590 } 0591 else 0592 { 0593 e->ignore(); 0594 } 0595 0596 break; 0597 } 0598 0599 case Qt::Key_Control: 0600 { 0601 if (d->wheelAction == GLViewerWidget::Private::zoomImage) 0602 { 0603 d->wheelAction = GLViewerWidget::Private::changeImage; 0604 } 0605 else 0606 { 0607 d->wheelAction = GLViewerWidget::Private::zoomImage; 0608 unsetCursor(); 0609 d->timerMouseMove.start(2000); 0610 } 0611 0612 break; 0613 } 0614 0615 default: 0616 { 0617 e->ignore(); 0618 break; 0619 } 0620 } 0621 } 0622 0623 /** 0624 * download texture to video memory 0625 */ 0626 void GLViewerWidget::downloadTexture(GLViewerTexture* const tex) 0627 { 0628 glBindTexture(GL_TEXTURE_RECTANGLE_NV, tex->textureId()); 0629 // glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB); 0630 // glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB); 0631 0632 // uncomment the following line to enable flat shading of texels -> debugging 0633 // glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER,GL_NEAREST); 0634 0635 // glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA, tex->width(), tex->height(), 0, 0636 // GL_RGBA, GL_UNSIGNED_BYTE, tex->data()); 0637 } 0638 0639 /** 0640 * load d->files[file_index] into a texture object if it is not already cached 0641 * \param file_index index to QStringList d->files 0642 */ 0643 GLViewerTexture* GLViewerWidget::loadImage(int file_index) const 0644 { 0645 int imod = file_index % CACHESIZE; //index for cache 0646 0647 if (d->cache[imod].file_index == file_index) 0648 { 0649 // image is already cached 0650 0651 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "image" << file_index << "is already in cache@" << imod; 0652 0653 return d->cache[imod].texture; 0654 } 0655 else 0656 { 0657 // image is net yet loaded 0658 0659 QString f = d->files[file_index]; 0660 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "loading image" << f << "(idx=" << file_index << ") to cache@" << imod; 0661 d->cache[imod].file_index = file_index; 0662 0663 // when loadImage is called the first time, the frame is not yet fullscreen 0664 0665 QSize size = d->firstImage ? d->screenSize : QSize(width(), height()); 0666 0667 // handle non-loadable images 0668 0669 if (!d->cache[imod].texture->load(f, size)) 0670 { 0671 d->cache[imod].texture->load(d->nullImage.toImage()); 0672 } 0673 0674 d->cache[imod].texture->setViewport(size.width(), size.height()); 0675 0676 return d->cache[imod].texture; 0677 } 0678 } 0679 0680 void GLViewerWidget::wheelEvent(QWheelEvent* e) 0681 { 0682 switch (d->wheelAction) 0683 { 0684 // mousewheel triggers zoom 0685 0686 case GLViewerWidget::Private::zoomImage: 0687 { 0688 setCursor(d->zoomCursor); 0689 zoom(e->angleDelta().y(), e->position().toPoint(), d->zoomfactor_scrollwheel); 0690 0691 break; 0692 } 0693 0694 // mousewheel triggers image change 0695 0696 case GLViewerWidget::Private::changeImage: 0697 { 0698 if (e->angleDelta().y() < 0) 0699 { 0700 nextImage(); 0701 } 0702 else if (e->angleDelta().y() > 0) 0703 { 0704 prevImage(); 0705 } 0706 0707 break; 0708 } 0709 } 0710 } 0711 0712 void GLViewerWidget::mousePressEvent(QMouseEvent* e) 0713 { 0714 // begin zoom 0715 // scale down d->texture for fast zooming 0716 // d->texture will be set to original size on mouse up 0717 0718 if (d->texture && d->texture->setNewSize(d->zoomsize)) 0719 { 0720 // load downsampled image 0721 0722 downloadTexture(d->texture); 0723 } 0724 0725 d->timerMouseMove.stop(); // user is something up to, therefore keep the cursor 0726 0727 if (e->button() == Qt::LeftButton) 0728 { 0729 setCursor(d->moveCursor); 0730 } 0731 0732 if (e->button() == Qt::RightButton) 0733 { 0734 setCursor(d->zoomCursor); 0735 } 0736 0737 d->startdrag = e->pos(); 0738 d->previous_pos = e->pos(); 0739 } 0740 0741 void GLViewerWidget::mouseMoveEvent(QMouseEvent* e) 0742 { 0743 if (e->buttons() == Qt::LeftButton) 0744 { 0745 // panning 0746 0747 setCursor(d->moveCursor); 0748 QPoint diff = e->pos()-d->startdrag; 0749 d->texture->move(diff); 0750 update(); 0751 d->startdrag = e->pos(); 0752 } 0753 else if (e->buttons() == Qt::RightButton) 0754 { 0755 int mdelta = 0; 0756 0757 // zooming 0758 // 0759 // if mouse pointer reached upper or lower boder, special treatment in order 0760 // to keep zooming enabled in that special case 0761 0762 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0763 0764 if (d->previous_pos.y() == e->position().toPoint().y()) 0765 { 0766 if (e->position().toPoint().y() == 0) 0767 0768 #else 0769 0770 if (d->previous_pos.y() == e->y()) 0771 { 0772 if (e->y() == 0) 0773 0774 #endif 0775 0776 { 0777 // mouse pointer is at upper edge, therefore assume zoom in 0778 0779 mdelta = 1; 0780 } 0781 else 0782 { 0783 // mouse pointer is as lower edge, therefore assume zoom out 0784 0785 mdelta = -1; 0786 } 0787 } 0788 else 0789 { 0790 // mouse pointer is in the middle of the screen, normal operation 0791 0792 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0793 0794 mdelta = d->previous_pos.y() - e->position().toPoint().y(); 0795 0796 #else 0797 0798 mdelta = d->previous_pos.y() - e->y(); 0799 0800 #endif 0801 0802 } 0803 0804 setCursor(d->zoomCursor); 0805 zoom(mdelta, d->startdrag, d->zoomfactor_mousemove); 0806 d->previous_pos = e->pos(); 0807 } 0808 else 0809 { 0810 // no key is pressed while moving mouse 0811 // don't do anything if ctrl is pressed 0812 0813 if (d->timerMouseMove.isActive()) 0814 { 0815 // ctrl is not pressed, no zooming, therefore restore and hide cursor in 2 sec 0816 0817 unsetCursor(); 0818 d->timerMouseMove.start(2000); 0819 } 0820 } 0821 0822 return; 0823 } 0824 0825 void GLViewerWidget::mouseReleaseEvent(QMouseEvent*) 0826 { 0827 d->timerMouseMove.start(2000); 0828 unsetCursor(); 0829 0830 if (d->texture && d->texture->setNewSize(QSize(0, 0))) 0831 { 0832 // load full resolution image 0833 0834 downloadTexture(d->texture); 0835 } 0836 0837 update(); 0838 } 0839 0840 /** 0841 * a double click resets the view (zoom and move) 0842 */ 0843 void GLViewerWidget::mouseDoubleClickEvent(QMouseEvent*) 0844 { 0845 d->texture->reset(true); 0846 update(); 0847 } 0848 0849 void GLViewerWidget::prevImage() 0850 { 0851 0852 #ifdef PERFORMANCE_ANALYSIS 0853 0854 GLViewerTimer timer; 0855 0856 #endif 0857 0858 if (d->file_idx > 0) 0859 { 0860 d->file_idx--; 0861 } 0862 else 0863 { 0864 return; 0865 } 0866 0867 #ifdef PERFORMANCE_ANALYSIS 0868 0869 timer.start(); 0870 0871 #endif 0872 0873 d->texture = loadImage(d->file_idx); 0874 d->texture->reset(true); 0875 0876 #ifdef PERFORMANCE_ANALYSIS 0877 0878 timer.at("loadImage"); 0879 0880 #endif 0881 0882 downloadTexture(d->texture); 0883 0884 #ifdef PERFORMANCE_ANALYSIS 0885 0886 timer.at("downloadTexture"); 0887 0888 #endif 0889 0890 update(); 0891 0892 #ifdef PERFORMANCE_ANALYSIS 0893 0894 timer.at("update"); 0895 0896 #endif 0897 0898 //image preloading 0899 0900 if (d->file_idx > 0) 0901 { 0902 loadImage(d->file_idx - 1); 0903 } 0904 } 0905 0906 void GLViewerWidget::nextImage() 0907 { 0908 #ifdef PERFORMANCE_ANALYSIS 0909 0910 GLViewerTimer timer; 0911 0912 #endif 0913 0914 if (d->file_idx < (unsigned int)(d->files.count() - 1)) 0915 { 0916 d->file_idx++; 0917 } 0918 else 0919 { 0920 return; 0921 } 0922 0923 #ifdef PERFORMANCE_ANALYSIS 0924 0925 timer.start(); 0926 0927 #endif 0928 0929 d->texture = loadImage(d->file_idx); 0930 d->texture->reset(true); 0931 0932 #ifdef PERFORMANCE_ANALYSIS 0933 0934 timer.at("loadImage"); 0935 0936 #endif 0937 0938 downloadTexture(d->texture); 0939 0940 #ifdef PERFORMANCE_ANALYSIS 0941 0942 timer.at("downloadTexture"); 0943 0944 #endif 0945 0946 update(); 0947 0948 #ifdef PERFORMANCE_ANALYSIS 0949 0950 timer.at("updateGL"); 0951 0952 #endif 0953 0954 //image preloading 0955 if (d->file_idx < ((unsigned int)d->files.count() - 1)) 0956 { 0957 loadImage(d->file_idx + 1); 0958 0959 #ifdef PERFORMANCE_ANALYSIS 0960 0961 timer.at("preloading"); 0962 0963 #endif 0964 0965 } 0966 } 0967 0968 /** 0969 * \param mdelta delta of mouse movement: 0970 * mdelta>0: zoom in 0971 * mdelta<0: zoom out 0972 * mdelta=0: do nothing 0973 * \param pos position of mouse 0974 * \param factor zoom factor:scrollwheel needs a higher factor that right click mouse move. factor=1 -> no zoom 0975 */ 0976 void GLViewerWidget::zoom(int mdelta, const QPoint& pos, float factor) 0977 { 0978 if (mdelta == 0) 0979 { 0980 // do nothing 0981 return; 0982 } 0983 0984 if (mdelta > 0) 0985 { 0986 // multiplicator for zooming in 0987 d->delta = factor; 0988 } 0989 0990 if (mdelta < 0) 0991 { 0992 // multiplicator for zooming out 0993 d->delta = 2.0 - factor; 0994 } 0995 0996 d->texture->zoom(d->delta, pos); 0997 update(); 0998 } 0999 1000 /** 1001 * being called if user didn't move the mouse for longer than 2 sec 1002 */ 1003 void GLViewerWidget::slotTimeoutMouseMove() 1004 { 1005 setCursor(Qt::BlankCursor); 1006 } 1007 1008 /** 1009 * check if OpenGL engine is ready. This function is called from outside the widget. 1010 * If OpenGL doesn't work correctly, the widget can be destroyed 1011 * \return OGLstate::oglNoContext No OpenGl context could be retrieved 1012 * \return OGLstate::oglNoRectangularTexture GLGL_ARB_texture_rectangle is not supported 1013 * \return OGLstate::oglOK all is fine 1014 */ 1015 OGLstate GLViewerWidget::getOGLstate() const 1016 { 1017 // No OpenGL context is found. Are the drivers ok? 1018 1019 if (!isValid()) 1020 { 1021 return oglNoContext; 1022 } 1023 1024 // GL_ARB_texture_rectangle is not supported 1025 1026 QString s = QString::fromLatin1(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))); 1027 1028 if (!s.contains(QString::fromLatin1("GL_ARB_texture_rectangle"), Qt::CaseInsensitive)) 1029 { 1030 return oglNoRectangularTexture; 1031 } 1032 1033 // Everything is ok! 1034 1035 return oglOK; 1036 } 1037 1038 /** 1039 * QGLWidget::isFullscreen() returns true if the internal state is already true 1040 * but the actually displayed size is still windowed. isReallyFullscreen() returns the 1041 * value of the visible size. 1042 * \return true if (screenwidth == widgedwidth) 1043 */ 1044 bool GLViewerWidget::isReallyFullScreen() const 1045 { 1046 return (width() == d->screenSize.width()); 1047 } 1048 1049 } // namespace DigikamGenericGLViewerPlugin 1050 1051 #include "moc_glviewerwidget.cpp"