/* ============================================================ * * This file is a part of kipi-plugins project * http://www.kipi-plugins.org * * Date : 2007-11-14 * Description : a kipi plugin to slide images. * * Copyright (C) 2007 by Valerio Fuoglio * * Parts of this code are based on smoothslidesaver by Carsten Weinhold * and slideshowgl.{cpp|h} 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. * * ============================================================ */ // C++ includes. #include #include // TQt includes. #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include // Local includes. #include "slideshowkb.h" #include "imageloadthread.h" #include "slideshowkb.moc" namespace KIPISlideShowPlugin { // ------------------------------------------------------------------------- ViewTrans::ViewTrans(bool zoomIn, float relAspect) { int i; // randomly select sizes of start end end viewport double s[2]; i = 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 xor 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], y[2], xMargin[2], yMargin[2], bestDist; double sx, sy; 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); } // ------------------------------------------------------------------------- Image::Image(ViewTrans *viewTrans, float aspect) { this->m_viewTrans = viewTrans; this->m_aspect = aspect; this->m_pos = 0.0; this->m_opacity = 0.0; this->m_paint = (m_viewTrans) ? true : false; this->m_texture = 0; } Image::~Image() { delete m_viewTrans; if (glIsTexture(m_texture)) glDeleteTextures(1, &m_texture); } // ------------------------------------------------------------------------- SlideShowKB::SlideShowKB(const TQValueList >& fileList, const TQStringList& commentsList, bool ImagesHasComments) : TQGLWidget(0, 0, 0, WStyle_StaysOnTop | WType_Popup | WX11BypassWM | WDestructiveClose) { #if KDE_IS_VERSION(3,2,0) TQRect deskRect = KGlobalSettings::desktopGeometry(this); m_deskX = deskRect.x(); m_deskY = deskRect.y(); m_deskWidth = deskRect.width(); m_deskHeight = deskRect.height(); #else TQRect deskRect = TQApplication::desktop()->screenGeometry(this); m_deskX = deskRect.x(); m_deskY = deskRect.y(); m_deskWidth = deskRect.width(); m_deskHeight = deskRect.height(); #endif move(m_deskX, m_deskY); resize(m_deskWidth, m_deskHeight); // ======================================================= // Avoid boring compile time "unused parameter" warning :P // These parameters could be useful for future implementations m_commentsList = commentsList; m_imagesHasComments = ImagesHasComments; // ======================================================= srand(TQTime::currentTime().msec()); m_config = new KConfig("kipirc"); m_config->setGroup("SlideShow Settings"); readSettings(); m_screen = new ScreenProperties(this); m_screen->enableVSync(); unsigned frameRate; if (m_forceFrameRate == 0) frameRate = m_screen->suggestFrameRate() * 2; else frameRate = m_forceFrameRate; m_image[0] = new Image(0); m_image[1] = new Image(0); m_effect = 0; m_step = 1.0 / ((float) (m_delay * frameRate)); m_zoomIn = rand() < RAND_MAX / 2; m_initialized = false; m_haveImages = true; TQValueList > m_fileList = fileList; m_imageLoadThread = new ImageLoadThread(m_fileList, width(), height()); m_timer = new TQTimer(this); m_endOfShow = false; m_showingEnd = false; connect(m_timer, TQT_SIGNAL(timeout(void)), this, TQT_SLOT(moveSlot())); connect(m_imageLoadThread, TQT_SIGNAL(endOfShow(void)), this, TQT_SLOT(slotEndOfShow())); // -- hide cursor when not moved -------------------- m_mouseMoveTimer = new TQTimer; connect(m_mouseMoveTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotMouseMoveTimeOut())); setMouseTracking(true); slotMouseMoveTimeOut(); m_imageLoadThread->start(); m_timer->start(1000 / frameRate); } SlideShowKB::~SlideShowKB() { delete m_effect; delete m_image[0]; delete m_image[1]; m_imageLoadThread->quit(); bool terminated = m_imageLoadThread->wait(10000); if ( !terminated) { m_imageLoadThread->terminate(); terminated = m_imageLoadThread->wait(3000); } if (terminated) delete m_imageLoadThread; delete m_mouseMoveTimer; delete m_timer; delete m_screen; } void SlideShowKB::setNewKBEffect() { KBEffect::Type type; bool needFadeIn = (m_effect == 0 || m_effect->type() == KBEffect::Fade); // we currently only have two effects if (m_disableFadeInOut) type = KBEffect::Blend; else if (m_disableCrossFade) type = KBEffect::Fade; else type = KBEffect::chooseKBEffect((m_effect) ? m_effect->type() : KBEffect::Fade); delete m_effect; switch (type) { case KBEffect::Fade: m_effect = new FadeKBEffect(this, needFadeIn); break; case KBEffect::Blend: m_effect = new BlendKBEffect(this, needFadeIn); break; default: qDebug("Unknown transition effect, falling back to crossfade"); m_effect = new BlendKBEffect(this, needFadeIn); } } void SlideShowKB::moveSlot() { if (m_initialized) { if (m_effect->done()) { setNewKBEffect(); m_imageLoadThread->requestNewImage(); } m_effect->advanceTime(m_step); } updateGL(); } bool SlideShowKB::setupNewImage(int idx) { assert(idx >= 0 && idx < 2); if ( !m_haveImages) return false; bool ok = false; m_zoomIn = !m_zoomIn; if (m_imageLoadThread->grabImage()) { delete m_image[idx]; // we have the image lock and there is an image float imageAspect = m_imageLoadThread->imageAspect(); ViewTrans *viewTrans = new ViewTrans(m_zoomIn, aspect() / imageAspect); m_image[idx] = new Image(viewTrans, imageAspect); applyTexture(m_image[idx], m_imageLoadThread->image()); ok = true; } else { m_haveImages = false; } // don't forget to release the lock on the copy of the image // owned by the image loader thread m_imageLoadThread->ungrabImage(); return ok; } void SlideShowKB::startSlideShowOnce() { // when the image loader thread is ready, it will already have loaded // the first image if (m_initialized == false && m_imageLoadThread->ready()) { setupNewImage(0); // setup the first image and m_imageLoadThread->requestNewImage(); // load the next one in background setNewKBEffect(); // set the initial effect m_initialized = true; } } void SlideShowKB::swapImages() { Image *tmp = m_image[0]; m_image[0] = m_image[1]; m_image[1] = tmp; } void SlideShowKB::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 SlideShowKB::paintGL() { startSlideShowOnce(); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); // only clear the color buffer, if none of the active images is fully opaque if ( !((m_image[0]->m_paint && m_image[0]->m_opacity == 1.0) || (m_image[1]->m_paint && m_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 (m_endOfShow && m_image[0]->m_paint && m_image[1]->m_paint) { endOfShow(); m_timer->stop(); } else { if (m_image[1]->m_paint) paintTexture(m_image[1]); if (m_image[0]->m_paint) paintTexture(m_image[0]); } glFlush(); } void SlideShowKB::resizeGL(int w, int h) { glViewport(0, 0, (GLint) w, (GLint) h); } void SlideShowKB::applyTexture(Image *img, const TQImage &texture) { /* create the texture */ glGenTextures(1, &img->m_texture); glBindTexture(GL_TEXTURE_2D, img->m_texture); /* actually generate the texture */ glTexImage2D(GL_TEXTURE_2D, 0, 3, texture.width(), texture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits()); /* enable linear filtering */ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); } void SlideShowKB::paintTexture(Image *img) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float sx = img->m_viewTrans->m_xScaleCorrect(); float sy = img->m_viewTrans->m_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); GLuint& tex = img->m_texture; glBindTexture(GL_TEXTURE_2D, tex); 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 SlideShowKB::readSettings() { m_delay = m_config->readUnsignedNumEntry("Delay", 8000)/1000; m_disableFadeInOut = m_config->readBoolEntry("KB Disable FadeInOut", false); m_disableCrossFade = m_config->readBoolEntry("KB Disable Crossfade", false); m_forceFrameRate = m_config->readUnsignedNumEntry("KB Force Framerate", 0); if (m_delay < 5) m_delay = 5; // if (m_delay > 20) m_delay = 20; if (m_forceFrameRate > 120) m_forceFrameRate = 120; } void SlideShowKB::endOfShow() { TQPixmap pix(512,512); pix.fill(TQt::black); TQFont fn(font()); fn.setPointSize(fn.pointSize()+10); fn.setBold(true); TQPainter p(&pix); p.setPen(TQt::white); p.setFont(fn); p.drawText(20, 50, i18n("SlideShow Completed.")); p.drawText(20, 100, i18n("Click To Exit...")); p.end(); TQImage image(pix.convertToImage()); TQImage t = convertToGLFormat(image); GLuint tex; /* create the texture */ glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); /* actually generate the texture */ glTexImage2D( GL_TEXTURE_2D, 0, 3, t.width(), t.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t.bits() ); /* enable linear filtering */ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); /* paint the texture */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); 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(); m_showingEnd = true; } TQStringList SlideShowKB::effectNames() { TQStringList effects; effects.append("Ken Burns"); return effects; } TQMap SlideShowKB::effectNamesI18N() { TQMap effects; effects["Ken Burns"] = i18n("Ken Burns"); return effects; } void SlideShowKB::mousePressEvent(TQMouseEvent *e) { // ======================================================= // Avoid boring compile time "unused parameter" warning :P // This parameter could be useful for future implementations if ( !e ) { /* TODO */ } // ======================================================= if (m_endOfShow && m_showingEnd) slotClose(); } void SlideShowKB::mouseMoveEvent(TQMouseEvent *e) { setCursor(TQCursor(TQt::ArrowCursor)); m_mouseMoveTimer->start(1000, true); TQPoint pos(e->pos()); } void SlideShowKB::slotEndOfShow() { m_endOfShow = true; } void SlideShowKB::slotMouseMoveTimeOut() { TQPoint pos(TQCursor::pos()); if ((pos.y() < (m_deskY+20)) || (pos.y() > (m_deskY+m_deskHeight-20-1))) return; setCursor(TQCursor(TQt::BlankCursor)); } void SlideShowKB::slotClose() { close(); } } // NameSpace KIPISlideShowPlugin