Update copyright headers
[qt:qt.git] / demos / embedded / fluidlauncher / pictureflow.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the ActiveQt framework of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of The Qt Company Ltd nor the names of its
21 **     contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 /*
42   ORIGINAL COPYRIGHT HEADER
43   PictureFlow - animated image show widget
44   http://pictureflow.googlecode.com
45
46   Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
47
48   Permission is hereby granted, free of charge, to any person obtaining a copy
49   of this software and associated documentation files (the "Software"), to deal
50   in the Software without restriction, including without limitation the rights
51   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52   copies of the Software, and to permit persons to whom the Software is
53   furnished to do so, subject to the following conditions:
54
55   The above copyright notice and this permission notice shall be included in
56   all copies or substantial portions of the Software.
57
58   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64   THE SOFTWARE.
65 */
66
67 #include "pictureflow.h"
68
69 #include <QBasicTimer>
70 #include <QCache>
71 #include <QImage>
72 #include <QKeyEvent>
73 #include <QPainter>
74 #include <QPixmap>
75 #include <QTimer>
76 #include <QVector>
77 #include <QWidget>
78 #include <QTime>
79
80 #ifdef Q_WS_QWS
81 #include <QScreen>
82 #endif
83
84 #include <QDebug>
85
86 static const int captionFontSize =
87 #ifdef Q_WS_S60
88     8;
89 #else
90     14;
91 #endif
92
93
94 // uncomment this to enable bilinear filtering for texture mapping
95 // gives much better rendering, at the cost of memory space
96 // #define PICTUREFLOW_BILINEAR_FILTER
97
98 // for fixed-point arithmetic, we need minimum 32-bit long
99 // long long (64-bit) might be useful for multiplication and division
100 typedef long PFreal;
101
102 typedef unsigned short QRgb565;
103
104 #define RGB565_RED_MASK 0xF800
105 #define RGB565_GREEN_MASK 0x07E0
106 #define RGB565_BLUE_MASK 0x001F
107
108 #define RGB565_RED(col) ((col&RGB565_RED_MASK)>>11)
109 #define RGB565_GREEN(col) ((col&RGB565_GREEN_MASK)>>5)
110 #define RGB565_BLUE(col) (col&RGB565_BLUE_MASK)
111
112 #define PFREAL_SHIFT 10
113 #define PFREAL_FACTOR (1 << PFREAL_SHIFT)
114 #define PFREAL_ONE (1 << PFREAL_SHIFT)
115 #define PFREAL_HALF (PFREAL_ONE >> 1)
116
117 inline PFreal fmul(PFreal a, PFreal b)
118 {
119   return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
120 }
121
122 inline PFreal fdiv(PFreal num, PFreal den)
123 {
124   long long p = (long long)(num) << (PFREAL_SHIFT*2);
125   long long q = p / (long long)den;
126   long long r = q >> PFREAL_SHIFT;
127
128   return r;
129 }
130
131 inline float fixedToFloat(PFreal val)
132 {
133   return ((float)val) / (float)PFREAL_ONE;
134 }
135
136 inline PFreal floatToFixed(float val)
137 {
138   return (PFreal)(val*PFREAL_ONE);
139 }
140
141 #define IANGLE_MAX 1024
142 #define IANGLE_MASK 1023
143
144 // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
145 static const PFreal sinTable[IANGLE_MAX] = {
146      3,      9,     15,     21,     28,     34,     40,     47,
147     53,     59,     65,     72,     78,     84,     90,     97,
148    103,    109,    115,    122,    128,    134,    140,    147,
149    153,    159,    165,    171,    178,    184,    190,    196,
150    202,    209,    215,    221,    227,    233,    239,    245,
151    251,    257,    264,    270,    276,    282,    288,    294,
152    300,    306,    312,    318,    324,    330,    336,    342,
153    347,    353,    359,    365,    371,    377,    383,    388,
154    394,    400,    406,    412,    417,    423,    429,    434,
155    440,    446,    451,    457,    463,    468,    474,    479,
156    485,    491,    496,    501,    507,    512,    518,    523,
157    529,    534,    539,    545,    550,    555,    561,    566,
158    571,    576,    581,    587,    592,    597,    602,    607,
159    612,    617,    622,    627,    632,    637,    642,    647,
160    652,    656,    661,    666,    671,    675,    680,    685,
161    690,    694,    699,    703,    708,    712,    717,    721,
162    726,    730,    735,    739,    743,    748,    752,    756,
163    760,    765,    769,    773,    777,    781,    785,    789,
164    793,    797,    801,    805,    809,    813,    816,    820,
165    824,    828,    831,    835,    839,    842,    846,    849,
166    853,    856,    860,    863,    866,    870,    873,    876,
167    879,    883,    886,    889,    892,    895,    898,    901,
168    904,    907,    910,    913,    916,    918,    921,    924,
169    927,    929,    932,    934,    937,    939,    942,    944,
170    947,    949,    951,    954,    956,    958,    960,    963,
171    965,    967,    969,    971,    973,    975,    977,    978,
172    980,    982,    984,    986,    987,    989,    990,    992,
173    994,    995,    997,    998,    999,   1001,   1002,   1003,
174   1004,   1006,   1007,   1008,   1009,   1010,   1011,   1012,
175   1013,   1014,   1015,   1015,   1016,   1017,   1018,   1018,
176   1019,   1019,   1020,   1020,   1021,   1021,   1022,   1022,
177   1022,   1023,   1023,   1023,   1023,   1023,   1023,   1023,
178   1023,   1023,   1023,   1023,   1023,   1023,   1023,   1022,
179   1022,   1022,   1021,   1021,   1020,   1020,   1019,   1019,
180   1018,   1018,   1017,   1016,   1015,   1015,   1014,   1013,
181   1012,   1011,   1010,   1009,   1008,   1007,   1006,   1004,
182   1003,   1002,   1001,    999,    998,    997,    995,    994,
183    992,    990,    989,    987,    986,    984,    982,    980,
184    978,    977,    975,    973,    971,    969,    967,    965,
185    963,    960,    958,    956,    954,    951,    949,    947,
186    944,    942,    939,    937,    934,    932,    929,    927,
187    924,    921,    918,    916,    913,    910,    907,    904,
188    901,    898,    895,    892,    889,    886,    883,    879,
189    876,    873,    870,    866,    863,    860,    856,    853,
190    849,    846,    842,    839,    835,    831,    828,    824,
191    820,    816,    813,    809,    805,    801,    797,    793,
192    789,    785,    781,    777,    773,    769,    765,    760,
193    756,    752,    748,    743,    739,    735,    730,    726,
194    721,    717,    712,    708,    703,    699,    694,    690,
195    685,    680,    675,    671,    666,    661,    656,    652,
196    647,    642,    637,    632,    627,    622,    617,    612,
197    607,    602,    597,    592,    587,    581,    576,    571,
198    566,    561,    555,    550,    545,    539,    534,    529,
199    523,    518,    512,    507,    501,    496,    491,    485,
200    479,    474,    468,    463,    457,    451,    446,    440,
201    434,    429,    423,    417,    412,    406,    400,    394,
202    388,    383,    377,    371,    365,    359,    353,    347,
203    342,    336,    330,    324,    318,    312,    306,    300,
204    294,    288,    282,    276,    270,    264,    257,    251,
205    245,    239,    233,    227,    221,    215,    209,    202,
206    196,    190,    184,    178,    171,    165,    159,    153,
207    147,    140,    134,    128,    122,    115,    109,    103,
208     97,     90,     84,     78,     72,     65,     59,     53,
209     47,     40,     34,     28,     21,     15,      9,      3,
210     -4,    -10,    -16,    -22,    -29,    -35,    -41,    -48,
211    -54,    -60,    -66,    -73,    -79,    -85,    -91,    -98,
212   -104,   -110,   -116,   -123,   -129,   -135,   -141,   -148,
213   -154,   -160,   -166,   -172,   -179,   -185,   -191,   -197,
214   -203,   -210,   -216,   -222,   -228,   -234,   -240,   -246,
215   -252,   -258,   -265,   -271,   -277,   -283,   -289,   -295,
216   -301,   -307,   -313,   -319,   -325,   -331,   -337,   -343,
217   -348,   -354,   -360,   -366,   -372,   -378,   -384,   -389,
218   -395,   -401,   -407,   -413,   -418,   -424,   -430,   -435,
219   -441,   -447,   -452,   -458,   -464,   -469,   -475,   -480,
220   -486,   -492,   -497,   -502,   -508,   -513,   -519,   -524,
221   -530,   -535,   -540,   -546,   -551,   -556,   -562,   -567,
222   -572,   -577,   -582,   -588,   -593,   -598,   -603,   -608,
223   -613,   -618,   -623,   -628,   -633,   -638,   -643,   -648,
224   -653,   -657,   -662,   -667,   -672,   -676,   -681,   -686,
225   -691,   -695,   -700,   -704,   -709,   -713,   -718,   -722,
226   -727,   -731,   -736,   -740,   -744,   -749,   -753,   -757,
227   -761,   -766,   -770,   -774,   -778,   -782,   -786,   -790,
228   -794,   -798,   -802,   -806,   -810,   -814,   -817,   -821,
229   -825,   -829,   -832,   -836,   -840,   -843,   -847,   -850,
230   -854,   -857,   -861,   -864,   -867,   -871,   -874,   -877,
231   -880,   -884,   -887,   -890,   -893,   -896,   -899,   -902,
232   -905,   -908,   -911,   -914,   -917,   -919,   -922,   -925,
233   -928,   -930,   -933,   -935,   -938,   -940,   -943,   -945,
234   -948,   -950,   -952,   -955,   -957,   -959,   -961,   -964,
235   -966,   -968,   -970,   -972,   -974,   -976,   -978,   -979,
236   -981,   -983,   -985,   -987,   -988,   -990,   -991,   -993,
237   -995,   -996,   -998,   -999,  -1000,  -1002,  -1003,  -1004,
238  -1005,  -1007,  -1008,  -1009,  -1010,  -1011,  -1012,  -1013,
239  -1014,  -1015,  -1016,  -1016,  -1017,  -1018,  -1019,  -1019,
240  -1020,  -1020,  -1021,  -1021,  -1022,  -1022,  -1023,  -1023,
241  -1023,  -1024,  -1024,  -1024,  -1024,  -1024,  -1024,  -1024,
242  -1024,  -1024,  -1024,  -1024,  -1024,  -1024,  -1024,  -1023,
243  -1023,  -1023,  -1022,  -1022,  -1021,  -1021,  -1020,  -1020,
244  -1019,  -1019,  -1018,  -1017,  -1016,  -1016,  -1015,  -1014,
245  -1013,  -1012,  -1011,  -1010,  -1009,  -1008,  -1007,  -1005,
246  -1004,  -1003,  -1002,  -1000,   -999,   -998,   -996,   -995,
247   -993,   -991,   -990,   -988,   -987,   -985,   -983,   -981,
248   -979,   -978,   -976,   -974,   -972,   -970,   -968,   -966,
249   -964,   -961,   -959,   -957,   -955,   -952,   -950,   -948,
250   -945,   -943,   -940,   -938,   -935,   -933,   -930,   -928,
251   -925,   -922,   -919,   -917,   -914,   -911,   -908,   -905,
252   -902,   -899,   -896,   -893,   -890,   -887,   -884,   -880,
253   -877,   -874,   -871,   -867,   -864,   -861,   -857,   -854,
254   -850,   -847,   -843,   -840,   -836,   -832,   -829,   -825,
255   -821,   -817,   -814,   -810,   -806,   -802,   -798,   -794,
256   -790,   -786,   -782,   -778,   -774,   -770,   -766,   -761,
257   -757,   -753,   -749,   -744,   -740,   -736,   -731,   -727,
258   -722,   -718,   -713,   -709,   -704,   -700,   -695,   -691,
259   -686,   -681,   -676,   -672,   -667,   -662,   -657,   -653,
260   -648,   -643,   -638,   -633,   -628,   -623,   -618,   -613,
261   -608,   -603,   -598,   -593,   -588,   -582,   -577,   -572,
262   -567,   -562,   -556,   -551,   -546,   -540,   -535,   -530,
263   -524,   -519,   -513,   -508,   -502,   -497,   -492,   -486,
264   -480,   -475,   -469,   -464,   -458,   -452,   -447,   -441,
265   -435,   -430,   -424,   -418,   -413,   -407,   -401,   -395,
266   -389,   -384,   -378,   -372,   -366,   -360,   -354,   -348,
267   -343,   -337,   -331,   -325,   -319,   -313,   -307,   -301,
268   -295,   -289,   -283,   -277,   -271,   -265,   -258,   -252,
269   -246,   -240,   -234,   -228,   -222,   -216,   -210,   -203,
270   -197,   -191,   -185,   -179,   -172,   -166,   -160,   -154,
271   -148,   -141,   -135,   -129,   -123,   -116,   -110,   -104,
272    -98,    -91,    -85,    -79,    -73,    -66,    -60,    -54,
273    -48,    -41,    -35,    -29,    -22,    -16,    -10,     -4
274 };
275
276 // this is the program the generate the above table
277 #if 0
278 #include <stdio.h>
279 #include <math.h>
280
281 #ifndef M_PI
282 #define M_PI 3.14159265358979323846
283 #endif
284
285 #define PFREAL_ONE 1024
286 #define IANGLE_MAX 1024
287
288 int main(int, char**)
289 {
290   FILE*f = fopen("table.c","wt");
291   fprintf(f,"PFreal sinTable[] = {\n");
292   for(int i = 0; i < 128; i++)
293   {
294     for(int j = 0; j < 8; j++)
295     {
296       int iang = j+i*8;
297       double ii = (double)iang + 0.5;
298       double angle = ii * 2 * M_PI / IANGLE_MAX;
299       double sinAngle = sin(angle);
300       fprintf(f,"%6d, ", (int)(floor(PFREAL_ONE*sinAngle)));
301     }
302     fprintf(f,"\n");
303   }
304   fprintf(f,"};\n");
305   fclose(f);
306
307   return 0;
308 }
309 #endif
310
311 inline PFreal fsin(int iangle)
312 {
313   while(iangle < 0)
314     iangle += IANGLE_MAX;
315   return sinTable[iangle & IANGLE_MASK];
316 }
317
318 inline PFreal fcos(int iangle)
319 {
320   // quarter phase shift
321   return fsin(iangle + (IANGLE_MAX >> 2));
322 }
323
324 struct SlideInfo
325 {
326   int slideIndex;
327   int angle;
328   PFreal cx;
329   PFreal cy;
330 };
331
332 class PictureFlowPrivate
333 {
334 public:
335   PictureFlowPrivate(PictureFlow* widget);
336
337   int slideCount() const;
338   void setSlideCount(int count);
339
340   QSize slideSize() const;
341   void setSlideSize(QSize size);
342
343   int zoomFactor() const;
344   void setZoomFactor(int z);
345
346   QImage slide(int index) const;
347   void setSlide(int index, const QImage& image);
348
349   int currentSlide() const;
350   void setCurrentSlide(int index);
351
352   int getTarget() const;
353
354   void showPrevious();
355   void showNext();
356   void showSlide(int index);
357
358   void resize(int w, int h);
359
360   void render();
361   void startAnimation();
362   void updateAnimation();
363
364   void clearSurfaceCache();
365
366   QImage buffer;
367   QBasicTimer animateTimer;
368
369   bool   singlePress;
370   int    singlePressThreshold;
371   QPoint firstPress;
372   QPoint previousPos;
373   QTime  previousPosTimestamp;
374   int    pixelDistanceMoved;
375   int    pixelsToMovePerSlide;
376
377   QVector<QString> captions;
378
379 private:
380   PictureFlow* widget;
381
382   int slideWidth;
383   int slideHeight;
384   int zoom;
385
386   QVector<QImage> slideImages;
387   int centerIndex;
388   SlideInfo centerSlide;
389   QVector<SlideInfo> leftSlides;
390   QVector<SlideInfo> rightSlides;
391
392   QVector<PFreal> rays;
393   int itilt;
394   int spacing;
395   PFreal offsetX;
396   PFreal offsetY;
397
398   QImage blankSurface;
399   QCache<int, QImage> surfaceCache;
400   QTimer triggerTimer;
401
402   int slideFrame;
403   int step;
404   int target;
405   int fade;
406
407   void recalc(int w, int h);
408   QRect renderSlide(const SlideInfo &slide, int alpha=256, int col1=-1, int col=-1);
409   QImage* surface(int slideIndex);
410   void triggerRender();
411   void resetSlides();
412 };
413
414 PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w)
415 {
416   widget = w;
417
418   slideWidth = 200;
419   slideHeight = 200;
420   zoom = 100;
421
422   centerIndex = 0;
423
424   slideFrame = 0;
425   step = 0;
426   target = 0;
427   fade = 256;
428
429   triggerTimer.setSingleShot(true);
430   triggerTimer.setInterval(0);
431   QObject::connect(&triggerTimer, SIGNAL(timeout()), widget, SLOT(render()));
432
433   recalc(200, 200);
434   resetSlides();
435 }
436
437 int PictureFlowPrivate::slideCount() const
438 {
439   return slideImages.count();
440 }
441
442 void PictureFlowPrivate::setSlideCount(int count)
443 {
444   slideImages.resize(count);
445   captions.resize(count);
446   surfaceCache.clear();
447   resetSlides();
448   triggerRender();
449 }
450
451 QSize PictureFlowPrivate::slideSize() const
452 {
453   return QSize(slideWidth, slideHeight);
454 }
455
456 void PictureFlowPrivate::setSlideSize(QSize size)
457 {
458   slideWidth = size.width();
459   slideHeight = size.height();
460   recalc(buffer.width(), buffer.height());
461   triggerRender();
462 }
463
464 int PictureFlowPrivate::zoomFactor() const
465 {
466   return zoom;
467 }
468
469 void PictureFlowPrivate::setZoomFactor(int z)
470 {
471   if(z <= 0)
472     return;
473
474   zoom = z;
475   recalc(buffer.width(), buffer.height());
476   triggerRender();
477 }
478
479 QImage PictureFlowPrivate::slide(int index) const
480 {
481   return slideImages[index];
482 }
483
484 void PictureFlowPrivate::setSlide(int index, const QImage& image)
485 {
486   if((index >= 0) && (index < slideImages.count()))
487   {
488     slideImages[index] = image;
489     surfaceCache.remove(index);
490     triggerRender();
491   }
492 }
493
494 int PictureFlowPrivate::getTarget() const
495 {
496   return target;
497 }
498
499 int PictureFlowPrivate::currentSlide() const
500 {
501   return centerIndex;
502 }
503
504 void PictureFlowPrivate::setCurrentSlide(int index)
505 {
506   step = 0;
507   centerIndex = qBound(index, 0, slideImages.count()-1);
508   target = centerIndex;
509   slideFrame = index << 16;
510   resetSlides();
511   triggerRender();
512 }
513
514 void PictureFlowPrivate::showPrevious()
515 {
516   if(step >= 0)
517   {
518     if(centerIndex > 0)
519     {
520       target--;
521       startAnimation();
522     }
523   }
524   else
525   {
526     target = qMax(0, centerIndex - 2);
527   }
528 }
529
530 void PictureFlowPrivate::showNext()
531 {
532   if(step <= 0)
533   {
534     if(centerIndex < slideImages.count()-1)
535     {
536       target++;
537       startAnimation();
538     }
539   }
540   else
541   {
542     target = qMin(centerIndex + 2, slideImages.count()-1);
543   }
544 }
545
546 void PictureFlowPrivate::showSlide(int index)
547 {
548   index = qMax(index, 0);
549   index = qMin(slideImages.count()-1, index);
550   if(index == centerSlide.slideIndex)
551     return;
552
553   target = index;
554   startAnimation();
555 }
556
557 void PictureFlowPrivate::resize(int w, int h)
558 {
559   recalc(w, h);
560   resetSlides();
561   triggerRender();
562 }
563
564
565 // adjust slides so that they are in "steady state" position
566 void PictureFlowPrivate::resetSlides()
567 {
568   centerSlide.angle = 0;
569   centerSlide.cx = 0;
570   centerSlide.cy = 0;
571   centerSlide.slideIndex = centerIndex;
572
573   leftSlides.clear();
574   leftSlides.resize(3);
575   for(int i = 0; i < leftSlides.count(); i++)
576   {
577     SlideInfo& si = leftSlides[i];
578     si.angle = itilt;
579     si.cx = -(offsetX + spacing*i*PFREAL_ONE);
580     si.cy = offsetY;
581     si.slideIndex = centerIndex-1-i;
582     //qDebug() << "Left[" << i << "] x=" << fixedToFloat(si.cx) << ", y=" << fixedToFloat(si.cy) ;
583   }
584
585   rightSlides.clear();
586   rightSlides.resize(3);
587   for(int i = 0; i < rightSlides.count(); i++)
588   {
589     SlideInfo& si = rightSlides[i];
590     si.angle = -itilt;
591     si.cx = offsetX + spacing*i*PFREAL_ONE;
592     si.cy = offsetY;
593     si.slideIndex = centerIndex+1+i;
594     //qDebug() << "Right[" << i << "] x=" << fixedToFloat(si.cx) << ", y=" << fixedToFloat(si.cy) ;
595   }
596 }
597
598 #define BILINEAR_STRETCH_HOR 4
599 #define BILINEAR_STRETCH_VER 4
600
601 static QImage prepareSurface(QImage img, int w, int h)
602 {
603   Qt::TransformationMode mode = Qt::SmoothTransformation;
604   img = img.scaled(w, h, Qt::IgnoreAspectRatio, mode);
605
606   // slightly larger, to accommodate for the reflection
607   int hs = h * 2;
608   int hofs = h / 3;
609
610   // offscreen buffer: black is sweet
611   QImage result(hs, w, QImage::Format_RGB16);
612   result.fill(0);
613
614   // transpose the image, this is to speed-up the rendering
615   // because we process one column at a time
616   // (and much better and faster to work row-wise, i.e in one scanline)
617   for(int x = 0; x < w; x++)
618     for(int y = 0; y < h; y++)
619       result.setPixel(hofs + y, x, img.pixel(x, y));
620
621   // create the reflection
622   int ht = hs - h - hofs;
623   int hte = ht;
624   for(int x = 0; x < w; x++)
625     for(int y = 0; y < ht; y++)
626     {
627       QRgb color = img.pixel(x, img.height()-y-1);
628       //QRgb565 color = img.scanLine(img.height()-y-1) + x*sizeof(QRgb565); //img.pixel(x, img.height()-y-1);
629       int a = qAlpha(color);
630       int r = qRed(color)   * a / 256 * (hte - y) / hte * 3/5;
631       int g = qGreen(color) * a / 256 * (hte - y) / hte * 3/5;
632       int b = qBlue(color)  * a / 256 * (hte - y) / hte * 3/5;
633       result.setPixel(h+hofs+y, x, qRgb(r, g, b));
634     }
635
636 #ifdef PICTUREFLOW_BILINEAR_FILTER
637   int hh = BILINEAR_STRETCH_VER*hs;
638   int ww = BILINEAR_STRETCH_HOR*w;
639   result = result.scaled(hh, ww, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
640 #endif
641
642   return result;
643 }
644
645
646 // get transformed image for specified slide
647 // if it does not exist, create it and place it in the cache
648 QImage* PictureFlowPrivate::surface(int slideIndex)
649 {
650   if(slideIndex < 0)
651     return 0;
652   if(slideIndex >= slideImages.count())
653     return 0;
654
655   if(surfaceCache.contains(slideIndex))
656     return surfaceCache[slideIndex];
657
658   QImage img = widget->slide(slideIndex);
659   if(img.isNull())
660   {
661     if(blankSurface.isNull())
662     {
663       blankSurface = QImage(slideWidth, slideHeight, QImage::Format_RGB16);
664
665       QPainter painter(&blankSurface);
666       QPoint p1(slideWidth*4/10, 0);
667       QPoint p2(slideWidth*6/10, slideHeight);
668       QLinearGradient linearGrad(p1, p2);
669       linearGrad.setColorAt(0, Qt::black);
670       linearGrad.setColorAt(1, Qt::white); 
671       painter.setBrush(linearGrad);
672       painter.fillRect(0, 0, slideWidth, slideHeight, QBrush(linearGrad));
673
674       painter.setPen(QPen(QColor(64,64,64), 4));
675       painter.setBrush(QBrush());
676       painter.drawRect(2, 2, slideWidth-3, slideHeight-3);
677       painter.end();
678       blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight);
679     }
680     return &blankSurface;
681   }
682
683   surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight)));
684   return surfaceCache[slideIndex];
685 }
686
687
688 // Schedules rendering the slides. Call this function to avoid immediate
689 // render and thus cause less flicker.
690 void PictureFlowPrivate::triggerRender()
691 {
692   triggerTimer.start();
693 }
694
695 // Render the slides. Updates only the offscreen buffer.
696 void PictureFlowPrivate::render()
697 {
698   buffer.fill(0);
699
700   int nleft = leftSlides.count();
701   int nright = rightSlides.count();
702
703   QRect r = renderSlide(centerSlide);
704   int c1 = r.left();
705   int c2 = r.right();
706
707   if(step == 0)
708   {
709     // no animation, boring plain rendering
710     for(int index = 0; index < nleft-1; index++)
711     {
712       int alpha = (index < nleft-2) ? 256 : 128;
713       QRect rs = renderSlide(leftSlides[index], alpha, 0, c1-1);
714       if(!rs.isEmpty())
715         c1 = rs.left();
716     }
717     for(int index = 0; index < nright-1; index++)
718     {
719       int alpha = (index < nright-2) ? 256 : 128;
720       QRect rs = renderSlide(rightSlides[index], alpha, c2+1, buffer.width());
721       if(!rs.isEmpty())
722         c2 = rs.right();
723     }
724
725     QPainter painter;
726     painter.begin(&buffer);
727
728     QFont font("Arial", captionFontSize);
729     font.setBold(true);
730     painter.setFont(font);
731     painter.setPen(Qt::white);
732     //painter.setPen(QColor(255,255,255,127));
733
734     if (!captions.isEmpty())
735         painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/4),
736         Qt::AlignCenter, captions[centerIndex]);
737
738     painter.end();
739
740   }
741   else
742   {
743     // the first and last slide must fade in/fade out
744     for(int index = 0; index < nleft; index++)
745     {
746       int alpha = 256;
747       if(index == nleft-1)
748         alpha = (step > 0) ? 0 : 128-fade/2;
749       if(index == nleft-2)
750         alpha = (step > 0) ? 128-fade/2 : 256-fade/2;
751       if(index == nleft-3)
752         alpha = (step > 0) ? 256-fade/2 : 256;
753       QRect rs = renderSlide(leftSlides[index], alpha, 0, c1-1);
754       if(!rs.isEmpty())
755         c1 = rs.left();
756
757       alpha = (step > 0) ? 256-fade/2 : 256;
758     }
759     for(int index = 0; index < nright; index++)
760     {
761       int alpha = (index < nright-2) ? 256 : 128;
762       if(index == nright-1)
763         alpha = (step > 0) ? fade/2 : 0;
764       if(index == nright-2)
765         alpha = (step > 0) ? 128+fade/2 : fade/2;
766       if(index == nright-3)
767         alpha = (step > 0) ? 256 : 128+fade/2;
768       QRect rs = renderSlide(rightSlides[index], alpha, c2+1, buffer.width());
769       if(!rs.isEmpty())
770         c2 = rs.right();
771     }
772
773     QPainter painter;
774     painter.begin(&buffer);
775
776     QFont font("Arial", captionFontSize);
777     font.setBold(true);
778     painter.setFont(font);
779
780     int leftTextIndex = (step>0) ? centerIndex : centerIndex-1;
781
782     painter.setPen(QColor(255,255,255, (255-fade) ));
783     painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/4),
784                       Qt::AlignCenter, captions[leftTextIndex]);
785
786     painter.setPen(QColor(255,255,255, fade));
787     painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/4),
788                       Qt::AlignCenter, captions[leftTextIndex+1]);
789
790     painter.end();
791   }
792 }
793
794
795 static inline uint BYTE_MUL_RGB16(uint x, uint a) {
796     a += 1;
797     uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
798     t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
799     return t;
800 }
801
802 static inline uint BYTE_MUL_RGB16_32(uint x, uint a) {
803     uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0;
804     t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f;
805     return t;
806 }
807
808
809 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
810 // alpha=256 means normal, alpha=0 is fully black, alpha=128 half transparent
811 // col1 and col2 limit the column for rendering.
812 QRect PictureFlowPrivate::renderSlide(const SlideInfo &slide, int alpha, 
813 int col1, int col2)
814 {
815   QImage* src = surface(slide.slideIndex);
816   if(!src)
817     return QRect();
818
819   QRect rect(0, 0, 0, 0);
820
821 #ifdef PICTUREFLOW_BILINEAR_FILTER
822   int sw = src->height() / BILINEAR_STRETCH_HOR;
823   int sh = src->width() / BILINEAR_STRETCH_VER;
824 #else
825   int sw = src->height();
826   int sh = src->width();
827 #endif
828   int h = buffer.height();
829   int w = buffer.width();
830
831   if(col1 > col2)
832   {
833     int c = col2;
834     col2 = col1;
835     col1 = c;
836   }
837
838   col1 = (col1 >= 0) ? col1 : 0;
839   col2 = (col2 >= 0) ? col2 : w-1;
840   col1 = qMin(col1, w-1);
841   col2 = qMin(col2, w-1);
842
843   int distance = h * 100 / zoom;
844   PFreal sdx = fcos(slide.angle);
845   PFreal sdy = fsin(slide.angle);
846   PFreal xs = slide.cx - slideWidth * sdx/2;
847   PFreal ys = slide.cy - slideWidth * sdy/2;
848   PFreal dist = distance * PFREAL_ONE;
849
850   int xi = qMax((PFreal)0, ((w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys)) >> PFREAL_SHIFT);
851   if(xi >= w)
852     return rect;
853
854   bool flag = false;
855   rect.setLeft(xi);
856   for(int x = qMax(xi, col1); x <= col2; x++)
857   {
858     PFreal hity = 0;
859     PFreal fk = rays[x];
860     if(sdy)
861     {
862       fk = fk - fdiv(sdx,sdy);
863       hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk);
864     }
865
866     dist = distance*PFREAL_ONE + hity;
867     if(dist < 0)
868       continue;
869
870     PFreal hitx = fmul(dist, rays[x]);
871     PFreal hitdist = fdiv(hitx - slide.cx, sdx);
872
873 #ifdef PICTUREFLOW_BILINEAR_FILTER
874     int column = sw*BILINEAR_STRETCH_HOR/2 + (hitdist*BILINEAR_STRETCH_HOR >> PFREAL_SHIFT);
875     if(column >= sw*BILINEAR_STRETCH_HOR)
876       break;
877 #else
878     int column = sw/2 + (hitdist >> PFREAL_SHIFT);
879     if(column >= sw)
880       break;
881 #endif
882     if(column < 0)
883       continue;
884
885     rect.setRight(x);
886     if(!flag)
887       rect.setLeft(x);
888     flag = true;
889
890     int y1 = h/2;
891     int y2 = y1+ 1;
892     QRgb565* pixel1 = (QRgb565*)(buffer.scanLine(y1)) + x;
893     QRgb565* pixel2 = (QRgb565*)(buffer.scanLine(y2)) + x;
894     int pixelstep = pixel2 - pixel1;
895
896 #ifdef PICTUREFLOW_BILINEAR_FILTER
897     int center = (sh*BILINEAR_STRETCH_VER/2);
898     int dy = dist*BILINEAR_STRETCH_VER / h;
899 #else
900     int center = (sh/2);
901     int dy = dist / h;
902 #endif
903     int p1 = center*PFREAL_ONE - dy/2;
904     int p2 = center*PFREAL_ONE + dy/2;
905
906     const QRgb565 *ptr = (const QRgb565*)(src->scanLine(column));
907     if(alpha == 256)
908       while((y1 >= 0) && (y2 < h) && (p1 >= 0))
909       {
910         *pixel1 = ptr[p1 >> PFREAL_SHIFT];
911         *pixel2 = ptr[p2 >> PFREAL_SHIFT];
912         p1 -= dy;
913         p2 += dy;
914         y1--;
915         y2++;
916         pixel1 -= pixelstep;
917         pixel2 += pixelstep;
918       }
919     else
920       while((y1 >= 0) && (y2 < h) && (p1 >= 0))
921       {
922         QRgb565 c1 = ptr[p1 >> PFREAL_SHIFT];
923         QRgb565 c2 = ptr[p2 >> PFREAL_SHIFT];
924
925         *pixel1 = BYTE_MUL_RGB16(c1, alpha);
926         *pixel2 = BYTE_MUL_RGB16(c2, alpha);
927
928 /*
929         int r1 = qRed(c1) * alpha/256;
930         int g1 = qGreen(c1) * alpha/256;
931         int b1 = qBlue(c1) * alpha/256;
932         int r2 = qRed(c2) * alpha/256;
933         int g2 = qGreen(c2) * alpha/256;
934         int b2 = qBlue(c2) * alpha/256;
935         *pixel1 = qRgb(r1, g1, b1);
936         *pixel2 = qRgb(r2, g2, b2);
937 */
938         p1 -= dy;
939         p2 += dy;
940         y1--;
941         y2++;
942         pixel1 -= pixelstep;
943         pixel2 += pixelstep;
944      }
945    }
946
947    rect.setTop(0);
948    rect.setBottom(h-1);
949    return rect;
950 }
951
952 // Updates look-up table and other stuff necessary for the rendering.
953 // Call this when the viewport size or slide dimension is changed.
954 void PictureFlowPrivate::recalc(int ww, int wh)
955 {
956   int w = (ww+1)/2;
957   int h = (wh+1)/2;
958   buffer = QImage(ww, wh, QImage::Format_RGB16);
959   buffer.fill(0);
960
961   rays.resize(w*2);
962
963   for(int i = 0; i < w; i++)
964   {
965     PFreal gg = (PFREAL_HALF + i * PFREAL_ONE) / (2*h);
966     rays[w-i-1] = -gg;
967     rays[w+i] = gg;
968   }
969
970   // pointer must move more than 1/15 of the window to enter drag mode
971   singlePressThreshold = ww / 15;
972 //  qDebug() << "singlePressThreshold now set to " << singlePressThreshold;
973
974   pixelsToMovePerSlide = ww / 3;
975 //  qDebug() << "pixelsToMovePerSlide now set to " << pixelsToMovePerSlide;
976
977   itilt = 80 * IANGLE_MAX / 360;  // approx. 80 degrees tilted
978
979   offsetY = slideWidth/2 * fsin(itilt);
980   offsetY += slideWidth * PFREAL_ONE / 4;
981
982 //  offsetX = slideWidth/2 * (PFREAL_ONE-fcos(itilt));
983 //  offsetX += slideWidth * PFREAL_ONE;
984
985   //         center slide             +         side slide
986   offsetX = slideWidth*PFREAL_ONE;
987 //  offsetX = 150*PFREAL_ONE;//(slideWidth/2)*PFREAL_ONE + ( slideWidth*fcos(itilt) )/2;
988 //  qDebug() << "center width = " << slideWidth;
989 //  qDebug() << "side width = " << fixedToFloat(slideWidth/2 * (PFREAL_ONE-fcos(itilt)));
990 //  qDebug() << "offsetX now " << fixedToFloat(offsetX);
991
992   spacing = slideWidth/5;
993
994   surfaceCache.clear();
995   blankSurface = QImage();
996 }
997
998 void PictureFlowPrivate::startAnimation()
999 {
1000   if(!animateTimer.isActive())
1001   {
1002     step = (target < centerSlide.slideIndex) ? -1 : 1;
1003     animateTimer.start(30, widget);
1004   }
1005 }
1006
1007 // Updates the animation effect. Call this periodically from a timer.
1008 void PictureFlowPrivate::updateAnimation()
1009 {
1010   if(!animateTimer.isActive())
1011     return;
1012   if(step == 0)
1013     return;
1014
1015   int speed = 16384;
1016
1017   // deaccelerate when approaching the target
1018   if(true)
1019   {
1020     const int max = 2 * 65536;
1021
1022     int fi = slideFrame;
1023     fi -= (target << 16);
1024     if(fi < 0)
1025       fi = -fi;
1026     fi = qMin(fi, max);
1027
1028     int ia = IANGLE_MAX * (fi-max/2) / (max*2);
1029     speed = 512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE;
1030   }
1031
1032   slideFrame += speed*step;
1033
1034   int index = slideFrame >> 16;
1035   int pos = slideFrame & 0xffff;
1036   int neg = 65536 - pos;
1037   int tick = (step < 0) ? neg : pos;
1038   PFreal ftick = (tick * PFREAL_ONE) >> 16;
1039
1040   // the leftmost and rightmost slide must fade away
1041   fade = pos / 256;
1042
1043   if(step < 0)
1044     index++;
1045   if(centerIndex != index)
1046   {
1047     centerIndex = index;
1048     slideFrame = index << 16;
1049     centerSlide.slideIndex = centerIndex;
1050     for(int i = 0; i < leftSlides.count(); i++)
1051       leftSlides[i].slideIndex = centerIndex-1-i;
1052     for(int i = 0; i < rightSlides.count(); i++)
1053       rightSlides[i].slideIndex = centerIndex+1+i;
1054   }
1055
1056   centerSlide.angle = (step * tick * itilt) >> 16;
1057   centerSlide.cx = -step * fmul(offsetX, ftick);
1058   centerSlide.cy = fmul(offsetY, ftick);
1059
1060   if(centerIndex == target)
1061   {
1062     resetSlides();
1063     animateTimer.stop();
1064     triggerRender();
1065     step = 0;
1066     fade = 256;
1067     return;
1068   }
1069
1070   for(int i = 0; i < leftSlides.count(); i++)
1071   {
1072     SlideInfo& si = leftSlides[i];
1073     si.angle = itilt;
1074     si.cx = -(offsetX + spacing*i*PFREAL_ONE + step*spacing*ftick);
1075     si.cy = offsetY;
1076   }
1077
1078   for(int i = 0; i < rightSlides.count(); i++)
1079   {
1080     SlideInfo& si = rightSlides[i];
1081     si.angle = -itilt;
1082     si.cx = offsetX + spacing*i*PFREAL_ONE - step*spacing*ftick;
1083     si.cy = offsetY;
1084   }
1085
1086   if(step > 0)
1087   {
1088     PFreal ftick = (neg * PFREAL_ONE) >> 16;
1089     rightSlides[0].angle = -(neg * itilt) >> 16;
1090     rightSlides[0].cx = fmul(offsetX, ftick);
1091     rightSlides[0].cy = fmul(offsetY, ftick);
1092   }
1093   else
1094   {
1095     PFreal ftick = (pos * PFREAL_ONE) >> 16;
1096     leftSlides[0].angle = (pos * itilt) >> 16;
1097     leftSlides[0].cx = -fmul(offsetX, ftick);
1098     leftSlides[0].cy = fmul(offsetY, ftick);
1099   }
1100
1101   // must change direction ?
1102   if(target < index) if(step > 0)
1103     step = -1;
1104   if(target > index) if(step < 0)
1105     step = 1;
1106
1107   triggerRender();
1108 }
1109
1110
1111 void PictureFlowPrivate::clearSurfaceCache()
1112 {
1113   surfaceCache.clear();
1114 }
1115
1116 // -----------------------------------------
1117
1118 PictureFlow::PictureFlow(QWidget* parent): QWidget(parent)
1119 {
1120   d = new PictureFlowPrivate(this);
1121
1122   setAttribute(Qt::WA_StaticContents, true);
1123   setAttribute(Qt::WA_OpaquePaintEvent, true);
1124   setAttribute(Qt::WA_NoSystemBackground, true);
1125
1126 #ifdef Q_WS_QWS
1127   if (QScreen::instance()->pixelFormat() != QImage::Format_Invalid)
1128     setAttribute(Qt::WA_PaintOnScreen, true);
1129 #endif
1130 }
1131
1132 PictureFlow::~PictureFlow()
1133 {
1134   delete d;
1135 }
1136
1137 int PictureFlow::slideCount() const
1138 {
1139   return d->slideCount();
1140 }
1141
1142 void PictureFlow::setSlideCount(int count)
1143 {
1144   d->setSlideCount(count);
1145 }
1146
1147 QSize PictureFlow::slideSize() const
1148 {
1149   return d->slideSize();
1150 }
1151
1152 void PictureFlow::setSlideSize(QSize size)
1153 {
1154   d->setSlideSize(size);
1155 }
1156
1157 int PictureFlow::zoomFactor() const
1158 {
1159   return d->zoomFactor();
1160 }
1161
1162 void PictureFlow::setZoomFactor(int z)
1163 {
1164   d->setZoomFactor(z);
1165 }
1166
1167 QImage PictureFlow::slide(int index) const
1168 {
1169   return d->slide(index);
1170 }
1171
1172 void PictureFlow::setSlide(int index, const QImage& image)
1173 {
1174   d->setSlide(index, image);
1175 }
1176
1177 void PictureFlow::setSlide(int index, const QPixmap& pixmap)
1178 {
1179   d->setSlide(index, pixmap.toImage());
1180 }
1181
1182 void PictureFlow::setSlideCaption(int index, QString caption)
1183 {
1184   d->captions[index] = caption;
1185 }
1186
1187
1188 int PictureFlow::currentSlide() const
1189 {
1190   return d->currentSlide();
1191 }
1192
1193 void PictureFlow::setCurrentSlide(int index)
1194 {
1195   d->setCurrentSlide(index);
1196 }
1197
1198 void PictureFlow::clear()
1199 {
1200   d->setSlideCount(0);
1201 }
1202
1203 void PictureFlow::clearCaches()
1204 {
1205   d->clearSurfaceCache();
1206 }
1207
1208 void PictureFlow::render()
1209 {
1210   d->render();
1211   update();
1212 }
1213
1214 void PictureFlow::showPrevious()
1215 {
1216   d->showPrevious();
1217 }
1218
1219 void PictureFlow::showNext()
1220 {
1221   d->showNext();
1222 }
1223
1224 void PictureFlow::showSlide(int index)
1225 {
1226   d->showSlide(index);
1227 }
1228
1229 void PictureFlow::keyPressEvent(QKeyEvent* event)
1230 {
1231   if(event->key() == Qt::Key_Left)
1232   {
1233     if(event->modifiers() == Qt::ControlModifier)
1234       showSlide(currentSlide()-10);
1235     else
1236       showPrevious();
1237     event->accept();
1238     return;
1239   }
1240
1241   if(event->key() == Qt::Key_Right)
1242   {
1243     if(event->modifiers() == Qt::ControlModifier)
1244       showSlide(currentSlide()+10);
1245     else
1246       showNext();
1247     event->accept();
1248     return;
1249   }
1250
1251   if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Select) {
1252     emit itemActivated(d->getTarget());
1253     event->accept();
1254     return;
1255   }
1256
1257   event->ignore();
1258 }
1259
1260 #define SPEED_LOWER_THRESHOLD 10
1261 #define SPEED_UPPER_LIMIT 40
1262
1263 void PictureFlow::mouseMoveEvent(QMouseEvent* event)
1264 {
1265   int distanceMovedSinceLastEvent = event->pos().x() - d->previousPos.x();
1266
1267   // Check to see if we need to switch from single press mode to a drag mode
1268   if (d->singlePress)
1269   {
1270     // Increment the distance moved for this event
1271     d->pixelDistanceMoved += distanceMovedSinceLastEvent;
1272
1273     // Check against threshold
1274     if (qAbs(d->pixelDistanceMoved) > d->singlePressThreshold)
1275     {
1276       d->singlePress = false;
1277 //      qDebug() << "DRAG MODE ON";
1278     }
1279   }
1280
1281   if (!d->singlePress)
1282   {
1283     int speed;
1284     // Calculate velocity in a 10th of a window width per second
1285     if (d->previousPosTimestamp.elapsed() == 0)
1286       speed = SPEED_LOWER_THRESHOLD;
1287     else
1288     {
1289       speed = ((qAbs(event->pos().x()-d->previousPos.x())*1000) / d->previousPosTimestamp.elapsed())
1290                     / (d->buffer.width() / 10);
1291
1292       if (speed < SPEED_LOWER_THRESHOLD)
1293         speed = SPEED_LOWER_THRESHOLD;
1294       else if (speed > SPEED_UPPER_LIMIT)
1295         speed = SPEED_UPPER_LIMIT;
1296       else {
1297         speed = SPEED_LOWER_THRESHOLD + (speed / 3);
1298 //        qDebug() << "ACCELERATION ENABLED Speed = " << speed << ", Distance = " << distanceMovedSinceLastEvent;
1299       }
1300     }
1301
1302 //    qDebug() << "Speed = " << speed;
1303
1304 //    int incr = ((event->pos().x() - d->previousPos.x())/10) * speed;
1305
1306 //    qDebug() << "Incremented by " << incr;
1307
1308     int incr = (distanceMovedSinceLastEvent * speed);
1309
1310     //qDebug() << "(distanceMovedSinceLastEvent * speed) = " << incr;
1311
1312     if (incr > d->pixelsToMovePerSlide*2) {
1313       incr = d->pixelsToMovePerSlide*2;
1314       //qDebug() << "Limiting incr to " << incr;
1315     }
1316
1317
1318     d->pixelDistanceMoved += (distanceMovedSinceLastEvent * speed);
1319  //   qDebug() << "distance: " << d->pixelDistanceMoved;
1320
1321     int slideInc;
1322
1323     slideInc = d->pixelDistanceMoved / (d->pixelsToMovePerSlide * 10);
1324
1325     if (slideInc != 0) {
1326       int targetSlide = d->getTarget() - slideInc;
1327       showSlide(targetSlide);
1328 //      qDebug() << "TargetSlide = " << targetSlide;
1329
1330       //qDebug() << "Decrementing pixelDistanceMoved by " << (d->pixelsToMovePerSlide *10) * slideInc;
1331
1332       d->pixelDistanceMoved -= (d->pixelsToMovePerSlide *10) * slideInc;
1333
1334 /*
1335       if ( (targetSlide <= 0) || (targetSlide >= d->slideCount()-1) )
1336         d->pixelDistanceMoved = 0;
1337 */
1338     }
1339   }
1340
1341   d->previousPos = event->pos();
1342   d->previousPosTimestamp.restart();
1343
1344   emit inputReceived();
1345 }
1346
1347 void PictureFlow::mousePressEvent(QMouseEvent* event)
1348 {
1349   d->firstPress = event->pos();
1350   d->previousPos = event->pos();
1351   d->previousPosTimestamp.start();
1352   d->singlePress = true; // Initially assume a single press
1353 //  d->dragStartSlide = d->getTarget();
1354   d->pixelDistanceMoved = 0;
1355
1356   emit inputReceived();
1357 }
1358
1359 void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
1360 {
1361   int sideWidth = (d->buffer.width() - slideSize().width()) /2;
1362
1363   if (d->singlePress)
1364   {
1365     if (event->x() < sideWidth )
1366     {
1367       showPrevious();
1368     } else if ( event->x() > sideWidth + slideSize().width() ) {
1369       showNext();
1370     } else {
1371       emit itemActivated(d->getTarget());
1372     }
1373
1374     event->accept();
1375   }
1376
1377   emit inputReceived();
1378 }
1379
1380
1381 void PictureFlow::paintEvent(QPaintEvent* event)
1382 {
1383   Q_UNUSED(event);
1384   QPainter painter(this);
1385   painter.setRenderHint(QPainter::Antialiasing, false);
1386   painter.drawImage(QPoint(0,0), d->buffer);
1387 }
1388
1389 void PictureFlow::resizeEvent(QResizeEvent* event)
1390 {
1391   d->resize(width(), height());
1392   QWidget::resizeEvent(event);
1393 }
1394
1395 void PictureFlow::timerEvent(QTimerEvent* event)
1396 {
1397   if(event->timerId() == d->animateTimer.timerId())
1398   {
1399 //    QTime now = QTime::currentTime();
1400     d->updateAnimation();
1401 //    d->animateTimer.start(qMax(0, 30-now.elapsed() ), this);
1402   }
1403   else
1404     QWidget::timerEvent(event);
1405 }