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

source code of qtsvg/examples/svg/embedded/fluidlauncher/pictureflow.cpp