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