diff -r 6aeb7a756187 -r 3c88a81ff781 browsercore/appfw/Common/PictureFlow.cpp --- a/browsercore/appfw/Common/PictureFlow.cpp Thu Sep 23 15:32:11 2010 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1637 +0,0 @@ -/* - PictureFlow - animated image show widget - http://pictureflow.googlecode.com - - Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). - Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) - Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#include "PictureFlow.h" - -// detect Qt version -#if QT_VERSION >= 0x040000 -#define PICTUREFLOW_QT4 -#elif QT_VERSION >= 0x030000 -#define PICTUREFLOW_QT3 -#elif QT_VERSION >= 235 -#define PICTUREFLOW_QT2 -#else -#error PictureFlow widgets need Qt 2, Qt 3 or Qt 4 -#endif - -#include -#include -#include - -#ifdef PICTUREFLOW_QT4 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include - -#ifdef PICTUREFLOW_QT3 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define qMax(x,y) ((x) > (y)) ? (x) : (y) -#define qMin(x,y) ((x) < (y)) ? (x) : (y) - -#define QVector QValueVector - -#define toImage convertToImage -#define contains find -#define modifiers state -#define ControlModifier ControlButton -#endif - -#ifdef PICTUREFLOW_QT2 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define qMax(x,y) ((x) > (y)) ? (x) : (y) -#define qMin(x,y) ((x) < (y)) ? (x) : (y) - -#define QVector QArray - -#define toImage convertToImage -#define contains find -#define modifiers state -#define ControlModifier ControlButton -#define flush flushX -#endif - -// for fixed-point arithmetic, we need minimum 32-bit long -// long long (64-bit) might be useful for multiplication and division -typedef long PFreal; -#define PFREAL_SHIFT 10 -#define PFREAL_ONE (1 << PFREAL_SHIFT) - -#define IANGLE_MAX 1024 -#define IANGLE_MASK 1023 - -namespace WRT { - -const int slideRatio1 = 2; -const int slideRatio2 = 5; -const int KScrollTimeout = 250; - -inline PFreal fmul(PFreal a, PFreal b) -{ - return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT; -} - -inline PFreal fdiv(PFreal num, PFreal den) -{ - long long p = (long long)(num) << (PFREAL_SHIFT*2); - long long q = p / (long long)den; - long long r = q >> PFREAL_SHIFT; - - return r; -} - -inline PFreal fsin(int iangle) -{ - // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! - static const PFreal tab[] = { - 3, 103, 202, 300, 394, 485, 571, 652, - 726, 793, 853, 904, 947, 980, 1004, 1019, - 1023, 1018, 1003, 978, 944, 901, 849, 789, - 721, 647, 566, 479, 388, 294, 196, 97, - -4, -104, -203, -301, -395, -486, -572, -653, - -727, -794, -854, -905, -948, -981, -1005, -1020, - -1024, -1019, -1004, -979, -945, -902, -850, -790, - -722, -648, -567, -480, -389, -295, -197, -98, - 3 - }; - - while(iangle < 0) - iangle += IANGLE_MAX; - iangle &= IANGLE_MASK; - - int i = (iangle >> 4); - PFreal p = tab[i]; - PFreal q = tab[(i+1)]; - PFreal g = (q - p); - return p + g * (iangle-i*16)/16; -} - -inline PFreal fcos(int iangle) -{ - return fsin(iangle + (IANGLE_MAX >> 2)); -} - -/* ---------------------------------------------------------- - -PictureFlowState stores the state of all slides, i.e. all the necessary -information to be able to render them. - -PictureFlowAnimator is responsible to move the slides during the -transition between slides, to achieve the effect similar to Cover Flow, -by changing the state. - -PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is -the actual 3-d renderer. It should render all slides given the state -(an instance of PictureFlowState). - -Instances of all the above three classes are stored in -PictureFlowPrivate. - -------------------------------------------------------- */ - -struct SlideInfo -{ - int slideIndex; - int angle; - PFreal cx; - PFreal cy; - int blend; -}; - -class PictureFlowState -{ -public: - PictureFlowState(); - ~PictureFlowState(); - - void reposition(); - void reset(); - - QRgb backgroundColor; - int slideWidth; - int slideHeight; - ReflectionEffect reflectionEffect; - QVector slideImages; - - int angle; - int spacing; - PFreal offsetX; - PFreal offsetY; - - SlideInfo centerSlide; - QVector leftSlides; - QVector rightSlides; - int centerIndex; -}; - -class PictureFlowAnimator -{ -public: - PictureFlowAnimator(); - PictureFlowState* state; - - void start(int slide); - void stop(int slide); - void update(); - - int target; - int step; - int frame; - QTimer animateTimer; -}; - -class PictureFlowAbstractRenderer -{ -public: - PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0), gWidget(0), gPainter(0) {} - virtual ~PictureFlowAbstractRenderer() {} - - PictureFlowState* state; - bool dirty; - QWidget* widget; - QGraphicsWidget* gWidget; - QPainter* gPainter; - QRect cRect; // central rect - - virtual void init() = 0; - virtual void paint() = 0; -}; - -class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer -{ -public: - PictureFlowSoftwareRenderer(); - ~PictureFlowSoftwareRenderer(); - - virtual void init(); - virtual void paint(); - - QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1); -private: - QSize size; - QRgb bgcolor; - int effect; - QImage buffer; - QVector rays; - QImage* blankSurface; -#ifdef PICTUREFLOW_QT4 - QCache surfaceCache; - QHash imageHash; -#endif -#ifdef PICTUREFLOW_QT3 - QCache surfaceCache; - QMap imageHash; -#endif -#ifdef PICTUREFLOW_QT2 - QCache surfaceCache; - QIntDict imageHash; -#endif - - void render(); - void renderSlides(); - QImage* surface(int slideIndex); -}; - -// ------------- PictureFlowState --------------------------------------- - -PictureFlowState::PictureFlowState(): -backgroundColor(0), slideWidth(150), slideHeight(200), -reflectionEffect(BlurredReflection), centerIndex(0) -{ -} - -PictureFlowState::~PictureFlowState() -{ - for(int i = 0; i < (int)slideImages.count(); i++) - delete slideImages[i]; -} - -// readjust the settings, call this when slide dimension is changed -void PictureFlowState::reposition() -{ - angle = 60*IANGLE_MAX / 360; - - offsetX = slideWidth/2 * (PFREAL_ONE-fcos(angle)); - offsetY = slideWidth/2 * fsin(angle); - offsetX += slideWidth * PFREAL_ONE; - offsetY += slideWidth * PFREAL_ONE / 4; - spacing = 20; -} - -// adjust slides so that they are in "steady state" position -void PictureFlowState::reset() -{ - centerSlide.angle = 0; - centerSlide.cx = 0; - centerSlide.cy = 0; - centerSlide.slideIndex = centerIndex; - centerSlide.blend = 256; - - leftSlides.resize(6); - for(int i = 0; i < (int)leftSlides.count(); i++) - { - SlideInfo& si = leftSlides[i]; - si.angle = angle; - si.cx = -(offsetX + spacing*i*PFREAL_ONE); - si.cy = offsetY; - si.slideIndex = centerIndex-1-i; - si.blend = 256; - if(i == (int)leftSlides.count()-2) - si.blend = 128; - if(i == (int)leftSlides.count()-1) - si.blend = 0; - } - - rightSlides.resize(6); - for(int i = 0; i < (int)rightSlides.count(); i++) - { - SlideInfo& si = rightSlides[i]; - si.angle = -angle; - si.cx = offsetX + spacing*i*PFREAL_ONE; - si.cy = offsetY; - si.slideIndex = centerIndex+1+i; - si.blend = 256; - if(i == (int)rightSlides.count()-2) - si.blend = 128; - if(i == (int)rightSlides.count()-1) - si.blend = 0; - } -} - -// ------------- PictureFlowAnimator --------------------------------------- - -PictureFlowAnimator::PictureFlowAnimator(): -state(0), target(0), step(0), frame(0) -{ -} - -void PictureFlowAnimator::start(int slide) -{ - target = slide; - if(!animateTimer.isActive() && state) - { - step = (target < state->centerSlide.slideIndex) ? -1 : 1; - animateTimer.start(30); - } -} - -void PictureFlowAnimator::stop(int slide) -{ - step = 0; - target = slide; - frame = slide << 16; - animateTimer.stop(); -} - -void PictureFlowAnimator::update() -{ - if(!animateTimer.isActive()) - return; - if(step == 0) - return; - if(!state) - return; - - int speed = 16384/4; - -#if 1 - // deaccelerate when approaching the target - // we disabled clicking until the animation is done so this has to be fast enough to not annoy users - const int max = 65536 + 16384; // was 65536*2 but that was too slow when we disabled clicks - - int fi = frame; - fi -= (target << 16); - if(fi < 0) - fi = -fi; - fi = qMin(fi, max); - - int ia = IANGLE_MAX * (fi-max/2) / (max*2); - speed = 512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE; -#endif - - frame += speed*step; - - int index = frame >> 16; - int pos = frame & 0xffff; - int neg = 65536 - pos; - int tick = (step < 0) ? neg : pos; - PFreal ftick = (tick * PFREAL_ONE) >> 16; - - if(step < 0) - index++; - - if(state->centerIndex != index) - { - state->centerIndex = index; - frame = index << 16; - state->centerSlide.slideIndex = state->centerIndex; - for(int i = 0; i < (int)state->leftSlides.count(); i++) - state->leftSlides[i].slideIndex = state->centerIndex-1-i; - for(int i = 0; i < (int)state->rightSlides.count(); i++) - state->rightSlides[i].slideIndex = state->centerIndex+1+i; - } - - state->centerSlide.angle = (step * tick * state->angle) >> 16; - state->centerSlide.cx = -step * fmul(state->offsetX, ftick); - state->centerSlide.cy = fmul(state->offsetY, ftick); - - if(state->centerIndex == target) - { - stop(target); - state->reset(); - return; - } - - for(int i = 0; i < (int)state->leftSlides.count(); i++) - { - SlideInfo& si = state->leftSlides[i]; - si.angle = state->angle; - si.cx = -(state->offsetX + state->spacing*i*PFREAL_ONE + step*state->spacing*ftick); - si.cy = state->offsetY; - } - - for(int i = 0; i < (int)state->rightSlides.count(); i++) - { - SlideInfo& si = state->rightSlides[i]; - si.angle = -state->angle; - si.cx = state->offsetX + state->spacing*i*PFREAL_ONE - step*state->spacing*ftick; - si.cy = state->offsetY; - } - - if(step > 0) - { - PFreal ftick = (neg * PFREAL_ONE) >> 16; - state->rightSlides[0].angle = -(neg * state->angle) >> 16; - state->rightSlides[0].cx = fmul(state->offsetX, ftick); - state->rightSlides[0].cy = fmul(state->offsetY, ftick); - } - else - { - PFreal ftick = (pos * PFREAL_ONE) >> 16; - state->leftSlides[0].angle = (pos * state->angle) >> 16; - state->leftSlides[0].cx = -fmul(state->offsetX, ftick); - state->leftSlides[0].cy = fmul(state->offsetY, ftick); - } - - // must change direction ? - if(target < index) if(step > 0) - step = -1; - if(target > index) if(step < 0) - step = 1; - - // the first and last slide must fade in/fade out - int nleft = state->leftSlides.count(); - int nright = state->rightSlides.count(); - int fade = pos / 256; - - for(int index = 0; index < nleft; index++) - { - int blend = 256; - if(index == nleft-1) - blend = (step > 0) ? 0 : 128-fade/2; - if(index == nleft-2) - blend = (step > 0) ? 128-fade/2 : 256-fade/2; - if(index == nleft-3) - blend = (step > 0) ? 256-fade/2 : 256; - state->leftSlides[index].blend = blend; - } - for(int index = 0; index < nright; index++) - { - int blend = (index < nright-2) ? 256 : 128; - if(index == nright-1) - blend = (step > 0) ? fade/2 : 0; - if(index == nright-2) - blend = (step > 0) ? 128+fade/2 : fade/2; - if(index == nright-3) - blend = (step > 0) ? 256 : 128+fade/2; - state->rightSlides[index].blend = blend; - } -} - -// ------------- PictureFlowSoftwareRenderer --------------------------------------- - -PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer(): -PictureFlowAbstractRenderer(), size(0,0), bgcolor(0), effect(-1), blankSurface(0) -{ -#ifdef PICTUREFLOW_QT3 - surfaceCache.setAutoDelete(true); -#endif -} - -PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer() -{ - surfaceCache.clear(); - buffer = QImage(); - delete blankSurface; -} - -void PictureFlowSoftwareRenderer::paint() -{ - if(!widget && !gWidget) - return; - - if(widget && widget->size() != size) - init(); - else if (gWidget && gWidget->size().toSize() != size) - init(); - - if(state->backgroundColor != bgcolor) - { - bgcolor = state->backgroundColor; - surfaceCache.clear(); - } - - if((int)(state->reflectionEffect) != effect) - { - effect = (int)state->reflectionEffect; - surfaceCache.clear(); - } - - if(dirty) - render(); - - if (widget) { - QPainter painter(widget); - painter.drawImage(QPoint(0,0), buffer); - } else if (gWidget && gPainter) { - gPainter->drawImage(QPoint(0,0), buffer); - } -} - -void PictureFlowSoftwareRenderer::init() -{ - if(!widget && !gWidget) - return; - - surfaceCache.clear(); - blankSurface = 0; - - if (widget) - size = widget->size(); - else - size = gWidget->size().toSize(); - - int ww = size.width(); - int wh = size.height(); - int w = (ww+1)/2; - int h = (wh+1)/2; - -#ifdef PICTUREFLOW_QT4 - buffer = QImage(ww, wh, QImage::Format_RGB32); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - buffer.create(ww, wh, 32); -#endif - buffer.fill(bgcolor); - - rays.resize(w*2); - for(int i = 0; i < w; i++) - { - PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2*h); - rays[w-i-1] = -gg; - rays[w+i] = gg; - } - - dirty = true; -} - -// TODO: optimize this with lookup tables -static QRgb blendColor(QRgb c1, QRgb c2, int blend) -{ - int r = qRed(c1) * blend/256 + qRed(c2)*(256-blend)/256; - int g = qGreen(c1) * blend/256 + qGreen(c2)*(256-blend)/256; - int b = qBlue(c1) * blend/256 + qBlue(c2)*(256-blend)/256; - return qRgb(r, g, b); -} - - -static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor, -ReflectionEffect reflectionEffect) -{ -#ifdef PICTUREFLOW_QT4 - Qt::TransformationMode mode = Qt::SmoothTransformation; - QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QImage img = slideImage->smoothScale(w, h); -#endif - - // slightly larger, to accomodate for the reflection - int hs = h * 2; - int hofs = h / 3; - - // offscreen buffer: black is sweet -#ifdef PICTUREFLOW_QT4 - QImage* result = new QImage(hs, w, QImage::Format_RGB32); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QImage* result = new QImage; - result->create(hs, w, 32); -#endif - result->fill(bgcolor); - - // transpose the image, this is to speed-up the rendering - // because we process one column at a time - // (and much better and faster to work row-wise, i.e in one scanline) - for(int x = 0; x < w; x++) - for(int y = 0; y < h; y++) - result->setPixel(hofs + y, x, img.pixel(x, y)); - - if(reflectionEffect != NoReflection) - { - // create the reflection - int ht = hs - h - hofs; - int hte = ht; - for(int x = 0; x < w; x++) - for(int y = 0; y < ht; y++) - { - QRgb color = img.pixel(x, img.height()-y-1); - result->setPixel(h+hofs+y, x, blendColor(color,bgcolor,128*(hte-y)/hte)); - } - - if(reflectionEffect == BlurredReflection) - { - // blur the reflection everything first - // Based on exponential blur algorithm by Jani Huhtanen - QRect rect(hs/2, 0, hs/2, w); - rect &= result->rect(); - - int r1 = rect.top(); - int r2 = rect.bottom(); - int c1 = rect.left(); - int c2 = rect.right(); - - int bpl = result->bytesPerLine(); - int rgba[4]; - unsigned char* p; - - // how many times blur is applied? - // for low-end system, limit this to only 1 loop - for(int loop = 0; loop < 2; loop++) - { - for(int col = c1; col <= c2; col++) - { - p = result->scanLine(r1) + col*4; - for(int i = 0; i < 3; i++) - rgba[i] = p[i] << 4; - - p += bpl; - for(int j = r1; j < r2; j++, p += bpl) - for(int i = 0; i < 3; i++) - p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; - } - - for(int row = r1; row <= r2; row++) - { - p = result->scanLine(row) + c1*4; - for(int i = 0; i < 3; i++) - rgba[i] = p[i] << 4; - - p += 4; - for(int j = c1; j < c2; j++, p+=4) - for(int i = 0; i < 3; i++) - p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; - } - - for(int col = c1; col <= c2; col++) - { - p = result->scanLine(r2) + col*4; - for(int i = 0; i < 3; i++) - rgba[i] = p[i] << 4; - - p -= bpl; - for(int j = r1; j < r2; j++, p -= bpl) - for(int i = 0; i < 3; i++) - p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; - } - - for(int row = r1; row <= r2; row++) - { - p = result->scanLine(row) + c2*4; - for(int i = 0; i < 3; i++) - rgba[i] = p[i] << 4; - - p -= 4; - for(int j = c1; j < c2; j++, p-=4) - for(int i = 0; i < 3; i++) - p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; - } - } - - // overdraw to leave only the reflection blurred (but not the actual image) - for(int x = 0; x < w; x++) - for(int y = 0; y < h; y++) - result->setPixel(hofs + y, x, img.pixel(x, y)); - } - } - - return result; -} - -QImage* PictureFlowSoftwareRenderer::surface(int slideIndex) -{ - if(!state) - return 0; - if(slideIndex < 0) - return 0; - if(slideIndex >= (int)state->slideImages.count()) - return 0; - -#ifdef PICTUREFLOW_QT4 - int key = slideIndex; -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QString key = QString::number(slideIndex); -#endif - - QImage* img = state->slideImages.at(slideIndex); - bool empty = img ? img->isNull() : true; - if(empty) - { - surfaceCache.remove(key); - imageHash.remove(slideIndex); - if(!blankSurface) - { - int sw = state->slideWidth; - int sh = state->slideHeight; - -#ifdef PICTUREFLOW_QT4 - QImage img = QImage(sw, sh, QImage::Format_RGB32); - - QPainter painter(&img); - QPoint p1(sw*4/10, 0); - QPoint p2(sw*6/10, sh); - QLinearGradient linearGrad(p1, p2); - linearGrad.setColorAt(0, Qt::black); - linearGrad.setColorAt(1, Qt::white); - painter.setBrush(linearGrad); - painter.fillRect(0, 0, sw, sh, QBrush(linearGrad)); - - painter.setPen(QPen(QColor(64,64,64), 4)); - painter.setBrush(QBrush()); - painter.drawRect(2, 2, sw-3, sh-3); - painter.end(); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QPixmap pixmap(sw, sh, 32); - QPainter painter(&pixmap); - painter.fillRect(pixmap.rect(), QColor(192,192,192)); - painter.fillRect(5, 5, sw-10, sh-10, QColor(64,64,64)); - painter.end(); - QImage img = pixmap.convertToImage(); -#endif - - blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect); - } - return blankSurface; - } - -#ifdef PICTUREFLOW_QT4 - bool exist = imageHash.contains(slideIndex); - if(exist) - if(img == imageHash.find(slideIndex).value()) -#endif -#ifdef PICTUREFLOW_QT3 - bool exist = imageHash.find(slideIndex) != imageHash.end(); - if(exist) - if(img == imageHash.find(slideIndex).data()) -#endif -#ifdef PICTUREFLOW_QT2 - if(img == imageHash[slideIndex]) -#endif - if(surfaceCache.contains(key)) - return surfaceCache[key]; - - QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect); - surfaceCache.insert(key, sr); - imageHash.insert(slideIndex, img); - - return sr; -} - -// Renders a slide to offscreen buffer. Returns a rect of the rendered area. -// col1 and col2 limit the column for rendering. -QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2) -{ - int blend = slide.blend; - if(!blend) - return QRect(); - - QImage* src = surface(slide.slideIndex); - if(!src) - return QRect(); - - QRect rect(0, 0, 0, 0); - - int sw = src->height(); - int sh = src->width(); - int h = buffer.height(); - int w = buffer.width(); - - if(col1 > col2) - { - int c = col2; - col2 = col1; - col1 = c; - } - - col1 = (col1 >= 0) ? col1 : 0; - col2 = (col2 >= 0) ? col2 : w-1; - col1 = qMin(col1, w-1); - col2 = qMin(col2, w-1); - - int zoom = 100; - int distance = h * 100 / zoom; - PFreal sdx = fcos(slide.angle); - PFreal sdy = fsin(slide.angle); - PFreal xs = slide.cx - state->slideWidth * sdx/2; - PFreal ys = slide.cy - state->slideWidth * sdy/2; - PFreal dist = distance * PFREAL_ONE; - int xi = qMax((PFreal)0, (w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys) >> PFREAL_SHIFT); - if(xi >= w) - return rect; - - bool flag = false; - rect.setLeft(xi); - - int centerY = 0; - for(int x = qMax(xi, col1); x <= col2; x++) - { - PFreal hity = 0; - PFreal fk = rays[x]; - if(sdy) - { - fk = fk - fdiv(sdx,sdy); - hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk); - } - - dist = distance*PFREAL_ONE + hity; - if(dist < 0) - continue; - - PFreal hitx = fmul(dist, rays[x]); - PFreal hitdist = fdiv(hitx - slide.cx, sdx); - - int column = sw/2 + (hitdist >> PFREAL_SHIFT); - if(column >= sw) - break; - if(column < 0) - continue; - - rect.setRight(x); - if(!flag) - rect.setLeft(x); - flag = true; - - int y1 = h/2; - int y2 = y1+ 1; - centerY = y1; - QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x; - QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x; - QRgb pixelstep = pixel2 - pixel1; - - int center = (sh/2); - int dy = dist / h; - int p1 = center*PFREAL_ONE - dy/2; - int p2 = center*PFREAL_ONE + dy/2; - - const QRgb *ptr = (const QRgb*)(src->scanLine(column)); - if(blend == 256) - while((y1 >= 0) && (y2 < h) && (p1 >= 0)) - { - *pixel1 = ptr[p1 >> PFREAL_SHIFT]; - *pixel2 = ptr[p2 >> PFREAL_SHIFT]; - p1 -= dy; - p2 += dy; - y1--; - y2++; - pixel1 -= pixelstep; - pixel2 += pixelstep; - } - else - while((y1 >= 0) && (y2 < h) && (p1 >= 0)) - { - QRgb c1 = ptr[p1 >> PFREAL_SHIFT]; - QRgb c2 = ptr[p2 >> PFREAL_SHIFT]; - *pixel1 = blendColor(c1, bgcolor, blend); - *pixel2 = blendColor(c2, bgcolor, blend); - p1 -= dy; - p2 += dy; - y1--; - y2++; - pixel1 -= pixelstep; - pixel2 += pixelstep; - } - } - - int yTop = (3 * centerY - 2 * state->slideHeight) / 3; - rect.setTop(yTop); - rect.setBottom(state->slideHeight + yTop); - return rect; -} - -void PictureFlowSoftwareRenderer::renderSlides() -{ - int nleft = state->leftSlides.count(); - int nright = state->rightSlides.count(); - - QRect r = renderSlide(state->centerSlide); - int c1 = r.left(); - int c2 = r.right(); - cRect = r; - for(int index = 0; index < nleft; index++) - { - QRect rs = renderSlide(state->leftSlides[index], 0, c1-1); - if(!rs.isEmpty()) - c1 = rs.left(); - } - for(int index = 0; index < nright; index++) - { - QRect rs = renderSlide(state->rightSlides[index], c2+1, buffer.width()); - if(!rs.isEmpty()) - c2 = rs.right(); - } -} - -// Render the slides. Updates only the offscreen buffer. -void PictureFlowSoftwareRenderer::render() -{ - buffer.fill(state->backgroundColor); - renderSlides(); - dirty = false; -} - -// ----------------------------------------- - -class PictureFlowPrivate -{ -public: - PictureFlowState* state; - PictureFlowAnimator* animator; - PictureFlowAbstractRenderer* renderer; - QTimer triggerTimer; - QTimer scrollTimer; -}; - - -PictureFlow::PictureFlow(QWidget* parent): FlowInterface(parent) -{ - d = new PictureFlowPrivate; - - d->state = new PictureFlowState; - d->state->reset(); - d->state->reposition(); - - d->renderer = new PictureFlowSoftwareRenderer; - d->renderer->state = d->state; - d->renderer->widget = this; - d->renderer->init(); - - d->animator = new PictureFlowAnimator; - d->animator->state = d->state; - QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation())); - - QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render())); - QObject::connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll())); - -#ifdef PICTUREFLOW_QT4 - setAttribute(Qt::WA_StaticContents, true); - setAttribute(Qt::WA_OpaquePaintEvent, true); - setAttribute(Qt::WA_NoSystemBackground, true); -#endif -#ifdef PICTUREFLOW_QT3 - setWFlags(getWFlags() | Qt::WStaticContents); - setWFlags(getWFlags() | Qt::WNoAutoErase); -#endif -#ifdef PICTUREFLOW_QT2 - setWFlags(getWFlags() | Qt::WPaintClever); - setWFlags(getWFlags() | Qt::WRepaintNoErase); - setWFlags(getWFlags() | Qt::WResizeNoErase); -#endif -} - -PictureFlow::~PictureFlow() -{ - delete d->renderer; - delete d->animator; - delete d->state; - delete d; -} - -int PictureFlow::slideCount() const -{ - return d->state->slideImages.count(); -} - -QColor PictureFlow::backgroundColor() const -{ - return QColor(d->state->backgroundColor); -} - -void PictureFlow::setBackgroundColor(const QColor& c) -{ - d->state->backgroundColor = c.rgb(); - triggerRender(); -} - -QSize PictureFlow::slideSize() const -{ - return QSize(d->state->slideWidth, d->state->slideHeight); -} - -void PictureFlow::setSlideSize(QSize size) -{ - d->state->slideWidth = size.width(); - d->state->slideHeight = size.height(); - d->state->reposition(); - triggerRender(); -} - -ReflectionEffect PictureFlow::reflectionEffect() const -{ - return d->state->reflectionEffect; -} - -void PictureFlow::setReflectionEffect(ReflectionEffect effect) -{ - d->state->reflectionEffect = effect; - triggerRender(); -} - -QImage PictureFlow::slide(int index) const -{ - QImage* i = 0; - if((index >= 0) && (index < slideCount())) - i = d->state->slideImages[index]; - return i ? QImage(*i) : QImage(); -} - -void PictureFlow::addSlide(const QImage& image) -{ - int c = d->state->slideImages.count(); - d->state->slideImages.resize(c+1); - d->state->slideImages[c] = new QImage(image); - triggerRender(); -} - -void PictureFlow::addSlide(const QPixmap& pixmap) -{ - addSlide(pixmap.toImage()); -} - -void PictureFlow::setSlide(int index, const QImage& image) -{ - if((index >= 0) && (index < slideCount())) - { - QImage* i = image.isNull() ? 0 : new QImage(image); - delete d->state->slideImages[index]; - d->state->slideImages[index] = i; - triggerRender(); - } -} - -void PictureFlow::setSlide(int index, const QPixmap& pixmap) -{ - setSlide(index, pixmap.toImage()); -} - -int PictureFlow::centerIndex() const -{ - return d->state->centerIndex; -} - -bool PictureFlow::slideAnimationOngoing() const -{ - return d->animator->animateTimer.isActive(); -} - -void PictureFlow::setCenterIndex(int index) -{ - index = qMin(index, slideCount()-1); - index = qMax(index, 0); - d->state->centerIndex = index; - d->state->reset(); - d->animator->stop(index); - triggerRender(); -} - -void PictureFlow::clear() -{ - int c = d->state->slideImages.count(); - for(int i = 0; i < c; i++) - delete d->state->slideImages[i]; - d->state->slideImages.resize(0); - - d->state->reset(); - triggerRender(); -} - -void PictureFlow::render() -{ - d->renderer->dirty = true; - update(); -} - -void PictureFlow::triggerRender() -{ -#ifdef PICTUREFLOW_QT4 - d->triggerTimer.setSingleShot(true); - d->triggerTimer.start(0); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - d->triggerTimer.start(0, true); -#endif -} - -void PictureFlow::showPrevious() -{ - int step = d->animator->step; - int center = d->state->centerIndex; - - if(step > 0) - d->animator->start(center); - - if(step == 0) - if(center > 0) - d->animator->start(center - 1); - - if(step < 0) - d->animator->target = qMax(0, center - 2); -} - -void PictureFlow::showNext() -{ - int step = d->animator->step; - int center = d->state->centerIndex; - - if(step < 0) - d->animator->start(center); - - if(step == 0) - if(center < slideCount()-1) - d->animator->start(center + 1); - - if(step > 0) - d->animator->target = qMin(center + 2, slideCount()-1); -} - -void PictureFlow::showSlide(int index) -{ - index = qMax(index, 0); - index = qMin(slideCount()-1, index); - if(index == d->state->centerSlide.slideIndex) - return; - - d->animator->start(index); -} - -void PictureFlow::keyPressEvent(QKeyEvent* event) -{ - switch (event->key()) { - case Qt::Key_Escape: - emit cancel(); - return; - - case Qt::Key_Enter: - case Qt::Key_Return: - case Qt::Key_Select: - emit ok(centerIndex()); - return; - case Qt::Key_Left: - if(event->modifiers() == Qt::ControlModifier) - showSlide(centerIndex()-10); - else - showPrevious(); - event->accept(); - return; - case Qt::Key_Right: - if(event->modifiers() == Qt::ControlModifier) - showSlide(centerIndex()+10); - else - showNext(); - event->accept(); - return; - } - event->ignore(); -} - -void PictureFlow::mousePressEvent(QMouseEvent* event) -{ - m_lastMoveEventPos = event->pos(); - if (d->scrollTimer.isActive()) - d->scrollTimer.stop(); - d->scrollTimer.start(KScrollTimeout); - scroll(); -} - -void PictureFlow::mouseMoveEvent(QMouseEvent* event) -{ - m_lastMoveEventPos = event->pos(); -} - -void PictureFlow::mouseReleaseEvent(QMouseEvent* event) -{ - d->scrollTimer.stop(); - if (slideAnimationOngoing()) { -// qDebug() << "pf:mouseReleaseEvent slideanimation running, ignoring click"; - return; - } - if(event->x() > ((width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2)) && event->x() < ((width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) { - emit ok(centerIndex()); - return; - } -} - -void PictureFlow::scroll() -{ - if(m_lastMoveEventPos.x() < ((width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2))) { - showPrevious(); - } - else if (m_lastMoveEventPos.x() > ((width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) { - showNext(); - } -} - - -void PictureFlow::paintEvent(QPaintEvent* event) -{ - Q_UNUSED(event); - d->renderer->paint(); -} - -void PictureFlow::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - - QSize s = event->size(); //parentWidget()->rect().size(); - setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2)); -} - -void PictureFlow::updateAnimation() -{ - int old_center = d->state->centerIndex; - d->animator->update(); - triggerRender(); - if(d->state->centerIndex != old_center) - emit centerIndexChanged(d->state->centerIndex); -} - -void PictureFlow::init() -{ - QSize s = size(); //parentWidget()->rect().size(); - - setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2)); - //resize(s); -//TODO: Disable refrection ? -// setReflectionEffect(PictureFlow::NoReflection); - setBackgroundColor(Qt::black); - // ensure that system cursor is an arrow, not a random icon - // This is not an issue if the platform does not have a system cursor -#ifndef __SYMBIAN32__ - setCursor(Qt::ArrowCursor); -#endif - setFocusPolicy(Qt::WheelFocus); - setFocus(Qt::OtherFocusReason); -} - -QRect PictureFlow::centralRect() const -{ - if (d->renderer) { - /* Render the slide to get the rectangle */ - SlideInfo s = d->state->centerSlide; - QRect r = ((PictureFlowSoftwareRenderer*)d->renderer)->renderSlide(s); - return r; - } - else - return QRect(); -} - -//----------------------------------------------- -// GraphicsPictureFlow class - -GraphicsPictureFlow::GraphicsPictureFlow(QObject* parent): GraphicsFlowInterface(NULL) -{ - setParent(parent); - - d = new PictureFlowPrivate; - - d->state = new PictureFlowState; - d->state->reset(); - d->state->reposition(); - - d->renderer = new PictureFlowSoftwareRenderer; - d->renderer->state = d->state; - d->renderer->gWidget = this; - d->renderer->init(); - - d->animator = new PictureFlowAnimator; - d->animator->state = d->state; - QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation())); - - QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render())); - QObject::connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll())); - -#ifdef PICTUREFLOW_QT4 - setAttribute(Qt::WA_StaticContents, true); - setAttribute(Qt::WA_OpaquePaintEvent, true); - setAttribute(Qt::WA_NoSystemBackground, true); -#endif -#ifdef PICTUREFLOW_QT3 - setWFlags(getWFlags() | Qt::WStaticContents); - setWFlags(getWFlags() | Qt::WNoAutoErase); -#endif -#ifdef PICTUREFLOW_QT2 - setWFlags(getWFlags() | Qt::WPaintClever); - setWFlags(getWFlags() | Qt::WRepaintNoErase); - setWFlags(getWFlags() | Qt::WResizeNoErase); -#endif -} - -GraphicsPictureFlow::~GraphicsPictureFlow() -{ - delete d->renderer; - delete d->animator; - delete d->state; - delete d; -} - -int GraphicsPictureFlow::slideCount() const -{ - return d->state->slideImages.count(); -} - -QColor GraphicsPictureFlow::backgroundColor() const -{ - return QColor(d->state->backgroundColor); -} - -void GraphicsPictureFlow::setBackgroundColor(const QColor& c) -{ - d->state->backgroundColor = c.rgb(); - triggerRender(); -} - -QSize GraphicsPictureFlow::slideSize() const -{ - return QSize(d->state->slideWidth, d->state->slideHeight); -} - -void GraphicsPictureFlow::setSlideSize(QSize size) -{ - d->state->slideWidth = size.width(); - d->state->slideHeight = size.height(); - d->state->reposition(); - triggerRender(); -} - -ReflectionEffect GraphicsPictureFlow::reflectionEffect() const -{ - return d->state->reflectionEffect; -} - -void GraphicsPictureFlow::setReflectionEffect(ReflectionEffect effect) -{ - d->state->reflectionEffect = effect; - triggerRender(); -} - -QImage GraphicsPictureFlow::slide(int index) const -{ - QImage* i = 0; - if((index >= 0) && (index < slideCount())) - i = d->state->slideImages[index]; - return i ? QImage(*i) : QImage(); -} - -void GraphicsPictureFlow::addSlide(const QImage& image) -{ - int c = d->state->slideImages.count(); - d->state->slideImages.resize(c+1); - d->state->slideImages[c] = new QImage(image); - triggerRender(); -} - -void GraphicsPictureFlow::addSlide(const QPixmap& pixmap) -{ - addSlide(pixmap.toImage()); -} - -void GraphicsPictureFlow::setSlide(int index, const QImage& image) -{ - if((index >= 0) && (index < slideCount())) - { - QImage* i = image.isNull() ? 0 : new QImage(image); - delete d->state->slideImages[index]; - d->state->slideImages[index] = i; - triggerRender(); - } -} - -void GraphicsPictureFlow::setSlide(int index, const QPixmap& pixmap) -{ - setSlide(index, pixmap.toImage()); -} - -int GraphicsPictureFlow::centerIndex() const -{ - return d->state->centerIndex; -} - -bool GraphicsPictureFlow::slideAnimationOngoing() const -{ - return d->animator->animateTimer.isActive(); -} - -void GraphicsPictureFlow::setCenterIndex(int index) -{ - index = qMin(index, slideCount()-1); - index = qMax(index, 0); - d->state->centerIndex = index; - d->state->reset(); - d->animator->stop(index); - triggerRender(); -} - -void GraphicsPictureFlow::clear() -{ - int c = d->state->slideImages.count(); - for(int i = 0; i < c; i++) - delete d->state->slideImages[i]; - d->state->slideImages.resize(0); - - d->state->reset(); - triggerRender(); -} - -void GraphicsPictureFlow::render() -{ - d->renderer->dirty = true; - update(); -} - -void GraphicsPictureFlow::triggerRender() -{ -#ifdef PICTUREFLOW_QT4 - d->triggerTimer.setSingleShot(true); - d->triggerTimer.start(0); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - d->triggerTimer.start(0, true); -#endif -} - -void GraphicsPictureFlow::showPrevious() -{ - int step = d->animator->step; - int center = d->state->centerIndex; - - if(step > 0) - d->animator->start(center); - - if(step == 0) - if(center > 0) - d->animator->start(center - 1); - - if(step < 0) - d->animator->target = qMax(0, center - 2); -} - -void GraphicsPictureFlow::showNext() -{ - int step = d->animator->step; - int center = d->state->centerIndex; - - if(step < 0) - d->animator->start(center); - - if(step == 0) - if(center < slideCount()-1) - d->animator->start(center + 1); - - if(step > 0) - d->animator->target = qMin(center + 2, slideCount()-1); -} - -void GraphicsPictureFlow::showSlide(int index) -{ - index = qMax(index, 0); - index = qMin(slideCount()-1, index); - if(index == d->state->centerSlide.slideIndex) - return; - - d->animator->start(index); -} - -void GraphicsPictureFlow::keyPressEvent(QKeyEvent* event) -{ - switch (event->key()) { - case Qt::Key_Escape: - emit cancel(); - return; - - case Qt::Key_Enter: - case Qt::Key_Return: - case Qt::Key_Select: - emit ok(centerIndex()); - return; - case Qt::Key_Left: - if(event->modifiers() == Qt::ControlModifier) - showSlide(centerIndex()-10); - else - showPrevious(); - event->accept(); - return; - case Qt::Key_Right: - if(event->modifiers() == Qt::ControlModifier) - showSlide(centerIndex()+10); - else - showNext(); - event->accept(); - return; - } - event->ignore(); -} - -void GraphicsPictureFlow::mousePressEvent(QGraphicsSceneMouseEvent* event) -{ - m_lastMoveEventPos = event->pos().toPoint(); - if (d->scrollTimer.isActive()) - d->scrollTimer.stop(); - d->scrollTimer.start(KScrollTimeout); - scroll(); -} - -void GraphicsPictureFlow::mouseMoveEvent(QGraphicsSceneMouseEvent* event) -{ - m_lastMoveEventPos = event->pos().toPoint(); -} - -void GraphicsPictureFlow::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) -{ - d->scrollTimer.stop(); - if (slideAnimationOngoing()) { -// qDebug() << "pf:mouseReleaseEvent slideanimation running, ignoring click"; - return; - } - if(event->pos().x() > ((size().width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2)) && event->pos().x() < ((size().width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) { - emit ok(centerIndex()); - return; - } -} - -void GraphicsPictureFlow::scroll() -{ - if(m_lastMoveEventPos.x() < ((size().width() * (slideRatio2 - slideRatio1)) / (2 * slideRatio2))) { - showPrevious(); - } - else if (m_lastMoveEventPos.x() > ((size().width() * (slideRatio2 + slideRatio1)) / (2 * slideRatio2))) { - showNext(); - } -} - -void GraphicsPictureFlow::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) -{ - d->renderer->gPainter = painter; - d->renderer->paint(); - d->renderer->gPainter = NULL; -} - -void GraphicsPictureFlow::resizeEvent(QGraphicsSceneResizeEvent* event) -{ - QGraphicsWidget::resizeEvent(event); - - QSize s = event->newSize().toSize(); - setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2)); -} - -void GraphicsPictureFlow::updateAnimation() -{ - int old_center = d->state->centerIndex; - d->animator->update(); - triggerRender(); - if(d->state->centerIndex != old_center) - emit centerIndexChanged(d->state->centerIndex); -} - -void GraphicsPictureFlow::init() -{ - QSize s = size().toSize(); //parentWidget()->rect().size(); - - setSlideSize(QSize((s.width() * slideRatio1) / slideRatio2, (s.height() * slideRatio1) / slideRatio2)); - //resize(s); -//TODO: Disable refrection ? -// setReflectionEffect(PictureFlow::NoReflection); - setBackgroundColor(Qt::black); - // ensure that system cursor is an arrow, not a random icon - // This is not an issue if the platform does not have a system cursor -#ifndef __SYMBIAN32__ - setCursor(Qt::ArrowCursor); -#endif - setFocusPolicy(Qt::WheelFocus); - setFocus(Qt::OtherFocusReason); -} - -QRect GraphicsPictureFlow::centralRect() const -{ - if (d->renderer) { - /* Render the slide to get the rectangle */ - SlideInfo s = d->state->centerSlide; - QRect r = ((PictureFlowSoftwareRenderer*)d->renderer)->renderSlide(s); - return r; - } - else - return QRect(); -} -}