1 /*
2   PictureFlow - animated image show widget
3   http://pictureflow.googlecode.com
4   Copyright (C) 2010 Emmanuel Wagner (manu.wagner@sfr.fr)
5 	Ariya's code modifications for amarok
6   Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
7   Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
8 
9   Permission is hereby granted, free of charge, to any person obtaining a copy
10   of this software and associated documentation files (the "Software"), to deal
11   in the Software without restriction, including without limitation the rights
12   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13   copies of the Software, and to permit persons to whom the Software is
14   furnished to do so, subject to the following conditions:
15 
16   The above copyright notice and this permission notice shall be included in
17   all copies or substantial portions of the Software.
18 
19   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25   THE SOFTWARE.
26 */
27 
28 #include "pictureflow.h"
29 
30 #include "core/meta/Meta.h"
31 
32 #include <QApplication>
33 #include <QCache>
34 #include <QHash>
35 #include <QImage>
36 #include <QKeyEvent>
37 #include <QPainter>
38 #include <QPixmap>
39 #include <QTimer>
40 #include <QVector>
41 #include <QWidget>
42 #include <KStandardDirs>
43 
44 #include <QGraphicsView>
45 #include <QFrame>
46 
47 // for fixed-point arithmetic, we need minimum 32-bit long
48 // long long (64-bit) might be useful for multiplication and division
49 typedef long PFreal;
50 #define PFREAL_SHIFT 10
51 #define PFREAL_ONE (1 << PFREAL_SHIFT)
52 
53 #define IANGLE_MAX 1024
54 #define IANGLE_MASK 1023
55 
fmul(PFreal a,PFreal b)56 inline PFreal fmul( PFreal a, PFreal b )
57 {
58     return (( long long )( a ) )*(( long long )( b ) ) >> PFREAL_SHIFT;
59 }
60 
fdiv(PFreal num,PFreal den)61 inline PFreal fdiv( PFreal num, PFreal den )
62 {
63     long long p = ( long long )( num ) << ( PFREAL_SHIFT * 2 );
64     long long q = p / ( long long )den;
65     long long r = q >> PFREAL_SHIFT;
66 
67     return r;
68 }
69 
fsin(int iangle)70 inline PFreal fsin( int iangle )
71 {
72     // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
73     static const PFreal tab[] =
74     {
75         3,    103,    202,    300,    394,    485,    571,    652,
76         726,    793,    853,    904,    947,    980,   1004,   1019,
77         1023,   1018,   1003,    978,    944,    901,    849,    789,
78         721,    647,    566,    479,    388,    294,    196,     97,
79         -4,   -104,   -203,   -301,   -395,   -486,   -572,   -653,
80         -727,   -794,   -854,   -905,   -948,   -981,  -1005,  -1020,
81         -1024,  -1019,  -1004,   -979,   -945,   -902,   -850,   -790,
82         -722,   -648,   -567,   -480,   -389,   -295,   -197,    -98,
83         3
84     };
85 
86     while ( iangle < 0 )
87         iangle += IANGLE_MAX;
88     iangle &= IANGLE_MASK;
89 
90     int i = ( iangle >> 4 );
91     PFreal p = tab[i];
92     PFreal q = tab[( i+1 )];
93     PFreal g = ( q - p );
94     return p + g * ( iangle - i*16 ) / 16;
95 }
96 
fcos(int iangle)97 inline PFreal fcos( int iangle )
98 {
99     return fsin( iangle + ( IANGLE_MAX >> 2 ) );
100 }
101 
102 /* ----------------------------------------------------------
103 
104 PictureFlowState stores the state of all slides, i.e. all the necessary
105 information to be able to render them.
106 
107 PictureFlowAnimator is responsible to move the slides during the
108 transition between slides, to achieve the effect similar to Cover Flow,
109 by changing the state.
110 
111 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
112 the actual 3-d renderer. It should render all slides given the state
113 (an instance of PictureFlowState).
114 
115 Instances of all the above three classes are stored in
116 PictureFlowPrivate.
117 
118 ------------------------------------------------------- */
119 
120 struct SlideInfo
121 {
122     int slideIndex;
123     int angle;
124     PFreal cx;
125     PFreal cy;
126     int blend;
127 };
128 
129 class PictureFlowState
130 {
131 public:
132     PictureFlowState();
133     ~PictureFlowState();
134 
135     void reposition();
136     void reset();
137 
138     QRgb backgroundColor;
139     int slideWidth;
140     int slideHeight;
141     PictureFlow::ReflectionEffect reflectionEffect;
142     QVector<QImage*> slideImages;
143 
144     int angle;
145     int spacing;
146     PFreal offsetX;
147     PFreal offsetY;
148 
149     SlideInfo centerSlide;
150     QVector<SlideInfo> leftSlides;
151     QVector<SlideInfo> rightSlides;
152     int centerIndex;
153 };
154 
155 class PictureFlowAnimator
156 {
157 public:
158     PictureFlowAnimator();
159     PictureFlowState* state;
160 
161     void start( int slide );
162     void stop( int slide );
163     void update();
164 
165     int target;
166     int step;
167     int frame;
168     QTimer animateTimer;
169     int animationDuration;
170 };
171 
172 class PictureFlowAbstractRenderer
173 {
174 public:
PictureFlowAbstractRenderer()175     PictureFlowAbstractRenderer(): state( 0 ), dirty( false ), widget( 0 ) {}
~PictureFlowAbstractRenderer()176     virtual ~PictureFlowAbstractRenderer() {}
177 
178     PictureFlowState* state;
179     bool dirty;
180     QWidget* widget;
181     QPainter::RenderHints render_hints;
182     virtual void init() = 0;
183     virtual void paint() = 0;
184 };
185 
186 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
187 {
188 public:
189     PictureFlowSoftwareRenderer();
190     ~PictureFlowSoftwareRenderer();
191 
192     virtual void init();
193     virtual void paint();
194 
195 private:
196     QSize size;
197     QRgb bgcolor;
198     int effect;
199     QImage buffer;
200     QVector<PFreal> rays;
201     QImage* blankSurface;
202     QCache<int, QImage> surfaceCache;
203     QHash<int, QImage*> imageHash;
204 
205     void render();
206     void renderSlides();
207     QRect renderSlide( const SlideInfo &slide, int col1 = -1, int col2 = -1 );
208     QImage* surface( int slideIndex );
209 };
210 
211 class PictureFlowOpenGLRenderer: public PictureFlowAbstractRenderer
212 {
213 public:
214     PictureFlowOpenGLRenderer();
215     ~PictureFlowOpenGLRenderer();
216     void init();
217     void paint();
218 private :
219     QSize size;
220     QRgb bgcolor;
221     int effect;
222     QImage* blankSurface;
223 };
224 
PictureFlowOpenGLRenderer()225 PictureFlowOpenGLRenderer::PictureFlowOpenGLRenderer():
226         PictureFlowAbstractRenderer(), size( 0, 0 ), bgcolor( 0 ), effect( -1 ), blankSurface( 0 )
227 {
228 }
~PictureFlowOpenGLRenderer()229 PictureFlowOpenGLRenderer::~PictureFlowOpenGLRenderer()
230 {
231 }
init()232 void PictureFlowOpenGLRenderer::init()
233 {
234 }
paint()235 void PictureFlowOpenGLRenderer::paint()
236 {
237 }
238 // ------------- PictureFlowState ---------------------------------------
239 
PictureFlowState()240 PictureFlowState::PictureFlowState():
241         backgroundColor( 0 ), slideWidth( 150 ), slideHeight( 200 ),
242         reflectionEffect( PictureFlow::BlurredReflection ), centerIndex( 0 )
243 {
244 }
245 
~PictureFlowState()246 PictureFlowState::~PictureFlowState()
247 {
248     for ( int i = 0; i < ( int )slideImages.count(); i++ )
249         delete slideImages[i];
250 }
251 
252 // readjust the settings, call this when slide dimension is changed
reposition()253 void PictureFlowState::reposition()
254 {
255     angle = 70 * IANGLE_MAX / 360;  // approx. 70 degrees tilted
256 
257     offsetX = slideWidth / 2 * ( PFREAL_ONE - fcos( angle ) );
258     offsetY = slideWidth / 2 * fsin( angle );
259     offsetX += slideWidth * PFREAL_ONE;
260     offsetY += slideWidth * PFREAL_ONE / 4;
261     spacing = 40;
262 }
263 
264 // adjust slides so that they are in "steady state" position
reset()265 void PictureFlowState::reset()
266 {
267     centerSlide.angle = 0;
268     centerSlide.cx = 0;
269     centerSlide.cy = 0;
270     centerSlide.slideIndex = centerIndex;
271     centerSlide.blend = 256;
272 
273     leftSlides.resize( 6 );
274     for ( int i = 0; i < ( int )leftSlides.count(); i++ )
275     {
276         SlideInfo& si = leftSlides[i];
277         si.angle = angle;
278         si.cx = -( offsetX + spacing * i * PFREAL_ONE );
279         si.cy = offsetY;
280         si.slideIndex = centerIndex - 1 - i;
281         si.blend = 256;
282         if ( i == ( int )leftSlides.count() - 2 )
283             si.blend = 128;
284         if ( i == ( int )leftSlides.count() - 1 )
285             si.blend = 0;
286     }
287 
288     rightSlides.resize( 6 );
289     for ( int i = 0; i < ( int )rightSlides.count(); i++ )
290     {
291         SlideInfo& si = rightSlides[i];
292         si.angle = -angle;
293         si.cx = offsetX + spacing * i * PFREAL_ONE;
294         si.cy = offsetY;
295         si.slideIndex = centerIndex + 1 + i;
296         si.blend = 256;
297         if ( i == ( int )rightSlides.count() - 2 )
298             si.blend = 128;
299         if ( i == ( int )rightSlides.count() - 1 )
300             si.blend = 0;
301     }
302 }
303 
304 // ------------- PictureFlowAnimator  ---------------------------------------
305 
PictureFlowAnimator()306 PictureFlowAnimator::PictureFlowAnimator():
307         state( 0 ), target( 0 ), step( 0 ), frame( 0 ), animationDuration( 30 )
308 {
309 }
310 
start(int slide)311 void PictureFlowAnimator::start( int slide )
312 {
313     target = slide;
314     if ( !animateTimer.isActive() && state )
315     {
316         step = ( target < state->centerSlide.slideIndex ) ? -1 : 1;
317         animateTimer.start( animationDuration );
318     }
319 }
320 
stop(int slide)321 void PictureFlowAnimator::stop( int slide )
322 {
323     step = 0;
324     target = slide;
325     frame = slide << 16;
326     animateTimer.stop();
327 }
328 
update()329 void PictureFlowAnimator::update()
330 {
331     if ( !animateTimer.isActive() )
332         return;
333     if ( step == 0 )
334         return;
335     if ( !state )
336         return;
337 
338     int speed = 16384 / 4;
339 
340 #if 1
341     // deaccelerate when approaching the target
342     const int max = 2 * 65536;
343 
344     int fi = frame;
345     fi -= ( target << 16 );
346     if ( fi < 0 )
347         fi = -fi;
348     fi = qMin( fi, max );
349 
350     int ia = IANGLE_MAX * ( fi - max / 2 ) / ( max * 2 );
351     speed = 512 + 16384 * ( PFREAL_ONE + fsin( ia ) ) / PFREAL_ONE;
352 #endif
353 
354     frame += speed * step;
355 
356     int index = frame >> 16;
357     int pos = frame & 0xffff;
358     int neg = 65536 - pos;
359     int tick = ( step < 0 ) ? neg : pos;
360     PFreal ftick = ( tick * PFREAL_ONE ) >> 16;
361 
362     if ( step < 0 )
363         index++;
364 
365     if ( state->centerIndex != index )
366     {
367         state->centerIndex = index;
368         frame = index << 16;
369         state->centerSlide.slideIndex = state->centerIndex;
370         for ( int i = 0; i < ( int )state->leftSlides.count(); i++ )
371             state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
372         for ( int i = 0; i < ( int )state->rightSlides.count(); i++ )
373             state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
374     }
375 
376     state->centerSlide.angle = ( step * tick * state->angle ) >> 16;
377     state->centerSlide.cx = -step * fmul( state->offsetX, ftick );
378     state->centerSlide.cy = fmul( state->offsetY, ftick );
379 
380     if ( state->centerIndex == target )
381     {
382         stop( target );
383         state->reset();
384         return;
385     }
386 
387     for ( int i = 0; i < ( int )state->leftSlides.count(); i++ )
388     {
389         SlideInfo& si = state->leftSlides[i];
390         si.angle = state->angle;
391         si.cx = -( state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick );
392         si.cy = state->offsetY;
393     }
394 
395     for ( int i = 0; i < ( int )state->rightSlides.count(); i++ )
396     {
397         SlideInfo& si = state->rightSlides[i];
398         si.angle = -state->angle;
399         si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
400         si.cy = state->offsetY;
401     }
402 
403     if ( step > 0 )
404     {
405         PFreal ftick = ( neg * PFREAL_ONE ) >> 16;
406         state->rightSlides[0].angle = -( neg * state->angle ) >> 16;
407         state->rightSlides[0].cx = fmul( state->offsetX, ftick );
408         state->rightSlides[0].cy = fmul( state->offsetY, ftick );
409     }
410     else
411     {
412         PFreal ftick = ( pos * PFREAL_ONE ) >> 16;
413         state->leftSlides[0].angle = ( pos * state->angle ) >> 16;
414         state->leftSlides[0].cx = -fmul( state->offsetX, ftick );
415         state->leftSlides[0].cy = fmul( state->offsetY, ftick );
416     }
417 
418     // must change direction ?
419     if ( target < index ) if ( step > 0 )
420             step = -1;
421     if ( target > index ) if ( step < 0 )
422             step = 1;
423 
424     // the first and last slide must fade in/fade out
425     int nleft = state->leftSlides.count();
426     int nright = state->rightSlides.count();
427     int fade = pos / 256;
428 
429     for ( int index = 0; index < nleft; index++ )
430     {
431         int blend = 256;
432         if ( index == nleft - 1 )
433             blend = ( step > 0 ) ? 0 : 128 - fade / 2;
434         if ( index == nleft - 2 )
435             blend = ( step > 0 ) ? 128 - fade / 2 : 256 - fade / 2;
436         if ( index == nleft - 3 )
437             blend = ( step > 0 ) ? 256 - fade / 2 : 256;
438         state->leftSlides[index].blend = blend;
439     }
440     for ( int index = 0; index < nright; index++ )
441     {
442         int blend = ( index < nright - 2 ) ? 256 : 128;
443         if ( index == nright - 1 )
444             blend = ( step > 0 ) ? fade / 2 : 0;
445         if ( index == nright - 2 )
446             blend = ( step > 0 ) ? 128 + fade / 2 : fade / 2;
447         if ( index == nright - 3 )
448             blend = ( step > 0 ) ? 256 : 128 + fade / 2;
449         state->rightSlides[index].blend = blend;
450     }
451 
452 }
453 
454 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
455 
PictureFlowSoftwareRenderer()456 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
457         PictureFlowAbstractRenderer(), size( 0, 0 ), bgcolor( 0 ), effect( -1 ), blankSurface( 0 )
458 {
459 }
460 
~PictureFlowSoftwareRenderer()461 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
462 {
463     surfaceCache.clear();
464     buffer = QImage();
465     delete blankSurface;
466 }
467 
paint()468 void PictureFlowSoftwareRenderer::paint()
469 {
470     if ( !widget )
471         return;
472 
473     if ( widget->size() != size )
474         init();
475 
476     if ( state->backgroundColor != bgcolor )
477     {
478         bgcolor = state->backgroundColor;
479         surfaceCache.clear();
480     }
481 
482     if (( int )( state->reflectionEffect ) != effect )
483     {
484         effect = ( int )state->reflectionEffect;
485         surfaceCache.clear();
486     }
487 
488     if ( dirty )
489         render();
490 
491     QPainter painter( widget );
492     painter.setRenderHints( render_hints );
493     painter.drawImage( QPoint( 0, 0 ), buffer );
494 }
495 
init()496 void PictureFlowSoftwareRenderer::init()
497 {
498     if ( !widget )
499         return;
500 
501     surfaceCache.clear();
502     blankSurface = 0;
503 
504     size = widget->size();
505     int ww = size.width();
506     int wh = size.height();
507     int w = ( ww + 1 ) / 2;
508     int h = ( wh + 1 ) / 2;
509 
510     buffer = QImage( ww, wh, QImage::Format_RGB32 );
511     buffer.fill( bgcolor );
512 
513     rays.resize( w*2 );
514     for ( int i = 0; i < w; i++ )
515     {
516         PFreal gg = (( PFREAL_ONE >> 1 ) + i * PFREAL_ONE ) / ( 2 * h );
517         rays[w-i-1] = -gg;
518         rays[w+i] = gg;
519     }
520 
521     dirty = true;
522 }
523 
524 // TODO: optimize this with lookup tables
blendColor(QRgb c1,QRgb c2,int blend)525 static QRgb blendColor( QRgb c1, QRgb c2, int blend )
526 {
527     int r = qRed( c1 ) * blend / 256 + qRed( c2 ) * ( 256 - blend ) / 256;
528     int g = qGreen( c1 ) * blend / 256 + qGreen( c2 ) * ( 256 - blend ) / 256;
529     int b = qBlue( c1 ) * blend / 256 + qBlue( c2 ) * ( 256 - blend ) / 256;
530     return qRgb( r, g, b );
531 }
532 
533 
prepareSurface(const QImage * slideImage,int w,int h,QRgb bgcolor,PictureFlow::ReflectionEffect reflectionEffect)534 static QImage* prepareSurface( const QImage* slideImage, int w, int h, QRgb bgcolor,
535                                PictureFlow::ReflectionEffect reflectionEffect )
536 {
537 
538     Qt::TransformationMode mode = Qt::SmoothTransformation;
539     QImage img = slideImage->scaled( w, h, Qt::IgnoreAspectRatio, mode );
540 
541     // slightly larger, to accommodate for the reflection
542     int hs = h * 2;
543     int hofs = h / 3;
544 
545     // offscreen buffer: black is sweet
546     QImage* result = new QImage( hs, w, QImage::Format_RGB32 );
547     result->fill( bgcolor );
548 
549     // transpose the image, this is to speed-up the rendering
550     // because we process one column at a time
551     // (and much better and faster to work row-wise, i.e in one scanline)
552     for ( int x = 0; x < w; x++ )
553         for ( int y = 0; y < h; y++ )
554             result->setPixel( hofs + y, x, img.pixel( x, y ) );
555 
556     if ( reflectionEffect != PictureFlow::NoReflection )
557     {
558         // create the reflection
559         int ht = hs - h - hofs;
560         int hte = ht;
561         for ( int x = 0; x < w; x++ )
562             for ( int y = 0; y < ht; y++ )
563             {
564                 QRgb color = img.pixel( x, img.height() - y - 1 );
565                 result->setPixel( h + hofs + y, x, blendColor( color, bgcolor, 128*( hte - y ) / hte ) );
566             }
567 
568         if ( reflectionEffect == PictureFlow::BlurredReflection )
569         {
570             // blur the reflection everything first
571             // Based on exponential blur algorithm by Jani Huhtanen
572             QRect rect( hs / 2, 0, hs / 2, w );
573             rect &= result->rect();
574 
575             int r1 = rect.top();
576             int r2 = rect.bottom();
577             int c1 = rect.left();
578             int c2 = rect.right();
579 
580             int bpl = result->bytesPerLine();
581             int rgba[4];
582             unsigned char* p;
583 
584             // how many times blur is applied?
585             // for low-end system, limit this to only 1 loop
586             for ( int loop = 0; loop < 2; loop++ )
587             {
588                 for ( int col = c1; col <= c2; col++ )
589                 {
590                     p = result->scanLine( r1 ) + col * 4;
591                     for ( int i = 0; i < 3; i++ )
592                         rgba[i] = p[i] << 4;
593 
594                     p += bpl;
595                     for ( int j = r1; j < r2; j++, p += bpl )
596                         for ( int i = 0; i < 3; i++ )
597                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
598                 }
599 
600                 for ( int row = r1; row <= r2; row++ )
601                 {
602                     p = result->scanLine( row ) + c1 * 4;
603                     for ( int i = 0; i < 3; i++ )
604                         rgba[i] = p[i] << 4;
605 
606                     p += 4;
607                     for ( int j = c1; j < c2; j++, p += 4 )
608                         for ( int i = 0; i < 3; i++ )
609                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
610                 }
611 
612                 for ( int col = c1; col <= c2; col++ )
613                 {
614                     p = result->scanLine( r2 ) + col * 4;
615                     for ( int i = 0; i < 3; i++ )
616                         rgba[i] = p[i] << 4;
617 
618                     p -= bpl;
619                     for ( int j = r1; j < r2; j++, p -= bpl )
620                         for ( int i = 0; i < 3; i++ )
621                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
622                 }
623 
624                 for ( int row = r1; row <= r2; row++ )
625                 {
626                     p = result->scanLine( row ) + c2 * 4;
627                     for ( int i = 0; i < 3; i++ )
628                         rgba[i] = p[i] << 4;
629 
630                     p -= 4;
631                     for ( int j = c1; j < c2; j++, p -= 4 )
632                         for ( int i = 0; i < 3; i++ )
633                             p[i] = ( rgba[i] += ((( p[i] << 4 ) - rgba[i] ) ) >> 1 ) >> 4;
634                 }
635             }
636 
637             // overdraw to leave only the reflection blurred (but not the actual image)
638             for ( int x = 0; x < w; x++ )
639                 for ( int y = 0; y < h; y++ )
640                     result->setPixel( hofs + y, x, img.pixel( x, y ) );
641         }
642     }
643 
644     return result;
645 }
646 
surface(int slideIndex)647 QImage* PictureFlowSoftwareRenderer::surface( int slideIndex )
648 {
649     if ( !state )
650         return 0;
651     if ( slideIndex < 0 )
652         return 0;
653     if ( slideIndex >= ( int )state->slideImages.count() )
654         return 0;
655 
656     int key = slideIndex;
657 
658     QImage* img = state->slideImages.at( slideIndex );
659     bool empty = img ? img->isNull() : true;
660     if ( empty )
661     {
662         surfaceCache.remove( key );
663         imageHash.remove( slideIndex );
664         if ( !blankSurface )
665         {
666             int sw = state->slideWidth;
667             int sh = state->slideHeight;
668 
669             QImage img = QImage( sw, sh, QImage::Format_RGB32 );
670 
671             QPainter painter( &img );
672             painter.setRenderHints( render_hints );
673             QPoint p1( sw*4 / 10, 0 );
674             QPoint p2( sw*6 / 10, sh );
675             QLinearGradient linearGrad( p1, p2 );
676             linearGrad.setColorAt( 0, Qt::black );
677             linearGrad.setColorAt( 1, Qt::white );
678             painter.setBrush( linearGrad );
679             painter.fillRect( 0, 0, sw, sh, QBrush( linearGrad ) );
680 
681             painter.setPen( QPen( QColor( 64, 64, 64 ), 4 ) );
682             painter.setBrush( QBrush() );
683             painter.drawRect( 2, 2, sw - 3, sh - 3 );
684             painter.end();
685 
686             blankSurface = prepareSurface( &img, sw, sh, bgcolor, state->reflectionEffect );
687         }
688         return blankSurface;
689     }
690     bool exist = imageHash.contains( slideIndex );
691     if ( exist )
692         if ( img == imageHash.find( slideIndex ).value() )
693             if ( surfaceCache.contains( key ) )
694                 return surfaceCache[key];
695 
696     QImage* sr = prepareSurface( img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect );
697     surfaceCache.insert( key, sr );
698     imageHash.insert( slideIndex, img );
699 
700     return sr;
701 }
702 
703 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
704 // col1 and col2 limit the column for rendering.
renderSlide(const SlideInfo & slide,int col1,int col2)705 QRect PictureFlowSoftwareRenderer::renderSlide( const SlideInfo &slide, int col1, int col2 )
706 {
707     int blend = slide.blend;
708     if ( !blend )
709         return QRect();
710 
711     QImage* src = surface( slide.slideIndex );
712     if ( !src )
713         return QRect();
714 
715     QRect rect( 0, 0, 0, 0 );
716 
717     int sw = src->height();
718     int sh = src->width();
719     int h = buffer.height();
720     int w = buffer.width();
721 
722     if ( col1 > col2 )
723     {
724         int c = col2;
725         col2 = col1;
726         col1 = c;
727     }
728 
729     col1 = ( col1 >= 0 ) ? col1 : 0;
730     col2 = ( col2 >= 0 ) ? col2 : w - 1;
731     col1 = qMin( col1, w - 1 );
732     col2 = qMin( col2, w - 1 );
733 
734     int zoom = 100;
735     int distance = h * 100 / zoom;
736     PFreal sdx = fcos( slide.angle );
737     PFreal sdy = fsin( slide.angle );
738     PFreal xs = slide.cx - state->slideWidth * sdx / 2;
739     PFreal ys = slide.cy - state->slideWidth * sdy / 2;
740     PFreal dist = distance * PFREAL_ONE;
741 
742     int xi = qMax(( PFreal )0, (( w * PFREAL_ONE / 2 ) + fdiv( xs * h, dist + ys ) ) >> PFREAL_SHIFT );
743     if ( xi >= w )
744         return rect;
745 
746     bool flag = false;
747     rect.setLeft( xi );
748     for ( int x = qMax( xi, col1 ); x <= col2; x++ )
749     {
750         PFreal hity = 0;
751         PFreal fk = rays[x];
752         if ( sdy )
753         {
754             fk = fk - fdiv( sdx, sdy );
755             hity = -fdiv(( rays[x] * distance - slide.cx + slide.cy * sdx / sdy ), fk );
756         }
757 
758         dist = distance * PFREAL_ONE + hity;
759         if ( dist < 0 )
760             continue;
761 
762         PFreal hitx = fmul( dist, rays[x] );
763         PFreal hitdist = fdiv( hitx - slide.cx, sdx );
764 
765         int column = sw / 2 + ( hitdist >> PFREAL_SHIFT );
766         if ( column >= sw )
767             break;
768         if ( column < 0 )
769             continue;
770 
771         rect.setRight( x );
772         if ( !flag )
773             rect.setLeft( x );
774         flag = true;
775 
776         int y1 = h / 2;
777         int y2 = y1 + 1;
778         QRgb* pixel1 = ( QRgb* )( buffer.scanLine( y1 ) ) + x;
779         QRgb* pixel2 = ( QRgb* )( buffer.scanLine( y2 ) ) + x;
780         QRgb pixelstep = pixel2 - pixel1;
781 
782         int center = ( sh / 2 );
783         int dy = dist / h;
784         int p1 = center * PFREAL_ONE - dy / 2;
785         int p2 = center * PFREAL_ONE + dy / 2;
786 
787         const QRgb *ptr = ( const QRgb* )( src->scanLine( column ) );
788         if ( blend == 256 )
789             while (( y1 >= 0 ) && ( y2 < h ) && ( p1 >= 0 ) )
790             {
791                 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
792                 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
793                 p1 -= dy;
794                 p2 += dy;
795                 y1--;
796                 y2++;
797                 pixel1 -= pixelstep;
798                 pixel2 += pixelstep;
799             }
800         else
801             while (( y1 >= 0 ) && ( y2 < h ) && ( p1 >= 0 ) )
802             {
803                 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
804                 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
805                 *pixel1 = blendColor( c1, bgcolor, blend );
806                 *pixel2 = blendColor( c2, bgcolor, blend );
807                 p1 -= dy;
808                 p2 += dy;
809                 y1--;
810                 y2++;
811                 pixel1 -= pixelstep;
812                 pixel2 += pixelstep;
813             }
814     }
815 
816     rect.setTop( 0 );
817     rect.setBottom( h - 1 );
818     return rect;
819 }
820 
renderSlides()821 void PictureFlowSoftwareRenderer::renderSlides()
822 {
823     int nleft = state->leftSlides.count();
824     int nright = state->rightSlides.count();
825 
826     QRect r = renderSlide( state->centerSlide );
827     int c1 = r.left();
828     int c2 = r.right();
829 
830     for ( int index = 0; index < nleft; index++ )
831     {
832         QRect rs = renderSlide( state->leftSlides[index], 0, c1 - 1 );
833         if ( !rs.isEmpty() )
834             c1 = rs.left();
835     }
836     for ( int index = 0; index < nright; index++ )
837     {
838         QRect rs = renderSlide( state->rightSlides[index], c2 + 1, buffer.width() );
839         if ( !rs.isEmpty() )
840             c2 = rs.right();
841     }
842 }
843 
844 // Render the slides. Updates only the offscreen buffer.
render()845 void PictureFlowSoftwareRenderer::render()
846 {
847     buffer.fill( state->backgroundColor );
848     renderSlides();
849     dirty = false;
850 }
851 
852 // -----------------------------------------
853 
854 class PictureFlowPrivate
855 {
856 public:
857     PictureFlowState* state;
858     PictureFlowAnimator* animator;
859     PictureFlowAbstractRenderer* renderer;
860     QTimer triggerTimer;
861 };
862 
863 
PictureFlow(QWidget * parent,bool enableOpenGL)864 PictureFlow::PictureFlow( QWidget* parent, bool enableOpenGL): QWidget( parent )
865 {
866     d = new PictureFlowPrivate;
867     m_opengl = enableOpenGL;
868 	//m_openglwidget = 0;
869     d->state = new PictureFlowState;
870     d->state->reset();
871     d->state->reposition();
872 
873 	if (!m_opengl)
874 	{
875 		d->renderer = new PictureFlowSoftwareRenderer;
876 		d->renderer->widget = this;
877 	}
878 	else
879 	{
880 		d->renderer = new PictureFlowOpenGLRenderer;
881 		//m_openglwidget = new CoverBling(0,m_album_list);
882 		//m_openglwidget->setParent(this);
883 	}
884 	d->renderer->state = d->state;
885     d->renderer->init();
886 
887     d->animator = new PictureFlowAnimator;
888     d->animator->state = d->state;
889     QObject::connect( &d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()) );
890 
891     QObject::connect( &d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()) );
892     setAttribute( Qt::WA_StaticContents, true );
893     setAttribute( Qt::WA_OpaquePaintEvent, true );
894     setAttribute( Qt::WA_NoSystemBackground, true );
895 }
896 
~PictureFlow()897 PictureFlow::~PictureFlow()
898 {
899     delete d->renderer;
900     delete d->animator;
901     delete d->state;
902     delete d;
903 }
setOpenGLMode(bool activateOpenGL)904 void PictureFlow::setOpenGLMode(bool activateOpenGL)
905 {
906 	m_opengl = activateOpenGL;
907 }
slideCount() const908 int PictureFlow::slideCount() const
909 {
910     return d->state->slideImages.count();
911 }
912 
backgroundColor() const913 QColor PictureFlow::backgroundColor() const
914 {
915     return QColor( d->state->backgroundColor );
916 }
917 
setBackgroundColor(const QColor & c)918 void PictureFlow::setBackgroundColor( const QColor& c )
919 {
920     d->state->backgroundColor = c.rgb();
921     triggerRender();
922 }
923 
slideSize() const924 QSize PictureFlow::slideSize() const
925 {
926     return QSize( d->state->slideWidth, d->state->slideHeight );
927 }
928 
setSlideSize(QSize size)929 void PictureFlow::setSlideSize( QSize size )
930 {
931     d->state->slideWidth = size.width();
932     d->state->slideHeight = size.height();
933     d->state->reposition();
934     triggerRender();
935 }
936 
reflectionEffect() const937 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
938 {
939     return d->state->reflectionEffect;
940 }
941 
setReflectionEffect(ReflectionEffect effect)942 void PictureFlow::setReflectionEffect( ReflectionEffect effect )
943 {
944     d->state->reflectionEffect = effect;
945     triggerRender();
946 }
setRenderHints(QPainter::RenderHints iHints)947 void PictureFlow::setRenderHints( QPainter::RenderHints iHints )
948 {
949     d->renderer->render_hints = iHints;
950     triggerRender();
951 }
setAnimationTime(int iTime)952 void PictureFlow::setAnimationTime( int iTime )
953 {
954     d->animator->animationDuration = iTime;
955 }
slide(int index) const956 QImage PictureFlow::slide( int index ) const
957 {
958     QImage* i = 0;
959     if (( index >= 0 ) && ( index < slideCount() ) )
960         i = d->state->slideImages[index];
961     return i ? QImage( *i ) : QImage();
962 }
963 
addSlide(const QImage & image)964 void PictureFlow::addSlide( const QImage& image )
965 {
966     int c = d->state->slideImages.count();
967     d->state->slideImages.resize( c + 1 );
968     d->state->slideImages[c] = new QImage( image );
969     triggerRender();
970 }
971 
addSlide(const QPixmap & pixmap)972 void PictureFlow::addSlide( const QPixmap& pixmap )
973 {
974     addSlide( pixmap.toImage() );
975 }
976 
setSlide(int index,const QImage & image)977 void PictureFlow::setSlide( int index, const QImage& image )
978 {
979     if (( index >= 0 ) && ( index < slideCount() ) )
980     {
981         QImage* i = image.isNull() ? 0 : new QImage( image );
982         delete d->state->slideImages[index];
983         d->state->slideImages[index] = i;
984         triggerRender();
985     }
986 }
987 
setSlide(int index,const QPixmap & pixmap)988 void PictureFlow::setSlide( int index, const QPixmap& pixmap )
989 {
990     setSlide( index, pixmap.toImage() );
991 }
addAlbum(Meta::AlbumPtr iAlbum)992 void PictureFlow::addAlbum( Meta::AlbumPtr iAlbum )
993 {
994     m_album_list.append( iAlbum );
995     //QPixmap* no_cover_pix = new QPixmap( KStandardDirs::locate( "data", "amarok/images/blingdefaultcover.png" ) );
996 }
setAlbums(Meta::AlbumList iAlbums)997 void PictureFlow::setAlbums(Meta::AlbumList iAlbums)
998 {
999 	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 }
album(int index)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 }
centerIndex() const1019 int PictureFlow::centerIndex() const
1020 {
1021     return d->state->centerIndex;
1022 }
1023 
setCenterIndex(int index)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 
clear()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 
render()1045 void PictureFlow::render()
1046 {
1047     d->renderer->dirty = true;
1048     update();
1049 }
1050 
triggerRender()1051 void PictureFlow::triggerRender()
1052 {
1053     d->triggerTimer.setSingleShot( true );
1054     d->triggerTimer.start( 0 );
1055 }
1056 
showPrevious()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 
showNext()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 
showSlide(int index)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 
keyPressEvent(QKeyEvent * event)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 
mousePressEvent(QMouseEvent * event)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 }
mouseDoubleClickEvent(QMouseEvent * event)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 }
wheelEvent(QWheelEvent * event)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 }
paintEvent(QPaintEvent * event)1152 void PictureFlow::paintEvent( QPaintEvent* event )
1153 {
1154     Q_UNUSED( event );
1155     d->renderer->paint();
1156 }
1157 
resizeEvent(QResizeEvent * event)1158 void PictureFlow::resizeEvent( QResizeEvent* event )
1159 {
1160     triggerRender();
1161     QWidget::resizeEvent( event );
1162 }
1163 
updateAnimation()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 
1173