You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kipi-plugins/kipi-plugins/slideshow/slideshowkb.cpp

579 lines
15 KiB

/* ============================================================
*
* 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 <valerio dot fuoglio at gmail dot com>
*
* Parts of this code are based on smoothslidesaver by Carsten Weinhold
* <carsten dot weinhold at gmx dot de> and slideshowgl.{cpp|h} by Renchi Raju
* <renchi@pooh.tam.uiuc.edu>
*
* 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 <cassert>
#include <cmath>
// TQt includes.
#include <tqapplication.h>
#include <tqimage.h>
#include <tqdatetime.h>
#include <tqpainter.h>
#include <tqobject.h>
#include <tqfont.h>
#include <tqcursor.h>
// KDE includes.
#include <kconfig.h>
#include <kglobal.h>
#include <klocale.h>
#include <tdeversion.h>
#include <kglobalsettings.h>
// 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<TQPair<TQString, int> >& 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<TQPair<TQString, int> > 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<TQString,TQString> SlideShowKB::effectNamesI18N()
{
TQMap<TQString,TQString> 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