diff --git a/core/dplugins/generic/view/presentation/opengl/presentationgl.cpp b/core/dplugins/generic/view/presentation/opengl/presentationgl.cpp index 6d243d0ca4..994a584fea 100644 --- a/core/dplugins/generic/view/presentation/opengl/presentationgl.cpp +++ b/core/dplugins/generic/view/presentation/opengl/presentationgl.cpp @@ -1,1856 +1,1857 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-01-19 * Description : a presentation tool. * * Copyright (C) 2004 by Renchi Raju * Copyright (C) 2006-2009 by Valerio Fuoglio * Copyright (C) 2009 by Andi Clemens * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "presentationgl.h" // C++ includes #include #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "presentationcontainer.h" #include "presentationctrlwidget.h" #include "presentationloader.h" #ifdef HAVE_MEDIAPLAYER # include "presentationaudiowidget.h" #endif using namespace Digikam; namespace DigikamGenericPresentationPlugin { class Q_DECL_HIDDEN PresentationGL::Private { public: explicit Private() : timer(nullptr), fileIndex(0), imageLoader(nullptr), tex1First(true), curr(0), width(0), height(0), xMargin(0), yMargin(0), effect(nullptr), effectRunning(false), timeout(0), random(false), endOfShow(false), i(0), dir(0), slideCtrlWidget(nullptr), #ifdef HAVE_MEDIAPLAYER playbackWidget(nullptr), #endif mouseMoveTimer(nullptr), deskX(0), deskY(0), deskWidth(0), deskHeight(0), sharedData(nullptr) { texture[0] = nullptr; texture[1] = nullptr; texture[2] = nullptr; } QMap effects; QTimer* timer; int fileIndex; PresentationLoader* imageLoader; QOpenGLTexture* texture[3]; bool tex1First; int curr; int width; int height; int xMargin; int yMargin; EffectMethod effect; bool effectRunning; int timeout; bool random; bool endOfShow; int i; int dir; float points[40][40][3] = {{{0.0}}}; PresentationCtrlWidget* slideCtrlWidget; #ifdef HAVE_MEDIAPLAYER PresentationAudioWidget* playbackWidget; #endif QTimer* mouseMoveTimer; int deskX; int deskY; int deskWidth; int deskHeight; PresentationContainer* sharedData; }; PresentationGL::PresentationGL(PresentationContainer* const sharedData) : QOpenGLWidget(), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); - setContextMenuPolicy(Qt::PreventContextMenu); - setWindowState(windowState() | Qt::WindowFullScreen); QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = qApp->activeWindow()) { if (QWindow* const window = widget->windowHandle()) { screen = window->screen(); } } QRect deskRect = screen->geometry(); d->deskX = deskRect.x(); d->deskY = deskRect.y(); d->deskWidth = deskRect.width(); d->deskHeight = deskRect.height(); move(d->deskX, d->deskY); resize(d->deskWidth, d->deskHeight); + setContextMenuPolicy(Qt::PreventContextMenu); + setWindowState(windowState() | Qt::WindowFullScreen); + d->sharedData = sharedData; d->slideCtrlWidget = new PresentationCtrlWidget(this); d->slideCtrlWidget->hide(); if (!d->sharedData->loop) { d->slideCtrlWidget->setEnabledPrev(false); } connect(d->slideCtrlWidget, SIGNAL(signalPause()), this, SLOT(slotPause())); connect(d->slideCtrlWidget, SIGNAL(signalPlay()), this, SLOT(slotPlay())); connect(d->slideCtrlWidget, SIGNAL(signalNext()), this, SLOT(slotNext())); connect(d->slideCtrlWidget, SIGNAL(signalPrev()), this, SLOT(slotPrev())); connect(d->slideCtrlWidget, SIGNAL(signalClose()), this, SLOT(slotClose())); #ifdef HAVE_MEDIAPLAYER d->playbackWidget = new PresentationAudioWidget(this, d->sharedData->soundtrackUrls, d->sharedData); d->playbackWidget->hide(); d->playbackWidget->move(d->deskX, d->deskY); #endif int w = d->slideCtrlWidget->width(); d->slideCtrlWidget->move(d->deskX + d->deskWidth - w - 1, d->deskY); // -- Minimal texture size (opengl specs) -------------- d->width = 64; d->height = 64; // -- Margin ------------------------------------------- d->xMargin = int (d->deskWidth / d->width); d->yMargin = int (d->deskWidth / d->height); // ------------------------------------------------------------------ d->fileIndex = -1; // start with -1 d->timeout = d->sharedData->delay; d->imageLoader = new PresentationLoader(d->sharedData, width(), height(), d->fileIndex); // -------------------------------------------------- registerEffects(); if (d->sharedData->effectNameGL == QLatin1String("Random")) { d->effect = getRandomEffect(); d->random = true; } else { d->effect = d->effects[d->sharedData->effectNameGL]; if (!d->effect) { d->effect = d->effects[QLatin1String("None")]; } d->random = false; } // -------------------------------------------------- d->timer = new QTimer(this); connect(d->timer, SIGNAL(timeout()), this, SLOT(slotTimeOut())); d->timer->setSingleShot(true); d->timer->start(10); // -- hide cursor when not moved -------------------- d->mouseMoveTimer = new QTimer(this); d->mouseMoveTimer->setSingleShot(true); connect(d->mouseMoveTimer, SIGNAL(timeout()), this, SLOT(slotMouseMoveTimeOut())); setMouseTracking(true); slotMouseMoveTimeOut(); #ifdef HAVE_MEDIAPLAYER if (d->sharedData->soundtrackPlay) { d->playbackWidget->slotPlay(); } #endif } PresentationGL::~PresentationGL() { #ifdef HAVE_MEDIAPLAYER d->playbackWidget->slotStop(); #endif d->timer->stop(); d->mouseMoveTimer->stop(); d->texture[0]->destroy(); d->texture[1]->destroy(); d->texture[2]->destroy(); delete d->texture[0]; delete d->texture[1]; delete d->texture[2]; delete d->imageLoader; delete d; } void PresentationGL::initializeGL() { // Enable Texture Mapping glEnable(GL_TEXTURE_2D); // Clear The Background Color glClearColor(0.0, 0.0, 0.0, 1.0f); // Turn Blending On glEnable(GL_BLEND); // Blending Function For Translucency Based On Source Alpha Value glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Enable perspective vision glClearDepth(1.0f); // get the maximum texture value. GLint maxTexVal; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexVal); // allow only maximum texture value of 1024. anything bigger and things slow down maxTexVal = qMin(1024, maxTexVal); d->width = d->deskWidth; d->height = d->deskHeight; d->width = 1 << (int)ceil(log((float)d->width) / log((float)2)) ; d->height = 1 << (int)ceil(log((float)d->height) / log((float)2)); d->width = qMin(maxTexVal, d->width); d->height = qMin(maxTexVal, d->height); d->texture[0] = new QOpenGLTexture(QOpenGLTexture::Target2D); d->texture[1] = new QOpenGLTexture(QOpenGLTexture::Target2D); d->texture[2] = new QOpenGLTexture(QOpenGLTexture::Target2D); // end screen texture } void PresentationGL::paintGL() { glDisable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (d->endOfShow) { showEndOfShow(); } else { if (d->effectRunning && d->effect) { (this->*d->effect)(); } else { paintTexture(); } } } void PresentationGL::resizeGL(int w, int h) { // Reset The Current Viewport And Perspective Transformation glViewport(0, 0, (GLint)w, (GLint)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); } void PresentationGL::keyPressEvent(QKeyEvent* event) { if (!event) { return; } d->slideCtrlWidget->keyPressEvent(event); #ifdef HAVE_MEDIAPLAYER d->playbackWidget->keyPressEvent(event); #endif } void PresentationGL::mousePressEvent(QMouseEvent* e) { if (d->endOfShow) { slotClose(); } if (e->button() == Qt::LeftButton) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotNext(); } else if ((e->button() == Qt::RightButton) && ((d->fileIndex - 1) >= 0)) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotPrev(); } } void PresentationGL::mouseMoveEvent(QMouseEvent* e) { setCursor(QCursor(Qt::ArrowCursor)); d->mouseMoveTimer->start(1000); if (!d->slideCtrlWidget->canHide() #ifdef HAVE_MEDIAPLAYER || !d->playbackWidget->canHide() #endif ) { return; } QPoint pos(e->pos()); if ((pos.y() > (d->deskY + 20)) && (pos.y() < (d->deskY + d->deskHeight - 20 - 1))) { if (d->slideCtrlWidget->isHidden() #ifdef HAVE_MEDIAPLAYER || d->playbackWidget->isHidden() #endif ) { return; } else { d->slideCtrlWidget->hide(); #ifdef HAVE_MEDIAPLAYER d->playbackWidget->hide(); #endif setFocus(); } return; } d->slideCtrlWidget->show(); #ifdef HAVE_MEDIAPLAYER d->playbackWidget->show(); #endif } void PresentationGL::wheelEvent(QWheelEvent* e) { if (!d->sharedData->enableMouseWheel) { return; } if (d->endOfShow) { slotClose(); } int delta = e->angleDelta().y(); if (delta < 0) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotNext(); } else if ((delta > 0) && ((d->fileIndex - 1) >= 0)) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotPrev(); } } void PresentationGL::registerEffects() { d->effects.insert(QLatin1String("None"), &PresentationGL::effectNone); d->effects.insert(QLatin1String("Blend"), &PresentationGL::effectBlend); d->effects.insert(QLatin1String("Fade"), &PresentationGL::effectFade); d->effects.insert(QLatin1String("Rotate"), &PresentationGL::effectRotate); d->effects.insert(QLatin1String("Bend"), &PresentationGL::effectBend); d->effects.insert(QLatin1String("In Out"), &PresentationGL::effectInOut); d->effects.insert(QLatin1String("Slide"), &PresentationGL::effectSlide); d->effects.insert(QLatin1String("Flutter"), &PresentationGL::effectFlutter); d->effects.insert(QLatin1String("Cube"), &PresentationGL::effectCube); } QStringList PresentationGL::effectNames() { QStringList effects; effects.append(QLatin1String("None")); effects.append(QLatin1String("Bend")); effects.append(QLatin1String("Blend")); effects.append(QLatin1String("Cube")); effects.append(QLatin1String("Fade")); effects.append(QLatin1String("Flutter")); effects.append(QLatin1String("In Out")); effects.append(QLatin1String("Rotate")); effects.append(QLatin1String("Slide")); effects.append(QLatin1String("Random")); return effects; } QMap PresentationGL::effectNamesI18N() { QMap effects; effects[QLatin1String("None")] = i18nc("Filter Effect: No effect", "None"); effects[QLatin1String("Bend")] = i18nc("Filter Effect: Bend", "Bend"); effects[QLatin1String("Blend")] = i18nc("Filter Effect: Blend", "Blend"); effects[QLatin1String("Cube")] = i18nc("Filter Effect: Cube", "Cube"); effects[QLatin1String("Fade")] = i18nc("Filter Effect: Fade", "Fade"); effects[QLatin1String("Flutter")] = i18nc("Filter Effect: Flutter", "Flutter"); effects[QLatin1String("In Out")] = i18nc("Filter Effect: In Out", "In Out"); effects[QLatin1String("Rotate")] = i18nc("Filter Effect: Rotate", "Rotate"); effects[QLatin1String("Slide")] = i18nc("Filter Effect: Slide", "Slide"); effects[QLatin1String("Random")] = i18nc("Filter Effect: Random effect", "Random"); return effects; } PresentationGL::EffectMethod PresentationGL::getRandomEffect() { QMap tmpMap(d->effects); tmpMap.remove(QLatin1String("None")); QStringList t = tmpMap.keys(); int count = t.count(); int i = (int)((float)(count) * qrand() / (RAND_MAX + 1.0)); QString key = t[i]; return tmpMap[key]; } void PresentationGL::advanceFrame() { d->fileIndex++; d->imageLoader->next(); int num = d->sharedData->urlList.count(); if (d->fileIndex >= num) { if (d->sharedData->loop) { d->fileIndex = 0; } else { d->fileIndex = num - 1; d->endOfShow = true; d->slideCtrlWidget->setEnabledPlay(false); d->slideCtrlWidget->setEnabledNext(false); d->slideCtrlWidget->setEnabledPrev(false); } } if (!d->sharedData->loop && !d->endOfShow) { d->slideCtrlWidget->setEnabledPrev(d->fileIndex > 0); d->slideCtrlWidget->setEnabledNext(d->fileIndex < num - 1); } d->tex1First = !d->tex1First; d->curr = (d->curr == 0) ? 1 : 0; } void PresentationGL::previousFrame() { d->fileIndex--; d->imageLoader->prev(); int num = d->sharedData->urlList.count(); if (d->fileIndex < 0) { if (d->sharedData->loop) { d->fileIndex = num - 1; } else { d->fileIndex = 0; d->endOfShow = true; d->slideCtrlWidget->setEnabledPlay(false); d->slideCtrlWidget->setEnabledNext(false); d->slideCtrlWidget->setEnabledPrev(false); } } if (!d->sharedData->loop && !d->endOfShow) { d->slideCtrlWidget->setEnabledPrev(d->fileIndex > 0); d->slideCtrlWidget->setEnabledNext(d->fileIndex < (num - 1)); } d->tex1First = !d->tex1First; d->curr = (d->curr == 0) ? 1 : 0; } void PresentationGL::loadImage() { QImage image = d->imageLoader->getCurrent(); int a = d->tex1First ? 0 : 1; if (!image.isNull()) { QImage black(width(), height(), QImage::Format_RGB32); black.fill(QColor(0, 0, 0).rgb()); montage(image, black); if (!d->sharedData->openGlFullScale) { black = black.scaled(d->width, d->height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } if (d->sharedData->printFileName) { printFilename(black); } if (d->sharedData->printProgress) { printProgress(black); } if (d->sharedData->printFileComments) { printComments(black); } /* create the texture */ d->texture[a]->destroy(); d->texture[a]->setData(black.mirrored()); d->texture[a]->setMinificationFilter(QOpenGLTexture::Linear); d->texture[a]->setMagnificationFilter(QOpenGLTexture::Linear); d->texture[a]->bind(); } } void PresentationGL::montage(QImage& top, QImage& bot) { int tw = top.width(); int th = top.height(); int bw = bot.width(); int bh = bot.height(); if ((tw > bw) || (th > bh)) { qFatal("Top Image should be smaller or same size as Bottom Image"); } if (top.depth() != 32) { top = top.convertToFormat(QImage::Format_RGB32); } if (bot.depth() != 32) { bot = bot.convertToFormat(QImage::Format_RGB32); } int sw = bw / 2 - tw / 2; // int ew = bw/2 + tw/2; int sh = bh / 2 - th / 2; int eh = bh / 2 + th / 2; unsigned int* tdata = reinterpret_cast(top.scanLine(0)); unsigned int* bdata = nullptr; for (int y = sh ; y < eh ; ++y) { bdata = reinterpret_cast(bot.scanLine(y)) + sw; for (int x = 0 ; x < tw ; ++x) { *(bdata++) = *(tdata++); } } } void PresentationGL::printFilename(QImage& layer) { QFileInfo fileinfo(d->sharedData->urlList[d->fileIndex].toLocalFile()); QString filename = fileinfo.fileName(); QPixmap pix = generateOutlinedTextPixmap(filename); // -------------------------------------------------------- QPainter painter; painter.begin(&layer); painter.drawPixmap(d->xMargin, layer.height() - d->yMargin - pix.height(), pix); painter.end(); } void PresentationGL::printProgress(QImage& layer) { QString progress(QString::number(d->fileIndex + 1) + QLatin1Char('/') + QString::number(d->sharedData->urlList.count())); QPixmap pix = generateOutlinedTextPixmap(progress); QPainter painter; painter.begin(&layer); painter.drawPixmap(layer.width() - d->xMargin - pix.width(), d->yMargin, pix); painter.end(); } void PresentationGL::printComments(QImage& layer) { DItemInfo info(d->sharedData->iface->itemInfo(d->imageLoader->currPath())); QString comments = info.comment(); int yPos = 5; // Text Y coordinate if (d->sharedData->printFileName) { yPos += 20; } QStringList commentsByLines; uint commentsIndex = 0; // Comments QString index while (commentsIndex < (uint) comments.length()) { QString newLine; bool breakLine = false; // End Of Line found uint currIndex; // Comments QString current index // Check miminal lines dimension int commentsLinesLengthLocal = d->sharedData->commentsLinesLength; for (currIndex = commentsIndex ; (currIndex < (uint)comments.length()) && !breakLine ; ++currIndex) { if (comments[currIndex] == QLatin1Char('\n') || comments[currIndex].isSpace()) { breakLine = true; } } if (commentsLinesLengthLocal <= (int)((currIndex - commentsIndex))) { commentsLinesLengthLocal = (currIndex - commentsIndex); } breakLine = false; for (currIndex = commentsIndex ; (currIndex <= (commentsIndex + commentsLinesLengthLocal)) && (currIndex < (uint)comments.length()) && !breakLine ; ++currIndex ) { breakLine = (comments[currIndex] == QLatin1Char('\n')) ? true : false; if (breakLine) { newLine.append(QLatin1Char(' ')); } else { newLine.append(comments[currIndex]); } } commentsIndex = currIndex; // The line is ended if (commentsIndex != (uint) comments.length()) { while (!newLine.endsWith(QLatin1Char(' '))) { newLine.truncate(newLine.length() - 1); commentsIndex--; } } commentsByLines.prepend(newLine.trimmed()); } yPos += int(2.0 * d->sharedData->captionFont->pointSize()); QFont font(*d->sharedData->captionFont); QColor fgColor(d->sharedData->commentsFontColor); QColor bgColor(d->sharedData->commentsBgColor); bool drawTextOutline = d->sharedData->commentsDrawOutline; int opacity = d->sharedData->bgOpacity; for (int lineNumber = 0 ; lineNumber < (int)commentsByLines.count() ; ++lineNumber) { QPixmap pix = generateCustomOutlinedTextPixmap(commentsByLines[lineNumber], font, fgColor, bgColor, opacity, drawTextOutline); QPainter painter; painter.begin(&layer); int xPos = (layer.width() / 2) - (pix.width() / 2); painter.drawPixmap(xPos, layer.height() - pix.height() - yPos, pix); painter.end(); yPos += int(pix.height() + d->height / 400); } } void PresentationGL::showEndOfShow() { QPixmap pix(width(), height()); pix.fill(Qt::black); QFont fn(font()); fn.setPointSize(fn.pointSize() + 10); fn.setBold(true); QPainter p(&pix); p.setPen(Qt::white); p.setFont(fn); p.drawText(20, 50, i18n("Slideshow Completed")); p.drawText(20, 100, i18n("Click to Exit...")); // QPixmap logoPixmap = KPSvgPixmapRenderer(width() / 6, width() / 6).getPixmap(); // p.drawPixmap(width()-(width()/12)-logoPixmap.width(), // height()-(height()/12)-logoPixmap.height(), // logoPixmap); p.end(); QImage image(pix.toImage()); /* create the texture */ d->texture[2]->destroy(); d->texture[2]->setData(image.mirrored()); d->texture[2]->setMinificationFilter(QOpenGLTexture::Linear); d->texture[2]->setMagnificationFilter(QOpenGLTexture::Linear); d->texture[2]->bind(); /* paint the texture */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0, -1.0, 0); glTexCoord2f(1, 0); glVertex3f(1.0, -1.0, 0); glTexCoord2f(1, 1); glVertex3f(1.0, 1.0, 0); glTexCoord2f(0, 1); glVertex3f(-1.0, 1.0, 0); } glEnd(); } void PresentationGL::slotTimeOut() { if (!d->effect) { qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "PresentationGL: No transition method"; d->effect = &PresentationGL::effectNone; } if (d->effectRunning) { d->timeout = 10; } else { if (d->timeout == 0) { // effect was running and is complete now // run timer while showing current image d->timeout = d->sharedData->delay; d->i = 0; } else { // timed out after showing current image // load next image and start effect if (d->random) { d->effect = getRandomEffect(); } advanceFrame(); if (d->endOfShow) { update(); return; } loadImage(); d->timeout = 10; d->effectRunning = true; d->i = 0; } } update(); d->timer->start(d->timeout); } void PresentationGL::slotMouseMoveTimeOut() { QPoint pos(QCursor::pos()); if ((pos.y() < (d->deskY + 20)) || (pos.y() > (d->deskY + d->deskHeight - 20 - 1)) || d->slideCtrlWidget->underMouse() #ifdef HAVE_MEDIAPLAYER || d->playbackWidget->underMouse() #endif ) { return; } setCursor(QCursor(Qt::BlankCursor)); } void PresentationGL::paintTexture() { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLuint tex = d->texture[d->curr]->textureId(); glBindTexture(GL_TEXTURE_2D, tex); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0, -1.0, 0); glTexCoord2f(1, 0); glVertex3f(1.0, -1.0, 0); glTexCoord2f(1, 1); glVertex3f(1.0, 1.0, 0); glTexCoord2f(0, 1); glVertex3f(-1.0, 1.0, 0); } glEnd(); } void PresentationGL::effectNone() { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } void PresentationGL::effectBlend() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0 / (100.0)*(float)d->i); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectFade() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } int a; float opacity; if (d->i <= 50) { a = (d->curr == 0) ? 1 : 0; opacity = 1.0 - 1.0 / 50.0 * (float)(d->i); } else { opacity = 1.0 / 50.0 * (float)(d->i - 50.0); a = d->curr; } GLuint ta = d->texture[a]->textureId(); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, opacity); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectRotate() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } if (d->i == 0) { d->dir = (int)((2.0 * qrand() / (RAND_MAX + 1.0))); } int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float rotate = 360.0 / 100.0 * (float)d->i; glRotatef( ((d->dir == 0) ? -1 : 1) * rotate, 0.0, 0.0, 1.0); float scale = 1.0 / 100.0 * (100.0 - (float)(d->i)); glScalef(scale, scale, 1.0); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectBend() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } if (d->i == 0) { d->dir = (int)((2.0 * qrand() / (RAND_MAX + 1.0))); } int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(90.0 / 100.0*(float)d->i, (d->dir == 0) ? 1.0 : 0.0, (d->dir == 1) ? 1.0 : 0.0, 0.0); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectInOut() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } if (d->i == 0) { d->dir = 1 + (int) ((4.0 * qrand() / (RAND_MAX + 1.0))); } int a; bool out; if (d->i <= 50) { a = (d->curr == 0) ? 1 : 0; out = 1; } else { a = d->curr; out = 0; } GLuint ta = d->texture[a]->textureId(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float t = out ? 1.0 / 50.0 * (50.0 - d->i) : 1.0 / 50.0 * (d->i - 50.0); glScalef(t, t, 1.0); t = 1.0 - t; glTranslatef((d->dir % 2 == 0) ? ((d->dir == 2) ? 1 : -1) * t : 0.0, (d->dir % 2 == 1) ? ((d->dir == 1) ? 1 : -1) * t : 0.0, 0.0); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectSlide() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } if (d->i == 0) { d->dir = 1 + (int)((4.0 * qrand() / (RAND_MAX + 1.0))); } int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float trans = 2.0 / 100.0 * (float)d->i; glTranslatef((d->dir % 2 == 0) ? ((d->dir == 2) ? 1 : -1) * trans : 0.0, (d->dir % 2 == 1) ? ((d->dir == 1) ? 1 : -1) * trans : 0.0, 0.0); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); d->i++; } void PresentationGL::effectFlutter() { if (d->i > 100) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); if (d->i == 0) { for (int x = 0 ; x < 40 ; ++x) { for (int y = 0 ; y < 40 ; ++y) { d->points[x][y][0] = (float) (x / 20.0f - 1.0f); d->points[x][y][1] = (float) (y / 20.0f - 1.0f); d->points[x][y][2] = (float) sin((x / 20.0f - 1.0f) * 3.141592654 * 2.0f) / 5.0; } } } glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0f, -1.0f, 0); glTexCoord2f(1, 0); glVertex3f(1.0f, -1.0f, 0); glTexCoord2f(1, 1); glVertex3f(1.0f, 1.0f, 0); glTexCoord2f(0, 1); glVertex3f(-1.0f, 1.0f, 0); } glEnd(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float rotate = 60.0 / 100.0 * (float)d->i; glRotatef(rotate, 1.0f, 0.0f, 0.0f); float scale = 1.0 / 100.0 * (100.0 - (float)d->i); glScalef(scale, scale, scale); glTranslatef(1.0 / 100.0*(float)d->i, 1.0 / 100.0*(float)d->i, 0.0); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); float float_x, float_y, float_xb, float_yb; int x, y; for (x = 0 ; x < 39 ; ++x) { for (y = 0 ; y < 39 ; ++y) { float_x = (float) x / 40.0f; float_y = (float) y / 40.0f; float_xb = (float) (x + 1) / 40.0f; float_yb = (float) (y + 1) / 40.0f; glTexCoord2f(float_x, float_y); glVertex3f(d->points[x][y][0], d->points[x][y][1], d->points[x][y][2]); glTexCoord2f(float_x, float_yb); glVertex3f(d->points[x][y + 1][0], d->points[x][y + 1][1], d->points[x][y + 1][2]); glTexCoord2f(float_xb, float_yb); glVertex3f(d->points[x + 1][y + 1][0], d->points[x + 1][y + 1][1], d->points[x + 1][y + 1][2]); glTexCoord2f(float_xb, float_y); glVertex3f(d->points[x + 1][y][0], d->points[x + 1][y][1], d->points[x + 1][y][2]); } } } glEnd(); // wave every two iterations if ((d->i % 2) == 0) { float hold; int x, y; for (y = 0 ; y < 40 ; ++y) { hold = d->points[0][y][2]; for (x = 0 ; x < 39 ; ++x) { d->points[x][y][2] = d->points[x + 1][y][2]; } d->points[39][y][2] = hold; } } d->i++; } void PresentationGL::effectCube() { int tot = 200; int rotStart = 50; if (d->i > tot) { paintTexture(); d->effectRunning = false; d->timeout = 0; return; } // Enable perspective vision glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); int a = (d->curr == 0) ? 1 : 0; int b = d->curr; GLuint ta = d->texture[a]->textureId(); GLuint tb = d->texture[b]->textureId(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // float PI = 4.0 * atan(1.0); float znear = 3.0; // float theta = 2.0 * atan2((float)2.0 / (float)2.0, (float)znear); // theta = theta * 180.0 / PI; glFrustum(-1.0, 1.0, -1.0, 1.0, znear - 0.01, 10.0); static float xrot; static float yrot; // static float zrot; if (d->i == 0) { xrot = 0.0; yrot = 0.0; // zrot = 0.0; } glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); float trans = 5.0 * (float)((d->i <= tot / 2) ? d->i : tot - d->i) / (float)tot; glTranslatef(0.0, 0.0, -znear - 1.0 - trans); glRotatef(xrot, 1.0f, 0.0f, 0.0f); glRotatef(yrot, 0.0f, 1.0f, 0.0f); glBindTexture(GL_TEXTURE_2D, 0); glBegin(GL_QUADS); { glColor4f(0.0f, 0.0f, 0.0f, 1.0f); /* Front Face */ glVertex3f( -1.00f, -1.00f, 0.99f ); glVertex3f( 1.00f, -1.00f, 0.99f ); glVertex3f( 1.00f, 1.00f, 0.99f ); glVertex3f( -1.00f, 1.00f, 0.99f ); /* Back Face */ glVertex3f( -1.00f, -1.00f, -0.99f ); glVertex3f( -1.00f, 1.00f, -0.99f ); glVertex3f( 1.00f, 1.00f, -0.99f ); glVertex3f( 1.00f, -1.00f, -0.99f ); /* Top Face */ glVertex3f( -1.00f, 0.99f, -1.00f ); glVertex3f( -1.00f, 0.99f, 1.00f ); glVertex3f( 1.00f, 0.99f, 1.00f ); glVertex3f( 1.00f, 0.99f, -1.00f ); /* Bottom Face */ glVertex3f( -1.00f, -0.99f, -1.00f ); glVertex3f( 1.00f, -0.99f, -1.00f ); glVertex3f( 1.00f, -0.99f, 1.00f ); glVertex3f( -1.00f, -0.99f, 1.00f ); /* Right face */ glVertex3f( 0.99f, -1.00f, -1.00f ); glVertex3f( 0.99f, 1.00f, -1.00f ); glVertex3f( 0.99f, 1.00f, 1.00f ); glVertex3f( 0.99f, -1.00f, 1.00f ); /* Left Face */ glVertex3f( -0.99f, -1.00f, -1.00f ); glVertex3f( -0.99f, -1.00f, 1.00f ); glVertex3f( -0.99f, 1.00f, 1.00f ); glVertex3f( -0.99f, 1.00f, -1.00f ); } glEnd(); glBindTexture(GL_TEXTURE_2D, ta); glBegin(GL_QUADS); { glColor4d(1.0, 1.0, 1.0, 1.0); // Front Face glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, 1.00f ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, 1.00f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f, 1.0f, 1.00f ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, 1.0f, 1.00f ); // Top Face glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f, 1.00f, -1.0f ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f, 1.00f, 1.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 1.0f, 1.00f, 1.0f ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 1.0f, 1.00f, -1.0f ); // Bottom Face glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.00f, -1.0f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f, -1.00f, -1.0f ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.0f, -1.00f, 1.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.00f, 1.0f ); // Right face glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 1.00f, -1.0f, -1.0f ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 1.00f, -1.0f, 1.0f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.00f, 1.0f, 1.0f ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.00f, 1.0f, -1.0f ); // Left Face glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.00f, -1.0f, -1.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.00f, 1.0f, -1.0f ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.00f, 1.0f, 1.0f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.00f, -1.0f, 1.0f ); } glEnd(); glBindTexture(GL_TEXTURE_2D, tb); glBegin(GL_QUADS); { glColor4d(1.0, 1.0, 1.0, 1.0); // Back Face glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, -1.00f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f, 1.0f, -1.00f ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 1.0f, 1.0f, -1.00f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.00f ); } glEnd(); if ((d->i >= rotStart) && (d->i < (tot - rotStart))) { xrot += 360.0f / (float)(tot - 2 * rotStart); yrot += 180.0f / (float)(tot - 2 * rotStart); } d->i++; } void PresentationGL::slotPause() { d->timer->stop(); if (d->slideCtrlWidget->isHidden()) { int w = d->slideCtrlWidget->width(); d->slideCtrlWidget->move(d->deskWidth - w - 1, 0); d->slideCtrlWidget->show(); } } void PresentationGL::slotPlay() { d->slideCtrlWidget->hide(); slotTimeOut(); } void PresentationGL::slotPrev() { previousFrame(); if (d->endOfShow) { update(); return; } d->effectRunning = false; loadImage(); update(); } void PresentationGL::slotNext() { advanceFrame(); if (d->endOfShow) { update(); return; } d->effectRunning = false; loadImage(); update(); } void PresentationGL::slotClose() { close(); } QPixmap PresentationGL::generateOutlinedTextPixmap(const QString& text) { QFont fn(font()); fn.setPointSize(fn.pointSize()); fn.setBold(true); return generateOutlinedTextPixmap(text, fn); } QPixmap PresentationGL::generateOutlinedTextPixmap(const QString& text, QFont& fn) { QColor fgColor(Qt::white); QColor bgColor(Qt::black); return generateCustomOutlinedTextPixmap(text, fn, fgColor, bgColor, 0, true); } QPixmap PresentationGL::generateCustomOutlinedTextPixmap(const QString& text, QFont& fn, QColor& fgColor, QColor& bgColor, int opacity, bool drawTextOutline) { QFontMetrics fm(fn); QRect rect = fm.boundingRect(text); rect.adjust( -fm.maxWidth(), -fm.height(), fm.maxWidth(), fm.height() / 2 ); QPixmap pix(rect.width(), rect.height()); pix.fill(Qt::transparent); if (opacity > 0) { QPainter pbg(&pix); pbg.setBrush(bgColor); pbg.setPen(bgColor); pbg.setOpacity(opacity / 10.0); pbg.drawRoundedRect(0, 0, (int)pix.width(), (int)pix.height(), (int)pix.height() / 3, (int)pix.height() / 3); } QPainter p(&pix); p.setRenderHint(QPainter::Antialiasing, true); p.setBrush(QBrush()); p.setPen(QPen()); // draw outline QPainterPath path; path.addText(fm.maxWidth(), fm.height() * 1.5, fn, text); QPainterPathStroker stroker; stroker.setWidth(2); stroker.setCapStyle(Qt::RoundCap); stroker.setJoinStyle(Qt::RoundJoin); QPainterPath outline = stroker.createStroke(path); if (drawTextOutline) { p.fillPath(outline, Qt::black); } p.fillPath(path, QBrush(fgColor)); p.setRenderHint(QPainter::Antialiasing, false); p.end(); return pix; } bool PresentationGL::checkOpenGL() const { // No OpenGL context is found. Are the drivers ok? if (!isValid()) { return false; } // GL_EXT_texture3D is not supported QString s = QString::fromLatin1(reinterpret_cast(glGetString(GL_EXTENSIONS))); if (!s.contains(QString::fromLatin1("GL_EXT_texture3D"), Qt::CaseInsensitive)) { return false; } // Everything is ok! return true; } } // namespace DigikamGenericPresentationPlugin diff --git a/core/dplugins/generic/view/presentation/opengl/presentationkb.cpp b/core/dplugins/generic/view/presentation/opengl/presentationkb.cpp index af61ea96fa..e77acd4c46 100644 --- a/core/dplugins/generic/view/presentation/opengl/presentationkb.cpp +++ b/core/dplugins/generic/view/presentation/opengl/presentationkb.cpp @@ -1,794 +1,795 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-11-14 * Description : a presentation tool. * * Copyright (C) 2007-2009 by Valerio Fuoglio * Copyright (C) 2012-2020 by Gilles Caulier * * Parts of this code are based on * smoothslidesaver by Carsten Weinhold * and slideshowgl by Renchi Raju * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "presentationkb.h" #include "presentationkb_p.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include namespace DigikamGenericPresentationPlugin { KBViewTrans::KBViewTrans(bool zoomIn, float relAspect) : m_deltaX(0.0), m_deltaY(0.0), m_deltaScale(0.0), m_baseScale(0.0), m_baseX(0.0), m_baseY(0.0), m_xScale(0.0), m_yScale(0.0) { int i = 0; // randomly select sizes of start and end viewport double s[2] = { 0.0 }; do { s[0] = 0.3 * rnd() + 1.0; s[1] = 0.3 * rnd() + 1.0; } while ((fabs(s[0] - s[1]) < 0.15) && (++i < 10)); if (zoomIn ^ (s[0] > s[1])) { double tmp = s[0]; s[0] = s[1]; s[1] = tmp; } m_deltaScale = s[1] / s[0] - 1.0; m_baseScale = s[0]; // additional scale factors to ensure proper m_aspect of the displayed image double x[2] = { 0.0 }; double y[2] = { 0.0 }; double xMargin[2] = { 0.0 }; double yMargin[2] = { 0.0 }; double bestDist = 0.0;; double sx = 0.0; double sy = 0.0; if (relAspect > 1.0) { sx = 1.0; sy = relAspect; } else { sx = 1.0 / relAspect; sy = 1.0; } m_xScale = sx; m_yScale = sy; // calculate path xMargin[0] = (s[0] * sx - 1.0) / 2.0; yMargin[0] = (s[0] * sy - 1.0) / 2.0; xMargin[1] = (s[1] * sx - 1.0) / 2.0; yMargin[1] = (s[1] * sy - 1.0) / 2.0; i = 0; bestDist = 0.0; do { double sign = rndSign(); x[0] = xMargin[0] * (0.2 * rnd() + 0.8) * sign; y[0] = yMargin[0] * (0.2 * rnd() + 0.8) * -sign; x[1] = xMargin[1] * (0.2 * rnd() + 0.8) * -sign; y[1] = yMargin[1] * (0.2 * rnd() + 0.8) * sign; if (fabs(x[1] - x[0]) + fabs(y[1] - y[0]) > bestDist) { m_baseX = x[0]; m_baseY = y[0]; m_deltaX = x[1] - x[0]; m_deltaY = y[1] - y[0]; bestDist = fabs(m_deltaX) + fabs(m_deltaY); } } while ((bestDist < 0.3) && (++i < 10)); } KBViewTrans::KBViewTrans() : m_deltaX(0.0), m_deltaY(0.0), m_deltaScale(0.0), m_baseScale(0.0), m_baseX(0.0), m_baseY(0.0), m_xScale(0.0), m_yScale(0.0) { } KBViewTrans::~KBViewTrans() { } float KBViewTrans::transX(float pos) const { return (m_baseX + m_deltaX * pos); } float KBViewTrans::transY(float pos) const { return (m_baseY + m_deltaY * pos); } float KBViewTrans::scale (float pos) const { return (m_baseScale * (1.0 + m_deltaScale * pos)); } float KBViewTrans::xScaleCorrect() const { return m_xScale; } float KBViewTrans::yScaleCorrect() const { return m_yScale; } double KBViewTrans::rnd() const { return ((double)qrand() / (double)RAND_MAX); } double KBViewTrans::rndSign() const { return ((qrand() < RAND_MAX / 2) ? 1.0 : -1.0); } // ------------------------------------------------------------------------- KBImage::KBImage(KBViewTrans* const viewTrans, float aspect) : m_viewTrans(viewTrans), m_aspect(aspect), m_pos(0.0), m_opacity(0.0), m_texture(nullptr) { m_paint = (m_viewTrans) ? true : false; } KBImage::~KBImage() { if (m_texture) { m_texture->destroy(); } delete m_viewTrans; delete m_texture; } // ------------------------------------------------------------------------- PresentationKB::PresentationKB(PresentationContainer* const sharedData) : QOpenGLWidget(), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); - setContextMenuPolicy(Qt::PreventContextMenu); - setWindowState(windowState() | Qt::WindowFullScreen); QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = qApp->activeWindow()) { if (QWindow* const window = widget->windowHandle()) { screen = window->screen(); } } QRect deskRect = screen->geometry(); d->deskX = deskRect.x(); d->deskY = deskRect.y(); d->deskWidth = deskRect.width(); d->deskHeight = deskRect.height(); move(d->deskX, d->deskY); resize(d->deskWidth, d->deskHeight); + setContextMenuPolicy(Qt::PreventContextMenu); + setWindowState(windowState() | Qt::WindowFullScreen); + d->sharedData = sharedData; qsrand(QTime::currentTime().msec()); readSettings(); unsigned frameRate; if (d->forceFrameRate == 0) { int rate = 25; if (screen) { rate = (int)screen->refreshRate(); } frameRate = rate * 2; } else { frameRate = d->forceFrameRate; } qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Frame Rate : " << frameRate; d->image[0] = new KBImage(nullptr); d->image[1] = new KBImage(nullptr); d->step = 1.0 / ((float)(d->delay * frameRate)); d->imageLoadThread = new KBImageLoader(d->sharedData, width(), height()); d->timer = new QTimer(this); connect(d->timer, SIGNAL(timeout()), this, SLOT(moveSlot())); // -- playback widget ------------------------------- #ifdef HAVE_MEDIAPLAYER d->playbackWidget = new PresentationAudioWidget(this, d->sharedData->soundtrackUrls, d->sharedData); d->playbackWidget->hide(); d->playbackWidget->move(d->deskX, d->deskY); #endif // -- hide cursor when not moved -------------------- d->mouseMoveTimer = new QTimer(this); d->mouseMoveTimer->setSingleShot(true); connect(d->mouseMoveTimer, SIGNAL(timeout()), this, SLOT(slotMouseMoveTimeOut())); setMouseTracking(true); slotMouseMoveTimeOut(); // -- load image and let's start d->imageLoadThread->start(); d->timer->start(1000 / frameRate); #ifdef HAVE_MEDIAPLAYER if (d->sharedData->soundtrackPlay) { d->playbackWidget->slotPlay(); } #endif } PresentationKB::~PresentationKB() { #ifdef HAVE_MEDIAPLAYER d->playbackWidget->slotStop(); #endif d->timer->stop(); d->mouseMoveTimer->stop(); delete d->effect; delete d->image[0]; delete d->image[1]; if (d->endTexture) { d->endTexture->destroy(); } delete d->endTexture; d->imageLoadThread->quit(); bool terminated = d->imageLoadThread->wait(10000); if (!terminated) { d->imageLoadThread->terminate(); d->imageLoadThread->wait(3000); } delete d->imageLoadThread; delete d; } float PresentationKB::aspect() const { return ((float)width() / (float)height()); } void PresentationKB::setNewKBEffect() { KBEffect::Type type; bool needFadeIn = ((d->effect == nullptr) || (d->effect->type() == KBEffect::Fade)); // we currently only have two effects if (d->disableFadeInOut) { type = KBEffect::Blend; } else if (d->disableCrossFade) { type = KBEffect::Fade; } else { type = KBEffect::chooseKBEffect((d->effect) ? d->effect->type() : KBEffect::Fade); } delete d->effect; switch (type) { case KBEffect::Fade: d->effect = new FadeKBEffect(this, needFadeIn); break; case KBEffect::Blend: d->effect = new BlendKBEffect(this, needFadeIn); break; default: qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown transition effect, falling back to crossfade"; d->effect = new BlendKBEffect(this, needFadeIn); break; } } void PresentationKB::moveSlot() { if (d->initialized) { if (d->effect->done()) { setNewKBEffect(); d->imageLoadThread->requestNewImage(); d->endOfShow = !d->haveImages; } d->effect->advanceTime(d->step); } update(); } bool PresentationKB::setupNewImage(int idx) { Q_ASSERT(idx >= 0 && idx < 2); if (!d->haveImages) { return false; } bool ok = false; d->zoomIn = !d->zoomIn; if (d->imageLoadThread->grabImage()) { delete d->image[idx]; // we have the image lock and there is an image float imageAspect = d->imageLoadThread->imageAspect(); KBViewTrans* const viewTrans = new KBViewTrans(d->zoomIn, aspect() / imageAspect); d->image[idx] = new KBImage(viewTrans, imageAspect); applyTexture(d->image[idx], d->imageLoadThread->image()); ok = true; } else { d->haveImages = false; } // don't forget to release the lock on the copy of the image // owned by the image loader thread d->imageLoadThread->ungrabImage(); return ok; } void PresentationKB::startSlideShowOnce() { // when the image loader thread is ready, it will already have loaded // the first image if ((d->initialized == false) && d->imageLoadThread->ready()) { setupNewImage(0); // setup the first image and d->imageLoadThread->requestNewImage(); // load the next one in background setNewKBEffect(); // set the initial effect d->initialized = true; } } void PresentationKB::swapImages() { KBImage* const tmp = d->image[0]; d->image[0] = d->image[1]; d->image[1] = tmp; } void PresentationKB::initializeGL() { // Enable Texture Mapping glEnable(GL_TEXTURE_2D); // Clear The Background Color glClearColor(0.0, 0.0, 0.0, 1.0f); glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); // Turn Blending On glEnable(GL_BLEND); // Blending Function For Translucency Based On Source Alpha Value glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Enable perspective vision glClearDepth(1.0f); } void PresentationKB::paintGL() { startSlideShowOnce(); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); // only clear the color buffer, if none of the active images is fully opaque if (!((d->image[0]->m_paint && (d->image[0]->m_opacity == 1.0)) || (d->image[1]->m_paint && (d->image[1]->m_opacity == 1.0)))) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (d->endOfShow) { endOfShow(); d->timer->stop(); } else { if (d->image[1]->m_paint) { paintTexture(d->image[1]); } if (d->image[0]->m_paint) { paintTexture(d->image[0]); } } glFlush(); } void PresentationKB::resizeGL(int w, int h) { glViewport(0, 0, (GLint)w, (GLint)h); } void PresentationKB::applyTexture(KBImage* const img, const QImage &texture) { /* create the texture */ img->m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); img->m_texture->setData(texture.mirrored()); img->m_texture->setMinificationFilter(QOpenGLTexture::Linear); img->m_texture->setMagnificationFilter(QOpenGLTexture::Linear); img->m_texture->bind(); } void PresentationKB::paintTexture(KBImage* const img) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float sx = img->m_viewTrans->xScaleCorrect(); float sy = img->m_viewTrans->yScaleCorrect(); glTranslatef(img->m_viewTrans->transX(img->m_pos) * 2.0, img->m_viewTrans->transY(img->m_pos) * 2.0, 0.0); glScalef(img->m_viewTrans->scale(img->m_pos), img->m_viewTrans->scale(img->m_pos), 0.0); img->m_texture->bind(); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, img->m_opacity); glTexCoord2f(0, 0); glVertex3f(-sx, -sy, 0); glTexCoord2f(1, 0); glVertex3f(sx, -sy, 0); glTexCoord2f(1, 1); glVertex3f(sx, sy, 0); glTexCoord2f(0, 1); glVertex3f(-sx, sy, 0); } glEnd(); } void PresentationKB::readSettings() { KConfig config; KConfigGroup group = config.group("Presentation Settings"); d->delay = group.readEntry("Delay", 8000) / 1000; d->disableFadeInOut = group.readEntry("KB Disable FadeInOut", false); d->disableCrossFade = group.readEntry("KB Disable Crossfade", false); d->forceFrameRate = group.readEntry("KB Force Framerate", 0); if (d->delay < 5) { d->delay = 5; } if (d->forceFrameRate > 120) { d->forceFrameRate = 120; } } void PresentationKB::endOfShow() { QPixmap pix(512, 512); pix.fill(Qt::black); QFont fn(font()); fn.setPointSize(fn.pointSize() + 10); fn.setBold(true); QPainter p(&pix); p.setPen(Qt::white); p.setFont(fn); p.drawText(20, 50, i18n("SlideShow Completed")); p.drawText(20, 100, i18n("Click to Exit...")); p.end(); /* create the texture */ d->endTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); d->endTexture->setData(pix.toImage().mirrored()); d->endTexture->setMinificationFilter(QOpenGLTexture::Linear); d->endTexture->setMagnificationFilter(QOpenGLTexture::Linear); d->endTexture->bind(); /* paint the texture */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBegin(GL_QUADS); { glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0, 0); glVertex3f(-1.0, -1.0, 0); glTexCoord2f(1, 0); glVertex3f(1.0, -1.0, 0); glTexCoord2f(1, 1); glVertex3f(1.0, 1.0, 0); glTexCoord2f(0, 1); glVertex3f(-1.0, 1.0, 0); } glEnd(); d->showingEnd = true; } QStringList PresentationKB::effectNames() { QStringList effects; effects.append(QLatin1String("Ken Burns")); return effects; } QMap PresentationKB::effectNamesI18N() { QMap effects; effects[QLatin1String("Ken Burns")] = i18n("Ken Burns"); return effects; } void PresentationKB::keyPressEvent(QKeyEvent* event) { if (!event) { return; } #ifdef HAVE_MEDIAPLAYER d->playbackWidget->keyPressEvent(event); #endif if (event->key() == Qt::Key_Escape) { close(); } } void PresentationKB::mousePressEvent(QMouseEvent* e) { if (!e) { return; } if (d->endOfShow && d->showingEnd) { slotClose(); } } void PresentationKB::mouseMoveEvent(QMouseEvent* e) { setCursor(QCursor(Qt::ArrowCursor)); d->mouseMoveTimer->start(1000); #ifdef HAVE_MEDIAPLAYER if (!d->playbackWidget->canHide()) { return; } QPoint pos(e->pos()); if ((pos.y() > (d->deskY + 20)) && (pos.y() < (d->deskY + d->deskHeight - 20 - 1))) { if (d->playbackWidget->isHidden()) { return; } else { d->playbackWidget->hide(); setFocus(); } return; } d->playbackWidget->show(); #else Q_UNUSED(e); #endif } void PresentationKB::slotMouseMoveTimeOut() { QPoint pos(QCursor::pos()); if ((pos.y() < (d->deskY + 20)) || (pos.y() > (d->deskY + d->deskHeight - 20 - 1)) #ifdef HAVE_MEDIAPLAYER || d->playbackWidget->underMouse() #endif ) { return; } setCursor(QCursor(Qt::BlankCursor)); } bool PresentationKB::checkOpenGL() const { // No OpenGL context is found. Are the drivers ok? if (!isValid()) { return false; } // GL_EXT_texture3D is not supported QString s = QString::fromLatin1(reinterpret_cast(glGetString(GL_EXTENSIONS))); if (!s.contains(QString::fromLatin1("GL_EXT_texture3D"), Qt::CaseInsensitive)) { return false; } // Everything is ok! return true; } void PresentationKB::slotClose() { close(); } } // namespace DigikamGenericPresentationPlugin diff --git a/core/dplugins/generic/view/presentation/widgets/presentationwidget.cpp b/core/dplugins/generic/view/presentation/widgets/presentationwidget.cpp index 1a1089eece..89373d8ed2 100644 --- a/core/dplugins/generic/view/presentation/widgets/presentationwidget.cpp +++ b/core/dplugins/generic/view/presentation/widgets/presentationwidget.cpp @@ -1,1723 +1,1724 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2003-02-16 * Description : a presentation tool. * * Copyright (C) 2006-2009 by Valerio Fuoglio * Copyright (C) 2009 by Andi Clemens * Copyright (C) 2003-2005 by Renchi Raju * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "presentationwidget.h" // C++ includes #include #include #include #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_config.h" #include "presentationcontainer.h" #include "presentationctrlwidget.h" #include "presentationloader.h" #ifdef HAVE_MEDIAPLAYER # include "presentationaudiowidget.h" # include "slidevideo.h" #endif using namespace Digikam; namespace DigikamGenericPresentationPlugin { class Q_DECL_HIDDEN PresentationWidget::Private { public: explicit Private() : sharedData(nullptr), imageLoader(nullptr), #ifdef HAVE_MEDIAPLAYER playbackWidget(nullptr), videoView(nullptr), #endif timer(nullptr), fileIndex(0), effect(nullptr), effectRunning(false), x(0), y(0), w(0), h(0), dx(0), dy(0), ix(0), iy(0), i(0), j(0), subType(0), x0(0), y0(0), x1(0), y1(0), wait(0), fx(0), fy(0), alpha(0), fd(0), intArray(nullptr), pdone(0), pixelMatrix(nullptr), slideCtrlWidget(nullptr), mouseMoveTimer(nullptr), deskX(0), deskY(0), deskWidth(0), deskHeight(0) { } PresentationContainer* sharedData; // ------------------------- QMap Effects; PresentationLoader* imageLoader; QPixmap currImage; #ifdef HAVE_MEDIAPLAYER PresentationAudioWidget* playbackWidget; SlideVideo* videoView; #endif QTimer* timer; int fileIndex; EffectMethod effect; bool effectRunning; QString effectName; /// values for state of various effects int x; int y; int w; int h; int dx; int dy; int ix; int iy; int i; int j; int subType; int x0; int y0; int x1; int y1; int wait; double fx; double fy; double alpha; double fd; int* intArray; bool pdone; bool** pixelMatrix; /// static QPolygon pa; PresentationCtrlWidget* slideCtrlWidget; QTimer* mouseMoveTimer; int deskX; int deskY; int deskWidth; int deskHeight; }; PresentationWidget::PresentationWidget(PresentationContainer* const sharedData) : QWidget(), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); - setContextMenuPolicy(Qt::PreventContextMenu); - setWindowState(windowState() | Qt::WindowFullScreen); QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = qApp->activeWindow()) { if (QWindow* const window = widget->windowHandle()) { screen = window->screen(); } } QRect deskRect = screen->geometry(); d->deskX = deskRect.x(); d->deskY = deskRect.y(); d->deskWidth = deskRect.width(); d->deskHeight = deskRect.height(); move(d->deskX, d->deskY); resize(d->deskWidth, d->deskHeight); + setContextMenuPolicy(Qt::PreventContextMenu); + setWindowState(windowState() | Qt::WindowFullScreen); + d->sharedData = sharedData; d->slideCtrlWidget = new PresentationCtrlWidget(this); d->slideCtrlWidget->hide(); d->slideCtrlWidget->move(d->deskWidth - d->slideCtrlWidget->width(), d->deskY); if (!d->sharedData->loop) { d->slideCtrlWidget->setEnabledPrev(false); } connect(d->slideCtrlWidget, SIGNAL(signalPause()), this, SLOT(slotPause())); connect(d->slideCtrlWidget, SIGNAL(signalPlay()), this, SLOT(slotPlay())); connect(d->slideCtrlWidget, SIGNAL(signalNext()), this, SLOT(slotNext())); connect(d->slideCtrlWidget, SIGNAL(signalPrev()), this, SLOT(slotPrev())); connect(d->slideCtrlWidget, SIGNAL(signalClose()), this, SLOT(slotClose())); #ifdef HAVE_MEDIAPLAYER // -- playback widget ------------------------------- d->playbackWidget = new PresentationAudioWidget(this, d->sharedData->soundtrackUrls, d->sharedData); d->playbackWidget->hide(); d->playbackWidget->move(d->deskX, d->deskY); // -- video preview --------------------------------- d->videoView = new SlideVideo(this); //TODO: pass mouse events from d->videoView to this ? //d->videoView->installEventFilter(this); connect(d->videoView, SIGNAL(signalVideoLoaded(bool)), this, SLOT(slotVideoLoaded(bool))); connect(d->videoView, SIGNAL(signalVideoFinished()), this, SLOT(slotVideoFinished())); d->videoView->hide(); d->videoView->resize(d->deskWidth, d->deskHeight); #endif // --------------------------------------------------------------- d->fileIndex = -1; // start with -1 d->effect = nullptr; d->effectRunning = false; d->intArray = nullptr; m_endOfShow = false; m_simplyShow = false; m_startPainter = false; d->timer = new QTimer(this); connect(d->timer, SIGNAL(timeout()), this, SLOT(slotTimeOut())); d->pa = QPolygon(4); m_buffer = QPixmap(size()); m_buffer.fill(Qt::black); d->imageLoader = new PresentationLoader(d->sharedData, width(), height(), d->fileIndex); // -------------------------------------------------- registerEffects(); if (d->sharedData->effectName == QLatin1String("Random")) { d->effect = getRandomEffect(); } else { d->effectName = d->sharedData->effectName; d->effect = d->Effects[d->sharedData->effectName]; if (!d->effect) { d->effect = d->Effects[QLatin1String("None")]; d->effectName = QLatin1String("None"); } } d->timer->setSingleShot(true); d->timer->start(10); // -- hide cursor when not moved -------------------- d->mouseMoveTimer = new QTimer(this); d->mouseMoveTimer->setSingleShot(true); connect(d->mouseMoveTimer, SIGNAL(timeout()), this, SLOT(slotMouseMoveTimeOut())); setMouseTracking(true); slotMouseMoveTimeOut(); #ifdef HAVE_MEDIAPLAYER if (d->sharedData->soundtrackPlay) { d->playbackWidget->slotPlay(); } #endif } PresentationWidget::~PresentationWidget() { #ifdef HAVE_MEDIAPLAYER d->playbackWidget->slotStop(); #endif d->timer->stop(); d->mouseMoveTimer->stop(); if (d->intArray) { delete [] d->intArray; } delete d->imageLoader; delete d; } void PresentationWidget::readSettings() { } void PresentationWidget::loadNextImage() { if (!d->currImage.isNull()) { m_buffer = d->currImage; } else { m_buffer = QPixmap(size()); m_buffer.fill(Qt::black); } d->fileIndex++; d->imageLoader->next(); int num = d->sharedData->urlList.count(); if (d->fileIndex >= num) { if (d->sharedData->loop) { d->fileIndex = 0; } else { d->currImage = QPixmap(0, 0); d->fileIndex = num - 1; return; } } if (!d->sharedData->loop) { d->slideCtrlWidget->setEnabledPrev(d->fileIndex > 0); d->slideCtrlWidget->setEnabledNext(d->fileIndex < num - 1); } QImage img = d->imageLoader->getCurrent(); QPixmap newPixmap = QPixmap::fromImage(img); QPixmap pixmap(width(), height()); pixmap.fill(Qt::black); QPainter p(&pixmap); p.drawPixmap((width() - newPixmap.width()) / 2, (height() - newPixmap.height()) / 2, newPixmap, 0, 0, newPixmap.width(), newPixmap.height()); d->currImage = pixmap; if (img.isNull()) { #ifdef HAVE_MEDIAPLAYER d->videoView->setCurrentUrl(d->imageLoader->currPath()); #endif } } void PresentationWidget::loadPrevImage() { d->fileIndex--; d->imageLoader->prev(); int num = d->sharedData->urlList.count(); if (d->fileIndex < 0) { if (d->sharedData->loop) { d->fileIndex = num - 1; } else { d->fileIndex = -1; // set this to -1. return; } } if (!d->sharedData->loop) { d->slideCtrlWidget->setEnabledPrev(d->fileIndex > 0); d->slideCtrlWidget->setEnabledNext(d->fileIndex < num - 1); } QImage img = d->imageLoader->getCurrent(); QPixmap newPixmap = QPixmap::fromImage(img); QPixmap pixmap(width(), height()); pixmap.fill(Qt::black); QPainter p(&pixmap); p.drawPixmap((width() - newPixmap.width()) / 2, (height() - newPixmap.height()) / 2, newPixmap, 0, 0, newPixmap.width(), newPixmap.height()); d->currImage = pixmap; if (img.isNull()) { #ifdef HAVE_MEDIAPLAYER d->videoView->setCurrentUrl(d->imageLoader->currPath()); #endif } } void PresentationWidget::printFilename() { if (d->currImage.isNull()) { return; } QPainter p; p.begin(&d->currImage); p.setPen(Qt::black); for (int x = 9 ; x <= 11 ; ++x) { for (int y = 31 ; y >= 29 ; --y) { p.drawText(x, height() - y, d->imageLoader->currFileName()); } } p.setPen(QColor(Qt::white)); p.drawText(10, height() - 30, d->imageLoader->currFileName()); } void PresentationWidget::printComments() { if (d->currImage.isNull()) { return; } DItemInfo info(d->sharedData->iface->itemInfo(d->imageLoader->currPath())); QString comments = info.comment(); int yPos = 30; // Text Y coordinate if (d->sharedData->printFileName) { yPos = 50; } QStringList commentsByLines; uint commentsIndex = 0; // Comments QString index while (commentsIndex < (uint)comments.length()) { QString newLine; bool breakLine = false; // End Of Line found uint currIndex; // Comments QString current index // Check minimal lines dimension uint commentsLinesLengthLocal = d->sharedData->commentsLinesLength; for (currIndex = commentsIndex ; (currIndex < (uint)comments.length()) && !breakLine ; ++currIndex) { if ((comments[currIndex] == QLatin1Char('\n')) || comments[currIndex].isSpace()) { breakLine = true; } } if (commentsLinesLengthLocal <= (currIndex - commentsIndex)) { commentsLinesLengthLocal = (currIndex - commentsIndex); } breakLine = false; for (currIndex = commentsIndex ; ((currIndex <= (commentsIndex + commentsLinesLengthLocal)) && (currIndex < (uint)comments.length()) && !breakLine) ; ++currIndex) { breakLine = (comments[currIndex] == QLatin1Char('\n')) ? true : false; if (breakLine) { newLine.append(QLatin1Char(' ')); } else { newLine.append(comments[currIndex]); } } commentsIndex = currIndex; // The line is ended if (commentsIndex != (uint)comments.length()) { while (!newLine.endsWith(QLatin1Char(' '))) { newLine.truncate(newLine.length() - 1); commentsIndex--; } } commentsByLines.prepend(newLine.trimmed()); } QPainter p; p.begin(&d->currImage); p.setFont(*d->sharedData->captionFont); for (int lineNumber = 0 ; lineNumber < (int)commentsByLines.count() ; ++lineNumber) { p.setPen(QColor(d->sharedData->commentsBgColor)); // coefficient 1.5 is used to maintain distance between different lines for (int x = 9 ; x <= 11 ; ++x) { for (int y = (int)(yPos + lineNumber * 1.5 * d->sharedData->captionFont->pointSize() + 1) ; y >= (int)(yPos + lineNumber * 1.5 * d->sharedData->captionFont->pointSize() - 1) ; --y) { p.drawText(x, height() - y, commentsByLines[lineNumber]); } } p.setPen(QColor(d->sharedData->commentsFontColor)); p.drawText(10, height() - (int)(lineNumber * 1.5 * d->sharedData->captionFont->pointSize() + yPos), commentsByLines[lineNumber]); } } void PresentationWidget::printProgress() { if (d->currImage.isNull()) { return; } QPainter p; p.begin(&d->currImage); QString progress(QString::number(d->fileIndex + 1) + QLatin1Char('/') + QString::number(d->sharedData->urlList.count())); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) int stringLength = p.fontMetrics().horizontalAdvance(progress) * progress.length(); #else int stringLength = p.fontMetrics().width(progress) * progress.length(); #endif p.setPen(QColor(Qt::black)); for (int x = 9 ; x <= 11 ; ++x) { for (int y = 21 ; y >= 19 ; --y) { p.drawText(width() - stringLength - x, y, progress); } } p.setPen(QColor(Qt::white)); p.drawText(width() - stringLength - 10, 20, progress); } void PresentationWidget::showEndOfShow() { m_endOfShow = true; update(); d->slideCtrlWidget->setEnabledPlay(false); d->slideCtrlWidget->setEnabledNext(false); d->slideCtrlWidget->setEnabledPrev(false); } void PresentationWidget::keyPressEvent(QKeyEvent* event) { if (!event) { return; } #ifdef HAVE_MEDIAPLAYER d->playbackWidget->keyPressEvent(event); #endif d->slideCtrlWidget->keyPressEvent(event); } void PresentationWidget::mousePressEvent(QMouseEvent* e) { if (m_endOfShow) { slotClose(); } if (e->button() == Qt::LeftButton) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotNext(); } else if ((e->button() == Qt::RightButton) && ((d->fileIndex - 1) >= 0)) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotPrev(); } } void PresentationWidget::mouseMoveEvent(QMouseEvent* e) { setCursor(QCursor(Qt::ArrowCursor)); d->mouseMoveTimer->start(1000); if (!d->slideCtrlWidget->canHide() #ifdef HAVE_MEDIAPLAYER || !d->playbackWidget->canHide() #endif ) { return; } QPoint pos(e->pos()); if ((pos.y() > (d->deskY + 20)) && (pos.y() < (d->deskY + d->deskHeight - 20 - 1))) { if (!d->slideCtrlWidget->canHide() #ifdef HAVE_MEDIAPLAYER || !d->playbackWidget->canHide() #endif ) { return; } else { d->slideCtrlWidget->hide(); #ifdef HAVE_MEDIAPLAYER d->playbackWidget->hide(); #endif } return; } /* int w = d->slideCtrlWidget->width(); int h = d->slideCtrlWidget->height(); if (pos.y() < (d->deskY + 20)) { if (pos.x() <= (d->deskX + d->deskWidth / 2)) { // position top left d->slideCtrlWidget->move(d->deskX, d->deskY); } else { // position top right d->slideCtrlWidget->move(d->deskX + d->deskWidth - w - 1, d->deskY); } } else { if (pos.x() <= (d->deskX + d->deskWidth / 2)) { // position bot left d->slideCtrlWidget->move(d->deskX, d->deskY + d->deskHeight - h - 1); } else { // position bot right d->slideCtrlWidget->move(d->deskX + d->deskWidth - w - 1, d->deskY + d->deskHeight - h - 1); } } */ d->slideCtrlWidget->show(); #ifdef HAVE_MEDIAPLAYER d->playbackWidget->show(); #endif } void PresentationWidget::wheelEvent(QWheelEvent* e) { if (!d->sharedData->enableMouseWheel) { return; } if (m_endOfShow) { slotClose(); } int delta = e->angleDelta().y(); if (delta < 0) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotNext(); } else if ((delta > 0) && ((d->fileIndex - 1) >= 0)) { d->timer->stop(); d->slideCtrlWidget->setPaused(true); slotPrev(); } } void PresentationWidget::slotMouseMoveTimeOut() { QPoint pos(QCursor::pos()); if ((pos.y() < (d->deskY + 20)) || (pos.y() > (d->deskY + d->deskHeight - 20 - 1)) || d->slideCtrlWidget->underMouse() #ifdef HAVE_MEDIAPLAYER || d->playbackWidget->underMouse() #endif ) { return; } setCursor(QCursor(Qt::BlankCursor)); } void PresentationWidget::paintEvent(QPaintEvent*) { QPainter p(this); if (m_simplyShow) { if (d->sharedData->printFileName) { printFilename(); } if (d->sharedData->printProgress) { printProgress(); } if (d->sharedData->printFileComments) { printComments(); } double ratio = devicePixelRatioF(); QSize fullSize = QSizeF(ratio * width(), ratio * height()).toSize(); QPixmap pixmap = d->currImage.scaled(fullSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); p.drawPixmap(0, 0, width(), height(), pixmap, 0, 0, pixmap.width(), pixmap.height()); p.end(); m_simplyShow = false; return; } if (m_endOfShow) { p.fillRect(0, 0, width(), height(), Qt::black); QFont fn(font()); fn.setPointSize(fn.pointSize() + 10); fn.setBold(true); p.setFont(fn); p.setPen(Qt::white); p.drawText(100, 100, i18n("Slideshow Completed")); p.drawText(100, 100 + 10 + fn.pointSize(), i18n("Click to Exit...")); p.end(); return; } // If execution reach this line, an effect is running p.drawPixmap(0, 0, m_buffer); } void PresentationWidget::startPainter() { m_startPainter = true; repaint(); } void PresentationWidget::slotPause() { d->timer->stop(); if (d->slideCtrlWidget->isHidden()) { int w = d->slideCtrlWidget->width(); d->slideCtrlWidget->move(d->deskWidth - w - 1, 0); d->slideCtrlWidget->show(); } } void PresentationWidget::slotPlay() { d->slideCtrlWidget->hide(); slotTimeOut(); } void PresentationWidget::slotPrev() { loadPrevImage(); if (d->currImage.isNull() || d->sharedData->urlList.isEmpty()) { showEndOfShow(); return; } d->effectRunning = false; showCurrentImage(); } void PresentationWidget::slotNext() { loadNextImage(); if (d->currImage.isNull() || d->sharedData->urlList.isEmpty()) { showEndOfShow(); return; } d->effectRunning = false; showCurrentImage(); } void PresentationWidget::slotClose() { close(); } void PresentationWidget::slotVideoLoaded(bool loaded) { if (loaded) { #ifdef HAVE_MEDIAPLAYER slotPause(); d->videoView->show(); #endif } } void PresentationWidget::slotVideoFinished() { #ifdef HAVE_MEDIAPLAYER d->videoView->hide(); slotPlay(); #endif } // -- Effects rules -------------------------------------------------------------------------------------------------------- void PresentationWidget::registerEffects() { d->Effects.insert(QLatin1String("None"), &PresentationWidget::effectNone); d->Effects.insert(QLatin1String("Chess Board"), &PresentationWidget::effectChessboard); d->Effects.insert(QLatin1String("Melt Down"), &PresentationWidget::effectMeltdown); d->Effects.insert(QLatin1String("Sweep"), &PresentationWidget::effectSweep); d->Effects.insert(QLatin1String("Mosaic"), &PresentationWidget::effectMosaic); d->Effects.insert(QLatin1String("Cubism"), &PresentationWidget::effectCubism); d->Effects.insert(QLatin1String("Growing"), &PresentationWidget::effectGrowing); d->Effects.insert(QLatin1String("Horizontal Lines"), &PresentationWidget::effectHorizLines); d->Effects.insert(QLatin1String("Vertical Lines"), &PresentationWidget::effectVertLines); d->Effects.insert(QLatin1String("Circle Out"), &PresentationWidget::effectCircleOut); d->Effects.insert(QLatin1String("MultiCircle Out"), &PresentationWidget::effectMultiCircleOut); d->Effects.insert(QLatin1String("Spiral In"), &PresentationWidget::effectSpiralIn); d->Effects.insert(QLatin1String("Blobs"), &PresentationWidget::effectBlobs); } QStringList PresentationWidget::effectNames() { QStringList effects; effects.append(QLatin1String("None")); effects.append(QLatin1String("Chess Board")); effects.append(QLatin1String("Melt Down")); effects.append(QLatin1String("Sweep")); effects.append(QLatin1String("Mosaic")); effects.append(QLatin1String("Cubism")); effects.append(QLatin1String("Growing")); effects.append(QLatin1String("Horizontal Lines")); effects.append(QLatin1String("Vertical Lines")); effects.append(QLatin1String("Circle Out")); effects.append(QLatin1String("MultiCircle Out")); effects.append(QLatin1String("Spiral In")); effects.append(QLatin1String("Blobs")); effects.append(QLatin1String("Random")); return effects; } QMap PresentationWidget::effectNamesI18N() { QMap effects; effects[QLatin1String("None")] = i18nc("Filter Effect: No effect", "None"); effects[QLatin1String("Chess Board")] = i18nc("Filter Effect: Chess Board", "Chess Board"); effects[QLatin1String("Melt Down")] = i18nc("Filter Effect: Melt Down", "Melt Down"); effects[QLatin1String("Sweep")] = i18nc("Filter Effect: Sweep", "Sweep"); effects[QLatin1String("Mosaic")] = i18nc("Filter Effect: Mosaic", "Mosaic"); effects[QLatin1String("Cubism")] = i18nc("Filter Effect: Cubism", "Cubism"); effects[QLatin1String("Growing")] = i18nc("Filter Effect: Growing", "Growing"); effects[QLatin1String("Horizontal Lines")] = i18nc("Filter Effect: Horizontal Lines", "Horizontal Lines"); effects[QLatin1String("Vertical Lines")] = i18nc("Filter Effect: Vertical Lines", "Vertical Lines"); effects[QLatin1String("Circle Out")] = i18nc("Filter Effect: Circle Out", "Circle Out"); effects[QLatin1String("MultiCircle Out")] = i18nc("Filter Effect: Multi-Circle Out", "Multi-Circle Out"); effects[QLatin1String("Spiral In")] = i18nc("Filter Effect: Spiral In", "Spiral In"); effects[QLatin1String("Blobs")] = i18nc("Filter Effect: Blobs", "Blobs"); effects[QLatin1String("Random")] = i18nc("Filter Effect: Random effect", "Random"); return effects; } void PresentationWidget::slotTimeOut() { if (!d->effect) { return; // No effect -> bye ! } int tmout = -1; if (d->effectRunning) // Effect under progress ? { tmout = (this->*d->effect)(false); } else { loadNextImage(); if (d->currImage.isNull() || d->sharedData->urlList.isEmpty()) // End of slideshow ? { showEndOfShow(); return; } if (d->sharedData->effectName == QLatin1String("Random")) // Take a random effect. { d->effect = getRandomEffect(); if (!d->effect) { return; } } d->effectRunning = true; tmout = (this->*d->effect)(true); } if (tmout <= 0) // Effect finished -> delay. { tmout = d->sharedData->delay; d->effectRunning = false; } d->timer->start(tmout); } void PresentationWidget::showCurrentImage() { if (d->currImage.isNull()) { return; } m_simplyShow = true; repaint(); } PresentationWidget::EffectMethod PresentationWidget::getRandomEffect() { QStringList effs = d->Effects.keys(); effs.removeAt(effs.indexOf(QLatin1String("None"))); int count = effs.count(); int i = qrand() % count; QString key = effs[i]; d->effectName = key; return d->Effects[key]; } int PresentationWidget::effectNone(bool /* aInit */) { showCurrentImage(); return -1; } int PresentationWidget::effectChessboard(bool aInit) { if (aInit) { d->w = width(); d->h = height(); d->dx = 8; // width of one tile d->dy = 8; // height of one tile d->j = (d->w + d->dx - 1) / d->dx; // number of tiles d->x = d->j * d->dx; // shrinking x-offset from screen border d->ix = 0; // growing x-offset from screen border d->iy = 0; // 0 or d->dy for growing tiling effect d->y = (d->j & 1) ? 0 : d->dy; // 0 or d->dy for shrinking tiling effect d->wait = 800 / d->j; // timeout between effects } if (d->ix >= d->w) { showCurrentImage(); return -1; } d->ix += d->dx; d->x -= d->dx; d->iy = d->iy ? 0 : d->dy; d->y = d->y ? 0 : d->dy; QPainter bufferPainter(&m_buffer); QBrush brush = QBrush(d->currImage); for (int y = 0 ; y < d->w ; y += (d->dy << 1)) { bufferPainter.fillRect(d->ix, y + d->iy, d->dx, d->dy, brush); bufferPainter.fillRect(d->x, y + d->y, d->dx, d->dy, brush); } repaint(); return d->wait; } int PresentationWidget::effectMeltdown(bool aInit) { int i; if (aInit) { delete [] d->intArray; d->w = width(); d->h = height(); d->dx = 4; d->dy = 16; d->ix = d->w / d->dx; d->intArray = new int[d->ix]; for (i = d->ix - 1 ; i >= 0 ; --i) { d->intArray[i] = 0; } } d->pdone = true; int y, x; QPainter bufferPainter(&m_buffer); for (i = 0, x = 0 ; i < d->ix ; ++i, x += d->dx) { y = d->intArray[i]; if (y >= d->h) { continue; } d->pdone = false; if ((qrand() & 15) < 6) { continue; } /* bufferPainter.drawPixmap(x, y + d->dy, m_buffer, x, y, d->dx, d->h - y - d->dy); */ bufferPainter.drawPixmap(x, y, d->currImage, x, y, d->dx, d->dy); d->intArray[i] += d->dy; } bufferPainter.end(); repaint(); if (d->pdone) { delete [] d->intArray; d->intArray = nullptr; showCurrentImage(); return -1; } return 15; } int PresentationWidget::effectSweep(bool aInit) { if (aInit) { // subtype: 0=sweep right to left, 1=sweep left to right // 2=sweep bottom to top, 3=sweep top to bottom d->subType = qrand() % 4; d->w = width(); d->h = height(); d->dx = (d->subType == 1 ? 16 : -16); d->dy = (d->subType == 3 ? 16 : -16); d->x = (d->subType == 1 ? 0 : d->w); d->y = (d->subType == 3 ? 0 : d->h); } if (d->subType == 0 || d->subType == 1) { // horizontal sweep if (((d->subType == 0) && (d->x < -64)) || ((d->subType == 1) && (d->x > d->w + 64))) { showCurrentImage(); return -1; } int w; int x; int i; for (w = 2, i = 4, x = d->x ; i > 0 ; --i, w <<= 1, x -= d->dx) { m_px = x; m_py = 0; m_psx = w; m_psy = d->h; QPainter bufferPainter(&m_buffer); bufferPainter.fillRect(m_px, m_py, m_psx, m_psy, QBrush(d->currImage)); bufferPainter.end(); repaint(); } d->x += d->dx; } else { // vertical sweep if (((d->subType == 2) && (d->y < -64)) || ((d->subType == 3) && (d->y > d->h + 64))) { showCurrentImage(); return -1; } int h; int y; int i; for (h = 2, i = 4, y = d->y ; i > 0 ; --i, h <<= 1, y -= d->dy) { m_px = 0; m_py = y; m_psx = d->w; m_psy = h; QPainter bufferPainter(&m_buffer); bufferPainter.fillRect(m_px, m_py, m_psx, m_psy, QBrush(d->currImage)); bufferPainter.end(); repaint(); } d->y += d->dy; } return 20; } int PresentationWidget::effectMosaic(bool aInit) { int dim = 10; // Size of a cell (dim x dim) int margin = dim + (int)(dim / 4); if (aInit) { d->i = 30; // giri totaly d->pixelMatrix = new bool*[width()]; for (int x = 0 ; x < width() ; ++x) { d->pixelMatrix[x] = new bool[height()]; for (int y = 0 ; y < height() ; ++y) { d->pixelMatrix[x][y] = false; } } } if (d->i <= 0) { showCurrentImage(); return -1; } int w = width(); int h = height(); QPainter bufferPainter(&m_buffer); for (int x = 0 ; x < w ; x += (qrand() % margin) + dim) { for (int y = 0 ; y < h ; y += (qrand() % margin) + dim) { if (d->pixelMatrix[x][y] == true) { if (y != 0) { y--; } continue; } bufferPainter.fillRect(x, y, dim, dim, QBrush(d->currImage)); for (int i = 0 ; i < dim && (x + i) < w ; ++i) { for (int j = 0 ; j < dim && (y + j) < h ; ++j) { d->pixelMatrix[x+i][y+j] = true; } } } } bufferPainter.end(); repaint(); d->i--; return 20; } int PresentationWidget::effectCubism(bool aInit) { if (aInit) { d->alpha = M_PI * 2; d->w = width(); d->h = height(); d->i = 150; } if (d->i <= 0) { showCurrentImage(); return -1; } QPainterPath painterPath; QPainter bufferPainter(&m_buffer); d->x = qrand() % d->w; d->y = qrand() % d->h; int r = (qrand() % 100) + 100; m_px = d->x - r; m_py = d->y - r; m_psx = r; m_psy = r; QTransform transform; transform.rotate((qrand() % 20) - 10); QRect rect(m_px, m_py, m_psx, m_psy); bufferPainter.setTransform(transform); bufferPainter.fillRect(rect, QBrush(d->currImage)); bufferPainter.end(); repaint(); d->i--; return 10; } int PresentationWidget::effectRandom(bool /*aInit*/) { d->fileIndex--; return -1; } int PresentationWidget::effectGrowing(bool aInit) { if (aInit) { d->w = width(); d->h = height(); d->x = d->w >> 1; d->y = d->h >> 1; d->i = 0; d->fx = d->x / 100.0; d->fy = d->y / 100.0; } d->x = (d->w >> 1) - (int)(d->i * d->fx); d->y = (d->h >> 1) - (int)(d->i * d->fy); d->i++; if ((d->x < 0) || (d->y < 0)) { showCurrentImage(); return -1; } m_px = d->x; m_py = d->y; m_psx = d->w - (d->x << 1); m_psy = d->h - (d->y << 1); QPainter bufferPainter(&m_buffer); bufferPainter.fillRect(m_px, m_py, m_psx, m_psy, QBrush(d->currImage)); bufferPainter.end(); repaint(); return 20; } int PresentationWidget::effectHorizLines(bool aInit) { static int iyPos[] = { 0, 4, 2, 6, 1, 5, 3, 7, -1 }; if (aInit) { d->w = width(); d->h = height(); d->i = 0; } if (iyPos[d->i] < 0) { return -1; } int iPos; int until = d->h; QPainter bufferPainter(&m_buffer); QBrush brush = QBrush(d->currImage); for (iPos = iyPos[d->i] ; iPos < until ; iPos += 8) { bufferPainter.fillRect(0, iPos, d->w, 1, brush); } bufferPainter.end(); repaint(); d->i++; if (iyPos[d->i] >= 0) { return 160; } showCurrentImage(); return -1; } int PresentationWidget::effectVertLines(bool aInit) { static int ixPos[] = { 0, 4, 2, 6, 1, 5, 3, 7, -1 }; if (aInit) { d->w = width(); d->h = height(); d->i = 0; } if (ixPos[d->i] < 0) { return -1; } int iPos; int until = d->w; QPainter bufferPainter(&m_buffer); QBrush brush = QBrush(d->currImage); for (iPos = ixPos[d->i] ; iPos < until ; iPos += 8) { bufferPainter.fillRect(iPos, 0, 1, d->h, brush); } bufferPainter.end(); repaint(); d->i++; if (ixPos[d->i] >= 0) { return 160; } showCurrentImage(); return -1; } int PresentationWidget::effectMultiCircleOut(bool aInit) { int x, y, i; double alpha; if (aInit) { startPainter(); d->w = width(); d->h = height(); d->x = d->w; d->y = d->h >> 1; d->pa.setPoint(0, d->w >> 1, d->h >> 1); d->pa.setPoint(3, d->w >> 1, d->h >> 1); d->fy = sqrt((double)d->w * d->w + d->h * d->h) / 2; d->i = qrand() % 15 + 2; d->fd = M_PI * 2 / d->i; d->alpha = d->fd; d->wait = 10 * d->i; d->fx = M_PI / 32; // divisor must be powers of 8 } if (d->alpha < 0) { showCurrentImage(); return -1; } for (alpha = d->alpha, i = d->i ; i >= 0 ; --i, alpha += d->fd) { x = (d->w >> 1) + (int)(d->fy * cos(-alpha)); y = (d->h >> 1) + (int)(d->fy * sin(-alpha)); d->x = (d->w >> 1) + (int)(d->fy * cos(-alpha + d->fx)); d->y = (d->h >> 1) + (int)(d->fy * sin(-alpha + d->fx)); d->pa.setPoint(1, x, y); d->pa.setPoint(2, d->x, d->y); QPainterPath painterPath; painterPath.addPolygon(QPolygon(d->pa)); QPainter bufferPainter(&m_buffer); bufferPainter.fillPath(painterPath, QBrush(d->currImage)); bufferPainter.end(); repaint(); } d->alpha -= d->fx; return d->wait; } int PresentationWidget::effectSpiralIn(bool aInit) { if (aInit) { update(); d->w = width(); d->h = height(); d->ix = d->w / 8; d->iy = d->h / 8; d->x0 = 0; d->x1 = d->w - d->ix; d->y0 = d->iy; d->y1 = d->h - d->iy; d->dx = d->ix; d->dy = 0; d->i = 0; d->j = 16 * 16; d->x = 0; d->y = 0; } if ((d->i == 0) && (d->x0 >= d->x1)) { showCurrentImage(); return -1; } if ((d->i == 0) && (d->x >= d->x1)) // switch to: down on right side { d->i = 1; d->dx = 0; d->dy = d->iy; d->x1 -= d->ix; } else if ((d->i == 1) && (d->y >= d->y1)) // switch to: right to left on bottom side { d->i = 2; d->dx = -d->ix; d->dy = 0; d->y1 -= d->iy; } else if ((d->i == 2) && (d->x <= d->x0)) // switch to: up on left side { d->i = 3; d->dx = 0; d->dy = -d->iy; d->x0 += d->ix; } else if ((d->i == 3) && (d->y <= d->y0)) // switch to: left to right on top side { d->i = 0; d->dx = d->ix; d->dy = 0; d->y0 += d->iy; } m_px = d->x; m_py = d->y; m_psx = d->ix; m_psy = d->iy; QPainter bufferPainter(&m_buffer); bufferPainter.fillRect(m_px, m_py, m_psx, m_psy, QBrush(d->currImage)); bufferPainter.end(); repaint(); d->x += d->dx; d->y += d->dy; d->j--; return 8; } int PresentationWidget::effectCircleOut(bool aInit) { int x, y; if (aInit) { startPainter(); d->w = width(); d->h = height(); d->x = d->w; d->y = d->h >> 1; d->alpha = 2 * M_PI; d->pa.setPoint(0, d->w >> 1, d->h >> 1); d->pa.setPoint(3, d->w >> 1, d->h >> 1); d->fx = M_PI / 16; // divisor must be powers of 8 d->fy = sqrt((double)d->w * d->w + d->h * d->h) / 2; } if (d->alpha < 0) { showCurrentImage(); return -1; } x = d->x; y = d->y; d->x = (d->w >> 1) + (int)(d->fy * cos(d->alpha)); d->y = (d->h >> 1) + (int)(d->fy * sin(d->alpha)); d->alpha -= d->fx; d->pa.setPoint(1, x, y); d->pa.setPoint(2, d->x, d->y); QPainterPath painterPath; painterPath.addPolygon(QPolygon(d->pa)); QPainter bufferPainter(&m_buffer); bufferPainter.fillPath(painterPath, QBrush(d->currImage)); bufferPainter.end(); repaint(); return 20; } int PresentationWidget::effectBlobs(bool aInit) { int r; if (aInit) { d->alpha = M_PI * 2; d->w = width(); d->h = height(); d->i = 150; } if (d->i <= 0) { showCurrentImage(); return -1; } d->x = qrand() % d->w; d->y = qrand() % d->h; r = (qrand() % 200) + 50; m_px = d->x - r; m_py = d->y - r; m_psx = r; m_psy = r; QPainterPath painterPath; painterPath.addEllipse(m_px, m_py, m_psx, m_psy); QPainter bufferPainter(&m_buffer); bufferPainter.fillPath(painterPath, QBrush(d->currImage)); bufferPainter.end(); repaint(); d->i--; return 10; } } // namespace DigikamGenericPresentationPlugin