8f427b2 by axis at 2009-04-24 1
/****************************************************************************
2
**
da19e7f by Jason McDonald at 2010-01-06 3
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
04e3b30 by Jason McDonald at 2009-09-09 4
** All rights reserved.
1047445 by Jason McDonald at 2009-08-12 5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
309db73 by Jason McDonald at 2009-08-31 7
** This file is part of the ActiveQt framework of the Qt Toolkit.
3058cd5 by Jason McDonald at 2009-08-11 8
**
309db73 by Jason McDonald at 2009-08-31 9
** $QT_BEGIN_LICENSE:BSD$
10
** You may use this file under the terms of the BSD license as follows:
3058cd5 by Jason McDonald at 2009-08-11 11
**
309db73 by Jason McDonald at 2009-08-31 12
** "Redistribution and use in source and binary forms, with or without
13
** modification, are permitted provided that the following conditions are
14
** met:
15
**   * Redistributions of source code must retain the above copyright
16
**     notice, this list of conditions and the following disclaimer.
17
**   * Redistributions in binary form must reproduce the above copyright
18
**     notice, this list of conditions and the following disclaimer in
19
**     the documentation and/or other materials provided with the
20
**     distribution.
21
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22
**     the names of its contributors may be used to endorse or promote
23
**     products derived from this software without specific prior written
24
**     permission.
3058cd5 by Jason McDonald at 2009-08-11 25
**
309db73 by Jason McDonald at 2009-08-31 26
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1047445 by Jason McDonald at 2009-08-12 37
** $QT_END_LICENSE$
38
**
8f427b2 by axis at 2009-04-24 39
****************************************************************************/
40
1047445 by Jason McDonald at 2009-08-12 41
/*
8f427b2 by axis at 2009-04-24 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] = {
1047445 by Jason McDonald at 2009-08-12 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
8f427b2 by axis at 2009-04-24 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];
1047445 by Jason McDonald at 2009-08-12 316
}
8f427b2 by axis at 2009-04-24 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()));
1047445 by Jason McDonald at 2009-08-12 432
8f427b2 by axis at 2009-04-24 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();
1047445 by Jason McDonald at 2009-08-12 491
  }
8f427b2 by axis at 2009-04-24 492
}
493
494
int PictureFlowPrivate::getTarget() const
495
{
496
  return target;
497
}
498
499
int PictureFlowPrivate::currentSlide() const
500
{
501
  return centerIndex;
1047445 by Jason McDonald at 2009-08-12 502
}
8f427b2 by axis at 2009-04-24 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
5bdb85e by Jerome Pasion at 2010-07-29 606
  // slightly larger, to accommodate for the reflection
8f427b2 by axis at 2009-04-24 607
  int hs = h * 2;
608
  int hofs = h / 3;
609
610
  // offscreen buffer: black is sweet
1047445 by Jason McDonald at 2009-08-12 611
  QImage result(hs, w, QImage::Format_RGB16);
8f427b2 by axis at 2009-04-24 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
1047445 by Jason McDonald at 2009-08-12 688
// Schedules rendering the slides. Call this function to avoid immediate
8f427b2 by axis at 2009-04-24 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();
1047445 by Jason McDonald at 2009-08-12 716
    }
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 758
    }
8f427b2 by axis at 2009-04-24 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
1047445 by Jason McDonald at 2009-08-12 819
  QRect rect(0, 0, 0, 0);
820
8f427b2 by axis at 2009-04-24 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
1047445 by Jason McDonald at 2009-08-12 885
    rect.setRight(x);
8f427b2 by axis at 2009-04-24 886
    if(!flag)
887
      rect.setLeft(x);
1047445 by Jason McDonald at 2009-08-12 888
    flag = true;
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 918
      }
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 944
     }
945
   }
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 1023
    fi -= (target << 16);
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 1038
  PFreal ftick = (tick * PFREAL_ONE) >> 16;
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 1068
  }
8f427b2 by axis at 2009-04-24 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);
1047445 by Jason McDonald at 2009-08-12 1099
  }
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 1135
}
8f427b2 by axis at 2009-04-24 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);
1047445 by Jason McDonald at 2009-08-12 1235
    else
8f427b2 by axis at 2009-04-24 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);
1047445 by Jason McDonald at 2009-08-12 1291
8f427b2 by axis at 2009-04-24 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;
1047445 by Jason McDonald at 2009-08-12 1305
8f427b2 by axis at 2009-04-24 1306
//    qDebug() << "Incremented by " << incr;
1307
1308
    int incr = (distanceMovedSinceLastEvent * speed);
1047445 by Jason McDonald at 2009-08-12 1309
8f427b2 by axis at 2009-04-24 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
}