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