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