File indexing completed on 2025-03-09 04:23:49

0001 /*
0002   PictureFlow - animated image show widget
0003   http://pictureflow.googlecode.com
0004   Copyright (C) 2010 Emmanuel Wagner (manu.wagner@sfr.fr)
0005     Ariya's code modifications for amarok
0006   Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
0007   Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
0008 
0009   Permission is hereby granted, free of charge, to any person obtaining a copy
0010   of this software and associated documentation files (the "Software"), to deal
0011   in the Software without restriction, including without limitation the rights
0012   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0013   copies of the Software, and to permit persons to whom the Software is
0014   furnished to do so, subject to the following conditions:
0015 
0016   The above copyright notice and this permission notice shall be included in
0017   all copies or substantial portions of the Software.
0018 
0019   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0020   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0021   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0022   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0023   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0024   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0025   THE SOFTWARE.
0026 */
0027 
0028 #include "pictureflow.h"
0029 
0030 #include "core/meta/Meta.h"
0031 
0032 #include <QApplication>
0033 #include <QCache>
0034 #include <QHash>
0035 #include <QImage>
0036 #include <QKeyEvent>
0037 #include <QPainter>
0038 #include <QPixmap>
0039 #include <QTimer>
0040 #include <QVector>
0041 #include <QWidget>
0042 #include <KStandardDirs>
0043 
0044 #include <QGraphicsView>
0045 #include <QFrame>
0046 
0047 // for fixed-point arithmetic, we need minimum 32-bit long
0048 // long long (64-bit) might be useful for multiplication and division
0049 typedef long PFreal;
0050 #define PFREAL_SHIFT 10
0051 #define PFREAL_ONE (1 << PFREAL_SHIFT)
0052 
0053 #define IANGLE_MAX 1024
0054 #define IANGLE_MASK 1023
0055 
0056 inline PFreal fmul( PFreal a, PFreal b )
0057 {
0058     return (( long long )( a ) )*(( long long )( b ) ) >> PFREAL_SHIFT;
0059 }
0060 
0061 inline PFreal fdiv( PFreal num, PFreal den )
0062 {
0063     long long p = ( long long )( num ) << ( PFREAL_SHIFT * 2 );
0064     long long q = p / ( long long )den;
0065     long long r = q >> PFREAL_SHIFT;
0066 
0067     return r;
0068 }
0069 
0070 inline PFreal fsin( int iangle )
0071 {
0072     // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
0073     static const PFreal tab[] =
0074     {
0075         3,    103,    202,    300,    394,    485,    571,    652,
0076         726,    793,    853,    904,    947,    980,   1004,   1019,
0077         1023,   1018,   1003,    978,    944,    901,    849,    789,
0078         721,    647,    566,    479,    388,    294,    196,     97,
0079         -4,   -104,   -203,   -301,   -395,   -486,   -572,   -653,
0080         -727,   -794,   -854,   -905,   -948,   -981,  -1005,  -1020,
0081         -1024,  -1019,  -1004,   -979,   -945,   -902,   -850,   -790,
0082         -722,   -648,   -567,   -480,   -389,   -295,   -197,    -98,
0083         3
0084     };
0085 
0086     while ( iangle < 0 )
0087         iangle += IANGLE_MAX;
0088     iangle &= IANGLE_MASK;
0089 
0090     int i = ( iangle >> 4 );
0091     PFreal p = tab[i];
0092     PFreal q = tab[( i+1 )];
0093     PFreal g = ( q - p );
0094     return p + g * ( iangle - i*16 ) / 16;
0095 }
0096 
0097 inline PFreal fcos( int iangle )
0098 {
0099     return fsin( iangle + ( IANGLE_MAX >> 2 ) );
0100 }
0101 
0102 /* ----------------------------------------------------------
0103 
0104 PictureFlowState stores the state of all slides, i.e. all the necessary
0105 information to be able to render them.
0106 
0107 PictureFlowAnimator is responsible to move the slides during the
0108 transition between slides, to achieve the effect similar to Cover Flow,
0109 by changing the state.
0110 
0111 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
0112 the actual 3-d renderer. It should render all slides given the state
0113 (an instance of PictureFlowState).
0114 
0115 Instances of all the above three classes are stored in
0116 PictureFlowPrivate.
0117 
0118 ------------------------------------------------------- */
0119 
0120 struct SlideInfo
0121 {
0122     int slideIndex;
0123     int angle;
0124     PFreal cx;
0125     PFreal cy;
0126     int blend;
0127 };
0128 
0129 class PictureFlowState
0130 {
0131 public:
0132     PictureFlowState();
0133     ~PictureFlowState();
0134 
0135     void reposition();
0136     void reset();
0137 
0138     QRgb backgroundColor;
0139     int slideWidth;
0140     int slideHeight;
0141     PictureFlow::ReflectionEffect reflectionEffect;
0142     QVector<QImage*> slideImages;
0143 
0144     int angle;
0145     int spacing;
0146     PFreal offsetX;
0147     PFreal offsetY;
0148 
0149     SlideInfo centerSlide;
0150     QVector<SlideInfo> leftSlides;
0151     QVector<SlideInfo> rightSlides;
0152     int centerIndex;
0153 };
0154 
0155 class PictureFlowAnimator
0156 {
0157 public:
0158     PictureFlowAnimator();
0159     PictureFlowState* state;
0160 
0161     void start( int slide );
0162     void stop( int slide );
0163     void update();
0164 
0165     int target;
0166     int step;
0167     int frame;
0168     QTimer animateTimer;
0169     int animationDuration;
0170 };
0171 
0172 class PictureFlowAbstractRenderer
0173 {
0174 public:
0175     PictureFlowAbstractRenderer(): state( 0 ), dirty( false ), widget( 0 ) {}
0176     virtual ~PictureFlowAbstractRenderer() {}
0177 
0178     PictureFlowState* state;
0179     bool dirty;
0180     QWidget* widget;
0181     QPainter::RenderHints render_hints;
0182     virtual void init() = 0;
0183     virtual void paint() = 0;
0184 };
0185 
0186 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
0187 {
0188 public:
0189     PictureFlowSoftwareRenderer();
0190     ~PictureFlowSoftwareRenderer();
0191 
0192     virtual void init();
0193     virtual void paint();
0194 
0195 private:
0196     QSize size;
0197     QRgb bgcolor;
0198     int effect;
0199     QImage buffer;
0200     QVector<PFreal> rays;
0201     QImage* blankSurface;
0202     QCache<int, QImage> surfaceCache;
0203     QHash<int, QImage*> imageHash;
0204 
0205     void render();
0206     void renderSlides();
0207     QRect renderSlide( const SlideInfo &slide, int col1 = -1, int col2 = -1 );
0208     QImage* surface( int slideIndex );
0209 };
0210 
0211 class PictureFlowOpenGLRenderer: public PictureFlowAbstractRenderer
0212 {
0213 public:
0214     PictureFlowOpenGLRenderer();
0215     ~PictureFlowOpenGLRenderer();
0216     void init();
0217     void paint();
0218 private :
0219     QSize size;
0220     QRgb bgcolor;
0221     int effect;
0222     QImage* blankSurface;
0223 };
0224 
0225 PictureFlowOpenGLRenderer::PictureFlowOpenGLRenderer():
0226         PictureFlowAbstractRenderer(), size( 0, 0 ), bgcolor( 0 ), effect( -1 ), blankSurface( 0 )
0227 {
0228 }
0229 PictureFlowOpenGLRenderer::~PictureFlowOpenGLRenderer()
0230 {
0231 }
0232 void PictureFlowOpenGLRenderer::init()
0233 {
0234 }
0235 void PictureFlowOpenGLRenderer::paint()
0236 {
0237 }
0238 // ------------- PictureFlowState ---------------------------------------
0239 
0240 PictureFlowState::PictureFlowState():
0241         backgroundColor( 0 ), slideWidth( 150 ), slideHeight( 200 ),
0242         reflectionEffect( PictureFlow::BlurredReflection ), centerIndex( 0 )
0243 {
0244 }
0245 
0246 PictureFlowState::~PictureFlowState()
0247 {
0248     for ( int i = 0; i < ( int )slideImages.count(); i++ )
0249         delete slideImages[i];
0250 }
0251 
0252 // readjust the settings, call this when slide dimension is changed
0253 void PictureFlowState::reposition()
0254 {
0255     angle = 70 * IANGLE_MAX / 360;  // approx. 70 degrees tilted
0256 
0257     offsetX = slideWidth / 2 * ( PFREAL_ONE - fcos( angle ) );
0258     offsetY = slideWidth / 2 * fsin( angle );
0259     offsetX += slideWidth * PFREAL_ONE;
0260     offsetY += slideWidth * PFREAL_ONE / 4;
0261     spacing = 40;
0262 }
0263 
0264 // adjust slides so that they are in "steady state" position
0265 void PictureFlowState::reset()
0266 {
0267     centerSlide.angle = 0;
0268     centerSlide.cx = 0;
0269     centerSlide.cy = 0;
0270     centerSlide.slideIndex = centerIndex;
0271     centerSlide.blend = 256;
0272 
0273     leftSlides.resize( 6 );
0274     for ( int i = 0; i < ( int )leftSlides.count(); i++ )
0275     {
0276         SlideInfo& si = leftSlides[i];
0277         si.angle = angle;
0278         si.cx = -( offsetX + spacing * i * PFREAL_ONE );
0279         si.cy = offsetY;
0280         si.slideIndex = centerIndex - 1 - i;
0281         si.blend = 256;
0282         if ( i == ( int )leftSlides.count() - 2 )
0283             si.blend = 128;
0284         if ( i == ( int )leftSlides.count() - 1 )
0285             si.blend = 0;
0286     }
0287 
0288     rightSlides.resize( 6 );
0289     for ( int i = 0; i < ( int )rightSlides.count(); i++ )
0290     {
0291         SlideInfo& si = rightSlides[i];
0292         si.angle = -angle;
0293         si.cx = offsetX + spacing * i * PFREAL_ONE;
0294         si.cy = offsetY;
0295         si.slideIndex = centerIndex + 1 + i;
0296         si.blend = 256;
0297         if ( i == ( int )rightSlides.count() - 2 )
0298             si.blend = 128;
0299         if ( i == ( int )rightSlides.count() - 1 )
0300             si.blend = 0;
0301     }
0302 }
0303 
0304 // ------------- PictureFlowAnimator  ---------------------------------------
0305 
0306 PictureFlowAnimator::PictureFlowAnimator():
0307         state( 0 ), target( 0 ), step( 0 ), frame( 0 ), animationDuration( 30 )
0308 {
0309 }
0310 
0311 void PictureFlowAnimator::start( int slide )
0312 {
0313     target = slide;
0314     if ( !animateTimer.isActive() && state )
0315     {
0316         step = ( target < state->centerSlide.slideIndex ) ? -1 : 1;
0317         animateTimer.start( animationDuration );
0318     }
0319 }
0320 
0321 void PictureFlowAnimator::stop( int slide )
0322 {
0323     step = 0;
0324     target = slide;
0325     frame = slide << 16;
0326     animateTimer.stop();
0327 }
0328 
0329 void PictureFlowAnimator::update()
0330 {
0331     if ( !animateTimer.isActive() )
0332         return;
0333     if ( step == 0 )
0334         return;
0335     if ( !state )
0336         return;
0337 
0338     int speed = 16384 / 4;
0339 
0340 #if 1
0341     // deaccelerate when approaching the target
0342     const int max = 2 * 65536;
0343 
0344     int fi = frame;
0345     fi -= ( target << 16 );
0346     if ( fi < 0 )
0347         fi = -fi;
0348     fi = qMin( fi, max );
0349 
0350     int ia = IANGLE_MAX * ( fi - max / 2 ) / ( max * 2 );
0351     speed = 512 + 16384 * ( PFREAL_ONE + fsin( ia ) ) / PFREAL_ONE;
0352 #endif
0353 
0354     frame += speed * step;
0355 
0356     int index = frame >> 16;
0357     int pos = frame & 0xffff;
0358     int neg = 65536 - pos;
0359     int tick = ( step < 0 ) ? neg : pos;
0360     PFreal ftick = ( tick * PFREAL_ONE ) >> 16;
0361 
0362     if ( step < 0 )
0363         index++;
0364 
0365     if ( state->centerIndex != index )
0366     {
0367         state->centerIndex = index;
0368         frame = index << 16;
0369         state->centerSlide.slideIndex = state->centerIndex;
0370         for ( int i = 0; i < ( int )state->leftSlides.count(); i++ )
0371             state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
0372         for ( int i = 0; i < ( int )state->rightSlides.count(); i++ )
0373             state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
0374     }
0375 
0376     state->centerSlide.angle = ( step * tick * state->angle ) >> 16;
0377     state->centerSlide.cx = -step * fmul( state->offsetX, ftick );
0378     state->centerSlide.cy = fmul( state->offsetY, ftick );
0379 
0380     if ( state->centerIndex == target )
0381     {
0382         stop( target );
0383         state->reset();
0384         return;
0385     }
0386 
0387     for ( int i = 0; i < ( int )state->leftSlides.count(); i++ )
0388     {
0389         SlideInfo& si = state->leftSlides[i];
0390         si.angle = state->angle;
0391         si.cx = -( state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick );
0392         si.cy = state->offsetY;
0393     }
0394 
0395     for ( int i = 0; i < ( int )state->rightSlides.count(); i++ )
0396     {
0397         SlideInfo& si = state->rightSlides[i];
0398         si.angle = -state->angle;
0399         si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
0400         si.cy = state->offsetY;
0401     }
0402 
0403     if ( step > 0 )
0404     {
0405         PFreal ftick = ( neg * PFREAL_ONE ) >> 16;
0406         state->rightSlides[0].angle = -( neg * state->angle ) >> 16;
0407         state->rightSlides[0].cx = fmul( state->offsetX, ftick );
0408         state->rightSlides[0].cy = fmul( state->offsetY, ftick );
0409     }
0410     else
0411     {
0412         PFreal ftick = ( pos * PFREAL_ONE ) >> 16;
0413         state->leftSlides[0].angle = ( pos * state->angle ) >> 16;
0414         state->leftSlides[0].cx = -fmul( state->offsetX, ftick );
0415         state->leftSlides[0].cy = fmul( state->offsetY, ftick );
0416     }
0417 
0418     // must change direction ?
0419     if ( target < index ) if ( step > 0 )
0420             step = -1;
0421     if ( target > index ) if ( step < 0 )
0422             step = 1;
0423 
0424     // the first and last slide must fade in/fade out
0425     int nleft = state->leftSlides.count();
0426     int nright = state->rightSlides.count();
0427     int fade = pos / 256;
0428 
0429     for ( int index = 0; index < nleft; index++ )
0430     {
0431         int blend = 256;
0432         if ( index == nleft - 1 )
0433             blend = ( step > 0 ) ? 0 : 128 - fade / 2;
0434         if ( index == nleft - 2 )
0435             blend = ( step > 0 ) ? 128 - fade / 2 : 256 - fade / 2;
0436         if ( index == nleft - 3 )
0437             blend = ( step > 0 ) ? 256 - fade / 2 : 256;
0438         state->leftSlides[index].blend = blend;
0439     }
0440     for ( int index = 0; index < nright; index++ )
0441     {
0442         int blend = ( index < nright - 2 ) ? 256 : 128;
0443         if ( index == nright - 1 )
0444             blend = ( step > 0 ) ? fade / 2 : 0;
0445         if ( index == nright - 2 )
0446             blend = ( step > 0 ) ? 128 + fade / 2 : fade / 2;
0447         if ( index == nright - 3 )
0448             blend = ( step > 0 ) ? 256 : 128 + fade / 2;
0449         state->rightSlides[index].blend = blend;
0450     }
0451 
0452 }
0453 
0454 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
0455 
0456 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
0457         PictureFlowAbstractRenderer(), size( 0, 0 ), bgcolor( 0 ), effect( -1 ), blankSurface( 0 )
0458 {
0459 }
0460 
0461 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
0462 {
0463     surfaceCache.clear();
0464     buffer = QImage();
0465     delete blankSurface;
0466 }
0467 
0468 void PictureFlowSoftwareRenderer::paint()
0469 {
0470     if ( !widget )
0471         return;
0472 
0473     if ( widget->size() != size )
0474         init();
0475 
0476     if ( state->backgroundColor != bgcolor )
0477     {
0478         bgcolor = state->backgroundColor;
0479         surfaceCache.clear();
0480     }
0481 
0482     if (( int )( state->reflectionEffect ) != effect )
0483     {
0484         effect = ( int )state->reflectionEffect;
0485         surfaceCache.clear();
0486     }
0487 
0488     if ( dirty )
0489         render();
0490 
0491     QPainter painter( widget );
0492     painter.setRenderHints( render_hints );
0493     painter.drawImage( QPoint( 0, 0 ), buffer );
0494 }
0495 
0496 void PictureFlowSoftwareRenderer::init()
0497 {
0498     if ( !widget )
0499         return;
0500 
0501     surfaceCache.clear();
0502     blankSurface = 0;
0503 
0504     size = widget->size();
0505     int ww = size.width();
0506     int wh = size.height();
0507     int w = ( ww + 1 ) / 2;
0508     int h = ( wh + 1 ) / 2;
0509 
0510     buffer = QImage( ww, wh, QImage::Format_RGB32 );
0511     buffer.fill( bgcolor );
0512 
0513     rays.resize( w*2 );
0514     for ( int i = 0; i < w; i++ )
0515     {
0516         PFreal gg = (( PFREAL_ONE >> 1 ) + i * PFREAL_ONE ) / ( 2 * h );
0517         rays[w-i-1] = -gg;
0518         rays[w+i] = gg;
0519     }
0520 
0521     dirty = true;
0522 }
0523 
0524 // TODO: optimize this with lookup tables
0525 static QRgb blendColor( QRgb c1, QRgb c2, int blend )
0526 {
0527     int r = qRed( c1 ) * blend / 256 + qRed( c2 ) * ( 256 - blend ) / 256;
0528     int g = qGreen( c1 ) * blend / 256 + qGreen( c2 ) * ( 256 - blend ) / 256;
0529     int b = qBlue( c1 ) * blend / 256 + qBlue( c2 ) * ( 256 - blend ) / 256;
0530     return qRgb( r, g, b );
0531 }
0532 
0533 
0534 static QImage* prepareSurface( const QImage* slideImage, int w, int h, QRgb bgcolor,
0535                                PictureFlow::ReflectionEffect reflectionEffect )
0536 {
0537 
0538     Qt::TransformationMode mode = Qt::SmoothTransformation;
0539     QImage img = slideImage->scaled( w, h, Qt::IgnoreAspectRatio, mode );
0540 
0541     // slightly larger, to accommodate for the reflection
0542     int hs = h * 2;
0543     int hofs = h / 3;
0544 
0545     // offscreen buffer: black is sweet
0546     QImage* result = new QImage( hs, w, QImage::Format_RGB32 );
0547     result->fill( bgcolor );
0548 
0549     // transpose the image, this is to speed-up the rendering
0550     // because we process one column at a time
0551     // (and much better and faster to work row-wise, i.e in one scanline)
0552     for ( int x = 0; x < w; x++ )
0553         for ( int y = 0; y < h; y++ )
0554             result->setPixel( hofs + y, x, img.pixel( x, y ) );
0555 
0556     if ( reflectionEffect != PictureFlow::NoReflection )
0557     {
0558         // create the reflection
0559         int ht = hs - h - hofs;
0560         int hte = ht;
0561         for ( int x = 0; x < w; x++ )
0562             for ( int y = 0; y < ht; y++ )
0563             {
0564                 QRgb color = img.pixel( x, img.height() - y - 1 );
0565                 result->setPixel( h + hofs + y, x, blendColor( color, bgcolor, 128*( hte - y ) / hte ) );
0566             }
0567 
0568         if ( reflectionEffect == PictureFlow::BlurredReflection )
0569         {
0570             // blur the reflection everything first
0571             // Based on exponential blur algorithm by Jani Huhtanen
0572             QRect rect( hs / 2, 0, hs / 2, w );
0573             rect &= result->rect();
0574 
0575             int r1 = rect.top();
0576             int r2 = rect.bottom();
0577             int c1 = rect.left();
0578             int c2 = rect.right();
0579 
0580             int bpl = result->bytesPerLine();
0581             int rgba[4];
0582             unsigned char* p;
0583 
0584             // how many times blur is applied?
0585             // for low-end system, limit this to only 1 loop
0586             for ( int loop = 0; loop < 2; loop++ )
0587             {
0588                 for ( int col = c1; col <= c2; col++ )
0589                 {
0590                     p = result->scanLine( r1 ) + col * 4;
0591                     for ( int i = 0; i < 3; i++ )
0592                         rgba[i] = p[i] << 4;
0593 
0594                     p += bpl;
0595                     for ( int j = r1; j < r2; j++, p += bpl )
0596                         for ( int i = 0; i < 3; i++ )
0597                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
0598                 }
0599 
0600                 for ( int row = r1; row <= r2; row++ )
0601                 {
0602                     p = result->scanLine( row ) + c1 * 4;
0603                     for ( int i = 0; i < 3; i++ )
0604                         rgba[i] = p[i] << 4;
0605 
0606                     p += 4;
0607                     for ( int j = c1; j < c2; j++, p += 4 )
0608                         for ( int i = 0; i < 3; i++ )
0609                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
0610                 }
0611 
0612                 for ( int col = c1; col <= c2; col++ )
0613                 {
0614                     p = result->scanLine( r2 ) + col * 4;
0615                     for ( int i = 0; i < 3; i++ )
0616                         rgba[i] = p[i] << 4;
0617 
0618                     p -= bpl;
0619                     for ( int j = r1; j < r2; j++, p -= bpl )
0620                         for ( int i = 0; i < 3; i++ )
0621                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
0622                 }
0623 
0624                 for ( int row = r1; row <= r2; row++ )
0625                 {
0626                     p = result->scanLine( row ) + c2 * 4;
0627                     for ( int i = 0; i < 3; i++ )
0628                         rgba[i] = p[i] << 4;
0629 
0630                     p -= 4;
0631                     for ( int j = c1; j < c2; j++, p -= 4 )
0632                         for ( int i = 0; i < 3; i++ )
0633                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
0634                 }
0635             }
0636 
0637             // overdraw to leave only the reflection blurred (but not the actual image)
0638             for ( int x = 0; x < w; x++ )
0639                 for ( int y = 0; y < h; y++ )
0640                     result->setPixel( hofs + y, x, img.pixel( x, y ) );
0641         }
0642     }
0643 
0644     return result;
0645 }
0646 
0647 QImage* PictureFlowSoftwareRenderer::surface( int slideIndex )
0648 {
0649     if ( !state )
0650         return 0;
0651     if ( slideIndex < 0 )
0652         return 0;
0653     if ( slideIndex >= ( int )state->slideImages.count() )
0654         return 0;
0655 
0656     int key = slideIndex;
0657 
0658     QImage* img = state->slideImages.at( slideIndex );
0659     bool empty = img ? img->isNull() : true;
0660     if ( empty )
0661     {
0662         surfaceCache.remove( key );
0663         imageHash.remove( slideIndex );
0664         if ( !blankSurface )
0665         {
0666             int sw = state->slideWidth;
0667             int sh = state->slideHeight;
0668 
0669             QImage img = QImage( sw, sh, QImage::Format_RGB32 );
0670 
0671             QPainter painter( &img );
0672             painter.setRenderHints( render_hints );
0673             QPoint p1( sw*4 / 10, 0 );
0674             QPoint p2( sw*6 / 10, sh );
0675             QLinearGradient linearGrad( p1, p2 );
0676             linearGrad.setColorAt( 0, Qt::black );
0677             linearGrad.setColorAt( 1, Qt::white );
0678             painter.setBrush( linearGrad );
0679             painter.fillRect( 0, 0, sw, sh, QBrush( linearGrad ) );
0680 
0681             painter.setPen( QPen( QColor( 64, 64, 64 ), 4 ) );
0682             painter.setBrush( QBrush() );
0683             painter.drawRect( 2, 2, sw - 3, sh - 3 );
0684             painter.end();
0685 
0686             blankSurface = prepareSurface( &img, sw, sh, bgcolor, state->reflectionEffect );
0687         }
0688         return blankSurface;
0689     }
0690     bool exist = imageHash.contains( slideIndex );
0691     if ( exist )
0692         if ( img == imageHash.find( slideIndex ).value() )
0693             if ( surfaceCache.contains( key ) )
0694                 return surfaceCache[key];
0695 
0696     QImage* sr = prepareSurface( img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect );
0697     surfaceCache.insert( key, sr );
0698     imageHash.insert( slideIndex, img );
0699 
0700     return sr;
0701 }
0702 
0703 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
0704 // col1 and col2 limit the column for rendering.
0705 QRect PictureFlowSoftwareRenderer::renderSlide( const SlideInfo &slide, int col1, int col2 )
0706 {
0707     int blend = slide.blend;
0708     if ( !blend )
0709         return QRect();
0710 
0711     QImage* src = surface( slide.slideIndex );
0712     if ( !src )
0713         return QRect();
0714 
0715     QRect rect( 0, 0, 0, 0 );
0716 
0717     int sw = src->height();
0718     int sh = src->width();
0719     int h = buffer.height();
0720     int w = buffer.width();
0721 
0722     if ( col1 > col2 )
0723     {
0724         int c = col2;
0725         col2 = col1;
0726         col1 = c;
0727     }
0728 
0729     col1 = ( col1 >= 0 ) ? col1 : 0;
0730     col2 = ( col2 >= 0 ) ? col2 : w - 1;
0731     col1 = qMin( col1, w - 1 );
0732     col2 = qMin( col2, w - 1 );
0733 
0734     int zoom = 100;
0735     int distance = h * 100 / zoom;
0736     PFreal sdx = fcos( slide.angle );
0737     PFreal sdy = fsin( slide.angle );
0738     PFreal xs = slide.cx - state->slideWidth * sdx / 2;
0739     PFreal ys = slide.cy - state->slideWidth * sdy / 2;
0740     PFreal dist = distance * PFREAL_ONE;
0741 
0742     int xi = qMax(( PFreal )0, (( w * PFREAL_ONE / 2 ) + fdiv( xs * h, dist + ys ) ) >> PFREAL_SHIFT );
0743     if ( xi >= w )
0744         return rect;
0745 
0746     bool flag = false;
0747     rect.setLeft( xi );
0748     for ( int x = qMax( xi, col1 ); x <= col2; x++ )
0749     {
0750         PFreal hity = 0;
0751         PFreal fk = rays[x];
0752         if ( sdy )
0753         {
0754             fk = fk - fdiv( sdx, sdy );
0755             hity = -fdiv(( rays[x] * distance - slide.cx + slide.cy * sdx / sdy ), fk );
0756         }
0757 
0758         dist = distance * PFREAL_ONE + hity;
0759         if ( dist < 0 )
0760             continue;
0761 
0762         PFreal hitx = fmul( dist, rays[x] );
0763         PFreal hitdist = fdiv( hitx - slide.cx, sdx );
0764 
0765         int column = sw / 2 + ( hitdist >> PFREAL_SHIFT );
0766         if ( column >= sw )
0767             break;
0768         if ( column < 0 )
0769             continue;
0770 
0771         rect.setRight( x );
0772         if ( !flag )
0773             rect.setLeft( x );
0774         flag = true;
0775 
0776         int y1 = h / 2;
0777         int y2 = y1 + 1;
0778         QRgb* pixel1 = ( QRgb* )( buffer.scanLine( y1 ) ) + x;
0779         QRgb* pixel2 = ( QRgb* )( buffer.scanLine( y2 ) ) + x;
0780         QRgb pixelstep = pixel2 - pixel1;
0781 
0782         int center = ( sh / 2 );
0783         int dy = dist / h;
0784         int p1 = center * PFREAL_ONE - dy / 2;
0785         int p2 = center * PFREAL_ONE + dy / 2;
0786 
0787         const QRgb *ptr = ( const QRgb* )( src->scanLine( column ) );
0788         if ( blend == 256 )
0789             while (( y1 >= 0 ) && ( y2 < h ) && ( p1 >= 0 ) )
0790             {
0791                 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
0792                 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
0793                 p1 -= dy;
0794                 p2 += dy;
0795                 y1--;
0796                 y2++;
0797                 pixel1 -= pixelstep;
0798                 pixel2 += pixelstep;
0799             }
0800         else
0801             while (( y1 >= 0 ) && ( y2 < h ) && ( p1 >= 0 ) )
0802             {
0803                 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
0804                 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
0805                 *pixel1 = blendColor( c1, bgcolor, blend );
0806                 *pixel2 = blendColor( c2, bgcolor, blend );
0807                 p1 -= dy;
0808                 p2 += dy;
0809                 y1--;
0810                 y2++;
0811                 pixel1 -= pixelstep;
0812                 pixel2 += pixelstep;
0813             }
0814     }
0815 
0816     rect.setTop( 0 );
0817     rect.setBottom( h - 1 );
0818     return rect;
0819 }
0820 
0821 void PictureFlowSoftwareRenderer::renderSlides()
0822 {
0823     int nleft = state->leftSlides.count();
0824     int nright = state->rightSlides.count();
0825 
0826     QRect r = renderSlide( state->centerSlide );
0827     int c1 = r.left();
0828     int c2 = r.right();
0829 
0830     for ( int index = 0; index < nleft; index++ )
0831     {
0832         QRect rs = renderSlide( state->leftSlides[index], 0, c1 - 1 );
0833         if ( !rs.isEmpty() )
0834             c1 = rs.left();
0835     }
0836     for ( int index = 0; index < nright; index++ )
0837     {
0838         QRect rs = renderSlide( state->rightSlides[index], c2 + 1, buffer.width() );
0839         if ( !rs.isEmpty() )
0840             c2 = rs.right();
0841     }
0842 }
0843 
0844 // Render the slides. Updates only the offscreen buffer.
0845 void PictureFlowSoftwareRenderer::render()
0846 {
0847     buffer.fill( state->backgroundColor );
0848     renderSlides();
0849     dirty = false;
0850 }
0851 
0852 // -----------------------------------------
0853 
0854 class PictureFlowPrivate
0855 {
0856 public:
0857     PictureFlowState* state;
0858     PictureFlowAnimator* animator;
0859     PictureFlowAbstractRenderer* renderer;
0860     QTimer triggerTimer;
0861 };
0862 
0863 
0864 PictureFlow::PictureFlow( QWidget* parent, bool enableOpenGL): QWidget( parent )
0865 {
0866     d = new PictureFlowPrivate;
0867     m_opengl = enableOpenGL;
0868     //m_openglwidget = 0;
0869     d->state = new PictureFlowState;
0870     d->state->reset();
0871     d->state->reposition();
0872 
0873     if (!m_opengl)
0874     {
0875         d->renderer = new PictureFlowSoftwareRenderer;
0876         d->renderer->widget = this;
0877     }
0878     else
0879     {
0880         d->renderer = new PictureFlowOpenGLRenderer;
0881         //m_openglwidget = new CoverBling(0,m_album_list);
0882         //m_openglwidget->setParent(this);
0883     }
0884     d->renderer->state = d->state;
0885     d->renderer->init();
0886 
0887     d->animator = new PictureFlowAnimator;
0888     d->animator->state = d->state;
0889     QObject::connect( &d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()) );
0890 
0891     QObject::connect( &d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()) );
0892     setAttribute( Qt::WA_StaticContents, true );
0893     setAttribute( Qt::WA_OpaquePaintEvent, true );
0894     setAttribute( Qt::WA_NoSystemBackground, true );
0895 }
0896 
0897 PictureFlow::~PictureFlow()
0898 {
0899     delete d->renderer;
0900     delete d->animator;
0901     delete d->state;
0902     delete d;
0903 }
0904 void PictureFlow::setOpenGLMode(bool activateOpenGL)
0905 {
0906     m_opengl = activateOpenGL;
0907 }
0908 int PictureFlow::slideCount() const
0909 {
0910     return d->state->slideImages.count();
0911 }
0912 
0913 QColor PictureFlow::backgroundColor() const
0914 {
0915     return QColor( d->state->backgroundColor );
0916 }
0917 
0918 void PictureFlow::setBackgroundColor( const QColor& c )
0919 {
0920     d->state->backgroundColor = c.rgb();
0921     triggerRender();
0922 }
0923 
0924 QSize PictureFlow::slideSize() const
0925 {
0926     return QSize( d->state->slideWidth, d->state->slideHeight );
0927 }
0928 
0929 void PictureFlow::setSlideSize( QSize size )
0930 {
0931     d->state->slideWidth = size.width();
0932     d->state->slideHeight = size.height();
0933     d->state->reposition();
0934     triggerRender();
0935 }
0936 
0937 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
0938 {
0939     return d->state->reflectionEffect;
0940 }
0941 
0942 void PictureFlow::setReflectionEffect( ReflectionEffect effect )
0943 {
0944     d->state->reflectionEffect = effect;
0945     triggerRender();
0946 }
0947 void PictureFlow::setRenderHints( QPainter::RenderHints iHints )
0948 {
0949     d->renderer->render_hints = iHints;
0950     triggerRender();
0951 }
0952 void PictureFlow::setAnimationTime( int iTime )
0953 {
0954     d->animator->animationDuration = iTime;
0955 }
0956 QImage PictureFlow::slide( int index ) const
0957 {
0958     QImage* i = 0;
0959     if (( index >= 0 ) && ( index < slideCount() ) )
0960         i = d->state->slideImages[index];
0961     return i ? QImage( *i ) : QImage();
0962 }
0963 
0964 void PictureFlow::addSlide( const QImage& image )
0965 {
0966     int c = d->state->slideImages.count();
0967     d->state->slideImages.resize( c + 1 );
0968     d->state->slideImages[c] = new QImage( image );
0969     triggerRender();
0970 }
0971 
0972 void PictureFlow::addSlide( const QPixmap& pixmap )
0973 {
0974     addSlide( pixmap.toImage() );
0975 }
0976 
0977 void PictureFlow::setSlide( int index, const QImage& image )
0978 {
0979     if (( index >= 0 ) && ( index < slideCount() ) )
0980     {
0981         QImage* i = image.isNull() ? 0 : new QImage( image );
0982         delete d->state->slideImages[index];
0983         d->state->slideImages[index] = i;
0984         triggerRender();
0985     }
0986 }
0987 
0988 void PictureFlow::setSlide( int index, const QPixmap& pixmap )
0989 {
0990     setSlide( index, pixmap.toImage() );
0991 }
0992 void PictureFlow::addAlbum( Meta::AlbumPtr iAlbum )
0993 {
0994     m_album_list.append( iAlbum );
0995     //QPixmap* no_cover_pix = new QPixmap( KStandardDirs::locate( "data", "amarok/images/blingdefaultcover.png" ) );
0996 }
0997 void PictureFlow::setAlbums(Meta::AlbumList iAlbums)
0998 {
0999     m_album_list = iAlbums;
1000     //m_openglwidget->resize(-1,-1);
1001     //d->renderer->widget = m_openglwidget;
1002     //if (m_openglwidget) 
1003     //{
1004         //m_openglwidget->init(m_album_list,QSize(150,150));
1005         //m_openglwidget->show();
1006         
1007     //}
1008     
1009 }
1010 Meta::AlbumPtr PictureFlow::album( int index )
1011 {
1012     Meta::AlbumPtr album;
1013     if (index < m_album_list.size() &&  index >= 0)
1014     {
1015         album = m_album_list[index];
1016     }
1017     return album;
1018 }
1019 int PictureFlow::centerIndex() const
1020 {
1021     return d->state->centerIndex;
1022 }
1023 
1024 void PictureFlow::setCenterIndex( int index )
1025 {
1026     index = qMin( index, slideCount() - 1 );
1027     index = qMax( index, 0 );
1028     d->state->centerIndex = index;
1029     d->state->reset();
1030     d->animator->stop( index );
1031     triggerRender();
1032 }
1033 
1034 void PictureFlow::clear()
1035 {
1036     int c = d->state->slideImages.count();
1037     for ( int i = 0; i < c; i++ )
1038         delete d->state->slideImages[i];
1039     d->state->slideImages.resize( 0 );
1040 
1041     d->state->reset();
1042     triggerRender();
1043 }
1044 
1045 void PictureFlow::render()
1046 {
1047     d->renderer->dirty = true;
1048     update();
1049 }
1050 
1051 void PictureFlow::triggerRender()
1052 {
1053     d->triggerTimer.setSingleShot( true );
1054     d->triggerTimer.start( 0 );
1055 }
1056 
1057 void PictureFlow::showPrevious()
1058 {
1059     int step = d->animator->step;
1060     int center = d->state->centerIndex;
1061 
1062     if ( step > 0 )
1063         d->animator->start( center );
1064 
1065     if ( step == 0 )
1066         if ( center > 0 )
1067             d->animator->start( center - 1 );
1068 
1069     if ( step < 0 )
1070         d->animator->target = qMax( 0, center - 2 );
1071 }
1072 
1073 void PictureFlow::showNext()
1074 {
1075     int step = d->animator->step;
1076     int center = d->state->centerIndex;
1077 
1078     if ( step < 0 )
1079         d->animator->start( center );
1080 
1081     if ( step == 0 )
1082         if ( center < slideCount() - 1 )
1083             d->animator->start( center + 1 );
1084 
1085     if ( step > 0 )
1086         d->animator->target = qMin( center + 2, slideCount() - 1 );
1087 }
1088 
1089 void PictureFlow::showSlide( int index )
1090 {
1091     index = qMax( index, 0 );
1092     index = qMin( slideCount() - 1, index );
1093     if ( index == d->state->centerSlide.slideIndex )
1094         return;
1095 
1096     d->animator->start( index );
1097 }
1098 
1099 void PictureFlow::keyPressEvent( QKeyEvent* event )
1100 {
1101     if ( event->key() == Qt::Key_Left )
1102     {
1103         if ( event->modifiers() == Qt::ControlModifier )
1104             showSlide( centerIndex() - 10 );
1105         else
1106             showPrevious();
1107         event->accept();
1108         return;
1109     }
1110 
1111     if ( event->key() == Qt::Key_Right )
1112     {
1113         if ( event->modifiers() == Qt::ControlModifier )
1114             showSlide( centerIndex() + 10 );
1115         else
1116             showNext();
1117         event->accept();
1118         return;
1119     }
1120 
1121     event->ignore();
1122 }
1123 
1124 void PictureFlow::mousePressEvent( QMouseEvent* event )
1125 {
1126     if ( event->x() > ( width() / 2 + ( d->state->slideWidth ) / 2 ) )
1127         showNext();
1128     if ( event->x() < ( width() / 2 - ( d->state->slideWidth ) / 2 ) )
1129         showPrevious();
1130 }
1131 void PictureFlow::mouseDoubleClickEvent( QMouseEvent* event )
1132 {
1133     if ((( event->x() <= ( width() / 2 + ( d->state->slideWidth ) / 2 ) ) && ( event->x() >= ( width() / 2 - ( d->state->slideWidth ) / 2 ) ) ) && (( event->y() <= ( height() / 2 + ( d->state->slideHeight ) / 2 ) ) && ( event->y() >= ( height() / 2 - ( d->state->slideHeight ) / 2 ) ) ) )
1134         emit doubleClicked( d->state->centerIndex );
1135 }
1136 void PictureFlow::wheelEvent( QWheelEvent * event )
1137 {
1138     int numDegrees = event->delta() / 8;
1139     int numSteps = numDegrees / 15;
1140     bool forward = true;
1141     if ( numSteps < 0 )
1142     {
1143         forward = false;
1144         numSteps = -numSteps;
1145     }
1146     for ( int i = 1;i <= numSteps;i++ )
1147     {
1148         if ( forward ) showNext();
1149         else showPrevious();
1150     }
1151 }
1152 void PictureFlow::paintEvent( QPaintEvent* event )
1153 {
1154     Q_UNUSED( event );
1155     d->renderer->paint();
1156 }
1157 
1158 void PictureFlow::resizeEvent( QResizeEvent* event )
1159 {
1160     triggerRender();
1161     QWidget::resizeEvent( event );
1162 }
1163 
1164 void PictureFlow::updateAnimation()
1165 {
1166     int old_center = d->state->centerIndex;
1167     d->animator->update();
1168     triggerRender();
1169     if ( d->state->centerIndex != old_center )
1170         emit centerIndexChanged( d->state->centerIndex );
1171 }
1172