qt4: dynamic_cast -> qobject_cast
[vlc.git] / modules / gui / qt4 / util / pictureflow.cpp
blob6b518632ee29390436fe5449930ee1203fd9202f
1 /*
2 PictureFlow - animated image show widget
3 http://pictureflow.googlecode.com
5 Copyright (C) 2009 Ariya Hidayat (ariya@kde.org)
6 Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
7 Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
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:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
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.
28 #include "pictureflow.hpp"
29 #include "components/playlist/ml_model.hpp"
31 #include <QApplication>
32 #include <QImage>
33 #include <QKeyEvent>
34 #include <QPainter>
35 #include <QPixmap>
36 #include <QTimer>
37 #include <QVector>
38 #include <QWidget>
39 #include <QHash>
40 #include "../components/playlist/playlist_model.hpp" /* getArtPixmap etc */
41 #include "../components/playlist/sorting.h" /* Columns List */
42 #include "input_manager.hpp"
44 // for fixed-point arithmetic, we need minimum 32-bit long
45 // long long (64-bit) might be useful for multiplication and division
46 typedef long PFreal;
47 #define PFREAL_SHIFT 10
48 #define PFREAL_ONE (1 << PFREAL_SHIFT)
50 #define IANGLE_MAX 1024
51 #define IANGLE_MASK 1023
53 inline PFreal fmul(PFreal a, PFreal b)
55 return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
58 inline PFreal fdiv(PFreal num, PFreal den)
60 long long p = (long long)(num) << (PFREAL_SHIFT * 2);
61 long long q = p / (long long)den;
62 long long r = q >> PFREAL_SHIFT;
64 return r;
67 inline PFreal fsin(int iangle)
69 // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
70 static const PFreal tab[] = {
71 3, 103, 202, 300, 394, 485, 571, 652,
72 726, 793, 853, 904, 947, 980, 1004, 1019,
73 1023, 1018, 1003, 978, 944, 901, 849, 789,
74 721, 647, 566, 479, 388, 294, 196, 97,
75 -4, -104, -203, -301, -395, -486, -572, -653,
76 -727, -794, -854, -905, -948, -981, -1005, -1020,
77 -1024, -1019, -1004, -979, -945, -902, -850, -790,
78 -722, -648, -567, -480, -389, -295, -197, -98,
82 while (iangle < 0)
83 iangle += IANGLE_MAX;
84 iangle &= IANGLE_MASK;
86 int i = (iangle >> 4);
87 PFreal p = tab[i];
88 PFreal q = tab[(i+1)];
89 PFreal g = (q - p);
90 return p + g *(iangle - i*16) / 16;
93 inline PFreal fcos(int iangle)
95 return fsin(iangle + (IANGLE_MAX >> 2));
98 /* ----------------------------------------------------------
100 PictureFlowState stores the state of all slides, i.e. all the necessary
101 information to be able to render them.
103 PictureFlowAnimator is responsible to move the slides during the
104 transition between slides, to achieve the effect similar to Cover Flow,
105 by changing the state.
107 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
108 the actual 3-d renderer. It should render all slides given the state
109 (an instance of PictureFlowState).
111 Instances of all the above three classes are stored in
112 PictureFlowPrivate.
114 ------------------------------------------------------- */
116 struct SlideInfo {
117 int slideIndex;
118 int angle;
119 PFreal cx;
120 PFreal cy;
121 int blend;
124 class PictureFlowState
126 public:
127 PictureFlowState();
128 ~PictureFlowState();
130 void reposition();
131 void reset();
133 QRgb backgroundColor;
134 int slideWidth;
135 int slideHeight;
136 PictureFlow::ReflectionEffect reflectionEffect;
138 int angle;
139 int spacing;
140 PFreal offsetX;
141 PFreal offsetY;
143 VLCModel *model;
144 SlideInfo centerSlide;
145 QVector<SlideInfo> leftSlides;
146 QVector<SlideInfo> rightSlides;
147 int centerIndex;
150 class PictureFlowAnimator
152 public:
153 PictureFlowAnimator();
154 PictureFlowState* state;
156 void start(int slide);
157 void stop(int slide);
158 void update();
160 int target;
161 int step;
162 int frame;
163 QTimer animateTimer;
166 class PictureFlowAbstractRenderer
168 public:
169 PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {}
170 virtual ~PictureFlowAbstractRenderer() {}
172 PictureFlowState* state;
173 bool dirty;
174 QWidget* widget;
176 virtual void init() = 0;
177 virtual void paint() = 0;
180 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
182 public:
183 PictureFlowSoftwareRenderer();
184 ~PictureFlowSoftwareRenderer();
186 virtual void init();
187 virtual void paint();
189 private:
190 QSize size;
191 QRgb bgcolor;
192 int effect;
193 QImage buffer;
194 QVector<PFreal> rays;
195 QImage* blankSurface;
197 void render();
198 void renderSlides();
199 QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
200 QImage* surface(QModelIndex);
201 QHash<QString, QImage*> cache;
204 // ------------- PictureFlowState ---------------------------------------
206 PictureFlowState::PictureFlowState():
207 backgroundColor(0), slideWidth(150), slideHeight(200),
208 reflectionEffect(PictureFlow::BlurredReflection), centerIndex(0)
212 PictureFlowState::~PictureFlowState()
216 // readjust the settings, call this when slide dimension is changed
217 void PictureFlowState::reposition()
219 angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted
221 offsetX = slideWidth / 2 * (PFREAL_ONE - fcos(angle));
222 offsetY = slideWidth / 2 * fsin(angle);
223 offsetX += slideWidth * PFREAL_ONE;
224 offsetY += slideWidth * PFREAL_ONE / 4;
225 spacing = 40;
228 // adjust slides so that they are in "steady state" position
229 void PictureFlowState::reset()
231 centerSlide.angle = 0;
232 centerSlide.cx = 0;
233 centerSlide.cy = 0;
234 centerSlide.slideIndex = centerIndex;
235 centerSlide.blend = 256;
237 leftSlides.resize(6);
238 for (int i = 0; i < (int)leftSlides.count(); i++) {
239 SlideInfo& si = leftSlides[i];
240 si.angle = angle;
241 si.cx = -(offsetX + spacing * i * PFREAL_ONE);
242 si.cy = offsetY;
243 si.slideIndex = centerIndex - 1 - i;
244 si.blend = 256;
245 if (i == (int)leftSlides.count() - 2)
246 si.blend = 128;
247 if (i == (int)leftSlides.count() - 1)
248 si.blend = 0;
251 rightSlides.resize(6);
252 for (int i = 0; i < (int)rightSlides.count(); i++) {
253 SlideInfo& si = rightSlides[i];
254 si.angle = -angle;
255 si.cx = offsetX + spacing * i * PFREAL_ONE;
256 si.cy = offsetY;
257 si.slideIndex = centerIndex + 1 + i;
258 si.blend = 256;
259 if (i == (int)rightSlides.count() - 2)
260 si.blend = 128;
261 if (i == (int)rightSlides.count() - 1)
262 si.blend = 0;
266 // ------------- PictureFlowAnimator ---------------------------------------
268 PictureFlowAnimator::PictureFlowAnimator():
269 state(0), target(0), step(0), frame(0)
273 void PictureFlowAnimator::start(int slide)
275 target = slide;
276 if (!animateTimer.isActive() && state) {
277 step = (target < state->centerSlide.slideIndex) ? -1 : 1;
278 animateTimer.start(30);
282 void PictureFlowAnimator::stop(int slide)
284 step = 0;
285 target = slide;
286 frame = slide << 16;
287 animateTimer.stop();
290 void PictureFlowAnimator::update()
292 if (!animateTimer.isActive())
293 return;
294 if (step == 0)
295 return;
296 if (!state)
297 return;
299 int speed = 16384;
301 #if 0
302 // deaccelerate when approaching the target
303 const int max = 2 * 65536;
305 int fi = frame;
306 fi -= (target << 16);
307 if (fi < 0)
308 fi = -fi;
309 fi = qMin(fi, max);
311 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
312 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
313 #endif
315 frame += speed * step;
317 int index = frame >> 16;
318 int pos = frame & 0xffff;
319 int neg = 65536 - pos;
320 int tick = (step < 0) ? neg : pos;
321 PFreal ftick = (tick * PFREAL_ONE) >> 16;
323 if (step < 0)
324 index++;
326 if (state->centerIndex != index) {
327 state->centerIndex = index;
328 frame = index << 16;
329 state->centerSlide.slideIndex = state->centerIndex;
330 for (int i = 0; i < (int)state->leftSlides.count(); i++)
331 state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
332 for (int i = 0; i < (int)state->rightSlides.count(); i++)
333 state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
336 state->centerSlide.angle = (step * tick * state->angle) >> 16;
337 state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
338 state->centerSlide.cy = fmul(state->offsetY, ftick);
340 if (state->centerIndex == target) {
341 stop(target);
342 state->reset();
343 return;
346 for (int i = 0; i < (int)state->leftSlides.count(); i++) {
347 SlideInfo& si = state->leftSlides[i];
348 si.angle = state->angle;
349 si.cx = -(state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick);
350 si.cy = state->offsetY;
353 for (int i = 0; i < (int)state->rightSlides.count(); i++) {
354 SlideInfo& si = state->rightSlides[i];
355 si.angle = -state->angle;
356 si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
357 si.cy = state->offsetY;
360 if (step > 0) {
361 PFreal ftick = (neg * PFREAL_ONE) >> 16;
362 state->rightSlides[0].angle = -(neg * state->angle) >> 16;
363 state->rightSlides[0].cx = fmul(state->offsetX, ftick);
364 state->rightSlides[0].cy = fmul(state->offsetY, ftick);
365 } else {
366 PFreal ftick = (pos * PFREAL_ONE) >> 16;
367 state->leftSlides[0].angle = (pos * state->angle) >> 16;
368 state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
369 state->leftSlides[0].cy = fmul(state->offsetY, ftick);
372 // must change direction ?
373 if (target < index) if (step > 0)
374 step = -1;
375 if (target > index) if (step < 0)
376 step = 1;
378 // the first and last slide must fade in/fade out
379 int nleft = state->leftSlides.count();
380 int nright = state->rightSlides.count();
381 int fade = pos / 256;
383 for (int index = 0; index < nleft; index++) {
384 int blend = 256;
385 if (index == nleft - 1)
386 blend = (step > 0) ? 0 : 128 - fade / 2;
387 if (index == nleft - 2)
388 blend = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
389 if (index == nleft - 3)
390 blend = (step > 0) ? 256 - fade / 2 : 256;
391 state->leftSlides[index].blend = blend;
393 for (int index = 0; index < nright; index++) {
394 int blend = (index < nright - 2) ? 256 : 128;
395 if (index == nright - 1)
396 blend = (step > 0) ? fade / 2 : 0;
397 if (index == nright - 2)
398 blend = (step > 0) ? 128 + fade / 2 : fade / 2;
399 if (index == nright - 3)
400 blend = (step > 0) ? 256 : 128 + fade / 2;
401 state->rightSlides[index].blend = blend;
406 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
408 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
409 PictureFlowAbstractRenderer(), size(0, 0), bgcolor(0), effect(-1), blankSurface(0)
413 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
415 buffer = QImage();
416 cache.clear();
417 delete blankSurface;
420 void PictureFlowSoftwareRenderer::paint()
422 if (!widget)
423 return;
425 if (widget->size() != size)
426 init();
428 if (state->backgroundColor != bgcolor) {
429 bgcolor = state->backgroundColor;
432 if ((int)(state->reflectionEffect) != effect) {
433 effect = (int)state->reflectionEffect;
436 if (dirty)
437 render();
439 QPainter painter(widget);
440 painter.drawImage(QPoint(0, 0), buffer);
442 QModelIndex index = state->model->index( state->centerIndex, 0, state->model->currentIndex().parent() );
446 void PictureFlowSoftwareRenderer::init()
448 if (!widget)
449 return;
451 blankSurface = 0;
453 size = widget->size();
454 int ww = size.width();
455 int wh = size.height();
456 int w = (ww + 1) / 2;
457 int h = (wh + 1) / 2;
459 buffer = QImage(ww, wh, QImage::Format_RGB32);
460 buffer.fill(bgcolor);
462 rays.resize(w*2);
463 for (int i = 0; i < w; i++) {
464 PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2 * h);
465 rays[w-i-1] = -gg;
466 rays[w+i] = gg;
469 dirty = true;
472 // TODO: optimize this with lookup tables
473 static QRgb blendColor(QRgb c1, QRgb c2, int blend)
475 int r = qRed(c1) * blend / 256 + qRed(c2) * (256 - blend) / 256;
476 int g = qGreen(c1) * blend / 256 + qGreen(c2) * (256 - blend) / 256;
477 int b = qBlue(c1) * blend / 256 + qBlue(c2) * (256 - blend) / 256;
478 return qRgb(r, g, b);
482 static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
483 PictureFlow::ReflectionEffect reflectionEffect, QModelIndex index)
485 Qt::TransformationMode mode = Qt::SmoothTransformation;
486 QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
488 // slightly larger, to accomodate for the reflection
489 int hs = h * 2;
490 int hofs = h / 3;
492 // offscreen buffer: black is sweet
493 QImage* result = new QImage(hs, w, QImage::Format_RGB32);
494 QFont font( index.data( Qt::FontRole ).value<QFont>() );
495 QPainter imagePainter( result );
496 QTransform rotation;
497 imagePainter.setFont( font );
498 rotation.rotate(90);
499 rotation.scale(1,-1);
500 rotation.translate( 0, hofs );
501 result->fill(bgcolor);
503 // transpose the image, this is to speed-up the rendering
504 // because we process one column at a time
505 // (and much better and faster to work row-wise, i.e in one scanline)
507 for (int x = 0; x < w; x++)
508 for (int y = 0; y < h; y++)
509 result->setPixel(hofs + y, x, img.pixel(x, y));
511 imagePainter.drawImage( hofs+h, 0, img );
512 if (reflectionEffect != PictureFlow::NoReflection) {
513 // create the reflection
514 int ht = hs - h - hofs;
515 int hte = ht;
516 for (int x = 0; x < w; x++)
518 QRgb *line = (QRgb*)(result->scanLine( x ));
519 for (int y = 0; y < ht; y++) {
520 QRgb color = img.pixel(x, img.height() - y - 1);
521 line[h+hofs+y] = blendColor( color, bgcolor, 128*(hte-y)/hte );
522 //result->setPixel(h + hofs + y, x, blendColor(color, bgcolor, 128*(hte - y) / hte));
526 if (reflectionEffect == PictureFlow::BlurredReflection) {
527 // blur the reflection everything first
528 // Based on exponential blur algorithm by Jani Huhtanen
529 QRect rect(hs / 2, 0, hs / 2, w);
530 rect &= result->rect();
532 int r1 = rect.top();
533 int r2 = rect.bottom();
534 int c1 = rect.left();
535 int c2 = rect.right();
537 int bpl = result->bytesPerLine();
538 int rgba[4];
539 unsigned char* p;
541 // how many times blur is applied?
542 // for low-end system, limit this to only 1 loop
543 for (int loop = 0; loop < 2; loop++) {
544 for (int col = c1; col <= c2; col++) {
545 p = result->scanLine(r1) + col * 4;
546 for (int i = 0; i < 3; i++)
547 rgba[i] = p[i] << 4;
549 p += bpl;
550 for (int j = r1; j < r2; j++, p += bpl)
551 for (int i = 0; i < 3; i++)
552 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
555 for (int row = r1; row <= r2; row++) {
556 p = result->scanLine(row) + c1 * 4;
557 for (int i = 0; i < 3; i++)
558 rgba[i] = p[i] << 4;
560 p += 4;
561 for (int j = c1; j < c2; j++, p += 4)
562 for (int i = 0; i < 3; i++)
563 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
566 for (int col = c1; col <= c2; col++) {
567 p = result->scanLine(r2) + col * 4;
568 for (int i = 0; i < 3; i++)
569 rgba[i] = p[i] << 4;
571 p -= bpl;
572 for (int j = r1; j < r2; j++, p -= bpl)
573 for (int i = 0; i < 3; i++)
574 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
577 for (int row = r1; row <= r2; row++) {
578 p = result->scanLine(row) + c2 * 4;
579 for (int i = 0; i < 3; i++)
580 rgba[i] = p[i] << 4;
582 p -= 4;
583 for (int j = c1; j < c2; j++, p -= 4)
584 for (int i = 0; i < 3; i++)
585 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
589 // overdraw to leave only the reflection blurred (but not the actual image)
590 imagePainter.setTransform( rotation );
591 imagePainter.drawImage( 0, 0, img );
592 imagePainter.setBrush( QBrush( Qt::lightGray ) );
593 imagePainter.setPen( QColor( Qt::lightGray ) );
594 QFontMetrics fm = imagePainter.fontMetrics();
595 imagePainter.drawText( 0, img.height()+ 13, VLCModel::getMeta( index, COLUMN_TITLE ) );
596 imagePainter.drawText( 0, img.height()+ 13 + fm.xHeight()*2, VLCModel::getMeta( index, COLUMN_ARTIST ) );
598 for (int x = 0; x < w; x++)
599 for (int y = 0; y < h; y++)
600 result->setPixel(hofs + y, x, img.pixel(x, y));
605 return result;
608 QImage* PictureFlowSoftwareRenderer::surface(QModelIndex index)
610 if (!state || !index.isValid())
611 return 0;
613 QImage* img = new QImage(VLCModel::getArtPixmap( index,
614 QSize( state->slideWidth, state->slideHeight ) ).toImage());
616 QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect, index );
618 delete img;
619 return sr;
622 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
623 // col1 and col2 limit the column for rendering.
624 QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
626 int blend = slide.blend;
627 if (!blend)
628 return QRect();
630 QModelIndex index;
632 QString artURL;
634 PLModel* plm = qobject_cast<PLModel*>( state->model );
635 #ifdef MEDIA_LIBRARY
636 MLModel* mlm = qobject_cast<MLModel*>( state->model );
637 #endif
638 if( plm != 0 )
640 index = ((PLModel*)state->model)->index( slide.slideIndex, 0, state->model->currentIndex().parent() );
641 if( !index.isValid() )
642 return QRect();
644 PLItem *item = static_cast<PLItem*>( index.internalPointer() );
645 artURL = InputManager::decodeArtURL( item->inputItem() );
647 #ifdef MEDIA_LIBRARY
648 else if( mlm != 0 )
650 index = ((MLModel*)state->model)->index( slide.slideIndex, 0, QModelIndex() );
651 if( !index.isValid() )
652 return QRect();
654 MLItem *item = static_cast<MLItem*>( index.internalPointer() );
655 artURL = qfu( item->getMedia()->psz_cover );
657 #endif
658 QString key = QString("%1%2%3%4").arg(VLCModel::getMeta( index, COLUMN_TITLE )).arg( VLCModel::getMeta( index, COLUMN_ARTIST ) ).arg(index.data( VLCModel::IsCurrentRole ).toBool() ).arg( artURL );
660 QImage* src;
661 if( cache.contains( key ) )
662 src = cache.value( key );
663 else
665 src = surface( index );
666 cache.insert( key, src );
668 if (!src)
669 return QRect();
671 QRect rect(0, 0, 0, 0);
673 int sw = src->height();
674 int sh = src->width();
675 int h = buffer.height();
676 int w = buffer.width();
678 if (col1 > col2) {
679 int c = col2;
680 col2 = col1;
681 col1 = c;
684 col1 = (col1 >= 0) ? col1 : 0;
685 col2 = (col2 >= 0) ? col2 : w - 1;
686 col1 = qMin(col1, w - 1);
687 col2 = qMin(col2, w - 1);
689 int zoom = 100;
690 int distance = h * 100 / zoom;
691 PFreal sdx = fcos(slide.angle);
692 PFreal sdy = fsin(slide.angle);
693 PFreal xs = slide.cx - state->slideWidth * sdx / 2;
694 PFreal ys = slide.cy - state->slideWidth * sdy / 2;
695 PFreal dist = distance * PFREAL_ONE;
697 int xi = qMax((PFreal)0, ((w * PFREAL_ONE / 2) + fdiv(xs * h, dist + ys)) >> PFREAL_SHIFT);
698 if (xi >= w)
700 return rect;
703 bool flag = false;
704 rect.setLeft(xi);
705 for (int x = qMax(xi, col1); x <= col2; x++) {
706 PFreal hity = 0;
707 PFreal fk = rays[x];
708 if (sdy) {
709 fk = fk - fdiv(sdx, sdy);
710 hity = -fdiv((rays[x] * distance - slide.cx + slide.cy * sdx / sdy), fk);
713 dist = distance * PFREAL_ONE + hity;
714 if (dist < 0)
715 continue;
717 PFreal hitx = fmul(dist, rays[x]);
718 PFreal hitdist = fdiv(hitx - slide.cx, sdx);
720 int column = sw / 2 + (hitdist >> PFREAL_SHIFT);
721 if (column >= sw)
722 break;
723 if (column < 0)
724 continue;
726 rect.setRight(x);
727 if (!flag)
728 rect.setLeft(x);
729 flag = true;
731 int y1 = h / 2;
732 int y2 = y1 + 1;
733 QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
734 QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
735 QRgb pixelstep = pixel2 - pixel1;
737 int center = (sh / 2);
738 int dy = dist / h;
739 int p1 = center * PFREAL_ONE - dy / 2;
740 int p2 = center * PFREAL_ONE + dy / 2;
742 const QRgb *ptr = (const QRgb*)(src->scanLine(column));
743 if (blend == 256)
744 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
745 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
746 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
747 p1 -= dy;
748 p2 += dy;
749 y1--;
750 y2++;
751 pixel1 -= pixelstep;
752 pixel2 += pixelstep;
754 else
755 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
756 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
757 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
758 *pixel1 = blendColor(c1, bgcolor, blend);
759 *pixel2 = blendColor(c2, bgcolor, blend);
760 p1 -= dy;
761 p2 += dy;
762 y1--;
763 y2++;
764 pixel1 -= pixelstep;
765 pixel2 += pixelstep;
769 rect.setTop(0);
770 rect.setBottom(h - 1);
771 return rect;
774 void PictureFlowSoftwareRenderer::renderSlides()
776 int nleft = state->leftSlides.count();
777 int nright = state->rightSlides.count();
779 QRect r = renderSlide(state->centerSlide);
780 int c1 = r.left();
781 int c2 = r.right();
783 for (int index = 0; index < nleft; index++) {
784 QRect rs = renderSlide(state->leftSlides[index], 0, c1 - 1);
785 if (!rs.isEmpty())
786 c1 = rs.left();
788 for (int index = 0; index < nright; index++) {
789 QRect rs = renderSlide(state->rightSlides[index], c2 + 1, buffer.width());
790 if (!rs.isEmpty())
791 c2 = rs.right();
795 // Render the slides. Updates only the offscreen buffer.
796 void PictureFlowSoftwareRenderer::render()
798 buffer.fill(state->backgroundColor);
799 renderSlides();
800 dirty = false;
803 // -----------------------------------------
805 class PictureFlowPrivate
807 public:
808 PictureFlowState* state;
809 PictureFlowAnimator* animator;
810 PictureFlowAbstractRenderer* renderer;
811 QTimer triggerTimer;
815 PictureFlow::PictureFlow(QWidget* parent, VLCModel* _p_model): QWidget(parent)
817 d = new PictureFlowPrivate;
818 d->state = new PictureFlowState;
819 d->state->model = _p_model;
820 d->state->reset();
821 d->state->reposition();
823 d->renderer = new PictureFlowSoftwareRenderer;
824 d->renderer->state = d->state;
825 d->renderer->widget = this;
826 d->renderer->init();
828 d->animator = new PictureFlowAnimator;
829 d->animator->state = d->state;
830 QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
832 QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
834 setAttribute(Qt::WA_StaticContents, true);
835 setAttribute(Qt::WA_OpaquePaintEvent, true);
836 setAttribute(Qt::WA_NoSystemBackground, true);
839 PictureFlow::~PictureFlow()
841 delete d->renderer;
842 delete d->animator;
843 delete d->state;
844 delete d;
847 int PictureFlow::slideCount() const
849 return d->state->model->rowCount( d->state->model->currentIndex().parent() );
852 QColor PictureFlow::backgroundColor() const
854 return QColor(d->state->backgroundColor);
857 void PictureFlow::setBackgroundColor(const QColor& c)
859 d->state->backgroundColor = c.rgb();
860 triggerRender();
863 QSize PictureFlow::slideSize() const
865 return QSize(d->state->slideWidth, d->state->slideHeight);
868 void PictureFlow::setSlideSize(QSize size)
870 d->state->slideWidth = size.width();
871 d->state->slideHeight = size.height();
872 d->state->reposition();
873 triggerRender();
876 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
878 return d->state->reflectionEffect;
881 void PictureFlow::setReflectionEffect(ReflectionEffect effect)
883 d->state->reflectionEffect = effect;
884 triggerRender();
887 int PictureFlow::centerIndex() const
889 return d->state->centerIndex;
892 void PictureFlow::setCenterIndex(int index)
894 index = qMin(index, slideCount() - 1);
895 index = qMax(index, 0);
896 d->state->centerIndex = index;
897 d->state->reset();
898 d->animator->stop(index);
899 triggerRender();
902 void PictureFlow::clear()
904 d->state->reset();
905 triggerRender();
908 void PictureFlow::render()
910 d->renderer->dirty = true;
911 update();
914 void PictureFlow::triggerRender()
916 d->triggerTimer.setSingleShot(true);
917 d->triggerTimer.start(0);
920 void PictureFlow::showPrevious()
922 int step = d->animator->step;
923 int center = d->state->centerIndex;
925 if (step > 0)
926 d->animator->start(center);
928 if (step == 0)
929 if (center > 0)
930 d->animator->start(center - 1);
932 if (step < 0)
933 d->animator->target = qMax(0, center - 2);
936 void PictureFlow::showNext()
938 int step = d->animator->step;
939 int center = d->state->centerIndex;
941 if (step < 0)
942 d->animator->start(center);
944 if (step == 0)
945 if (center < slideCount() - 1)
946 d->animator->start(center + 1);
948 if (step > 0)
949 d->animator->target = qMin(center + 2, slideCount() - 1);
952 void PictureFlow::showSlide(int index)
954 index = qMax(index, 0);
955 index = qMin(slideCount() - 1, index);
956 if (index == d->state->centerSlide.slideIndex)
957 return;
959 d->animator->start(index);
962 void PictureFlow::keyPressEvent(QKeyEvent* event)
964 if (event->key() == Qt::Key_Left) {
965 if (event->modifiers() == Qt::ControlModifier)
966 showSlide(centerIndex() - 10);
967 else
968 showPrevious();
969 event->accept();
970 return;
973 if (event->key() == Qt::Key_Right) {
974 if (event->modifiers() == Qt::ControlModifier)
975 showSlide(centerIndex() + 10);
976 else
977 showNext();
978 event->accept();
979 return;
982 event->ignore();
985 void PictureFlow::mousePressEvent(QMouseEvent* event)
987 if (event->x() > width() / 2 + d->state->slideWidth/2 )
988 showNext();
989 else if (event->x() < width() / 2 - d->state->slideWidth/2 )
990 showPrevious();
991 else if ( d->state->model->currentIndex().row() != d->state->centerIndex )
992 d->state->model->activateItem( d->state->model->index( d->state->centerIndex, 0,
993 d->state->model->currentIndex().parent() ) );
996 void PictureFlow::paintEvent(QPaintEvent* event)
998 Q_UNUSED(event);
999 d->renderer->paint();
1002 void PictureFlow::resizeEvent(QResizeEvent* event)
1004 triggerRender();
1005 QWidget::resizeEvent(event);
1008 void PictureFlow::updateAnimation()
1010 int old_center = d->state->centerIndex;
1011 d->animator->update();
1012 triggerRender();
1013 if (d->state->centerIndex != old_center)
1014 emit centerIndexChanged(d->state->centerIndex);