1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the ActiveQt framework of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
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
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.
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."
39 ****************************************************************************/
42 ORIGINAL COPYRIGHT HEADER
43 PictureFlow - animated image show widget
44 http://pictureflow.googlecode.com
46 Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
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:
55 The above copyright notice and this permission notice shall be included in
56 all copies or substantial portions of the Software.
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
67 #include "pictureflow.h"
69 #include <QBasicTimer>
86 static const int captionFontSize =
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
98 // for fixed-point arithmetic, we need minimum 32-bit long
99 // long long (64-bit) might be useful for multiplication and division
102 typedef unsigned short QRgb565;
104 #define RGB565_RED_MASK 0xF800
105 #define RGB565_GREEN_MASK 0x07E0
106 #define RGB565_BLUE_MASK 0x001F
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)
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)
117 inline PFreal fmul(PFreal a, PFreal b)
119 return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
122 inline PFreal fdiv(PFreal num, PFreal den)
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;
131 inline float fixedToFloat(PFreal val)
133 return ((float)val) / (float)PFREAL_ONE;
136 inline PFreal floatToFixed(float val)
138 return (PFreal)(val*PFREAL_ONE);
141 #define IANGLE_MAX 1024
142 #define IANGLE_MASK 1023
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
276 // this is the program the generate the above table
282 #define M_PI 3.14159265358979323846
285 #define PFREAL_ONE 1024
286 #define IANGLE_MAX 1024
288 int main(int, char**)
290 FILE*f = fopen("table.c","wt");
291 fprintf(f,"PFreal sinTable[] = {\n");
292 for(int i = 0; i < 128; i++)
294 for(int j = 0; j < 8; j++)
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)));
311 inline PFreal fsin(int iangle)
314 iangle += IANGLE_MAX;
315 return sinTable[iangle & IANGLE_MASK];
318 inline PFreal fcos(int iangle)
320 // quarter phase shift
321 return fsin(iangle + (IANGLE_MAX >> 2));
332 class PictureFlowPrivate
335 PictureFlowPrivate(PictureFlow* widget);
337 int slideCount() const;
338 void setSlideCount(int count);
340 QSize slideSize() const;
341 void setSlideSize(QSize size);
343 int zoomFactor() const;
344 void setZoomFactor(int z);
346 QImage slide(int index) const;
347 void setSlide(int index, const QImage& image);
349 int currentSlide() const;
350 void setCurrentSlide(int index);
352 int getTarget() const;
356 void showSlide(int index);
358 void resize(int w, int h);
361 void startAnimation();
362 void updateAnimation();
364 void clearSurfaceCache();
367 QBasicTimer animateTimer;
370 int singlePressThreshold;
373 QTime previousPosTimestamp;
374 int pixelDistanceMoved;
375 int pixelsToMovePerSlide;
377 QVector<QString> captions;
386 QVector<QImage> slideImages;
388 SlideInfo centerSlide;
389 QVector<SlideInfo> leftSlides;
390 QVector<SlideInfo> rightSlides;
392 QVector<PFreal> rays;
399 QCache<int, QImage> surfaceCache;
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();
414 PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w)
429 triggerTimer.setSingleShot(true);
430 triggerTimer.setInterval(0);
431 QObject::connect(&triggerTimer, SIGNAL(timeout()), widget, SLOT(render()));
437 int PictureFlowPrivate::slideCount() const
439 return slideImages.count();
442 void PictureFlowPrivate::setSlideCount(int count)
444 slideImages.resize(count);
445 captions.resize(count);
446 surfaceCache.clear();
451 QSize PictureFlowPrivate::slideSize() const
453 return QSize(slideWidth, slideHeight);
456 void PictureFlowPrivate::setSlideSize(QSize size)
458 slideWidth = size.width();
459 slideHeight = size.height();
460 recalc(buffer.width(), buffer.height());
464 int PictureFlowPrivate::zoomFactor() const
469 void PictureFlowPrivate::setZoomFactor(int z)
475 recalc(buffer.width(), buffer.height());
479 QImage PictureFlowPrivate::slide(int index) const
481 return slideImages[index];
484 void PictureFlowPrivate::setSlide(int index, const QImage& image)
486 if((index >= 0) && (index < slideImages.count()))
488 slideImages[index] = image;
489 surfaceCache.remove(index);
494 int PictureFlowPrivate::getTarget() const
499 int PictureFlowPrivate::currentSlide() const
504 void PictureFlowPrivate::setCurrentSlide(int index)
507 centerIndex = qBound(index, 0, slideImages.count()-1);
508 target = centerIndex;
509 slideFrame = index << 16;
514 void PictureFlowPrivate::showPrevious()
526 target = qMax(0, centerIndex - 2);
530 void PictureFlowPrivate::showNext()
534 if(centerIndex < slideImages.count()-1)
542 target = qMin(centerIndex + 2, slideImages.count()-1);
546 void PictureFlowPrivate::showSlide(int index)
548 index = qMax(index, 0);
549 index = qMin(slideImages.count()-1, index);
550 if(index == centerSlide.slideIndex)
557 void PictureFlowPrivate::resize(int w, int h)
565 // adjust slides so that they are in "steady state" position
566 void PictureFlowPrivate::resetSlides()
568 centerSlide.angle = 0;
571 centerSlide.slideIndex = centerIndex;
574 leftSlides.resize(3);
575 for(int i = 0; i < leftSlides.count(); i++)
577 SlideInfo& si = leftSlides[i];
579 si.cx = -(offsetX + spacing*i*PFREAL_ONE);
581 si.slideIndex = centerIndex-1-i;
582 //qDebug() << "Left[" << i << "] x=" << fixedToFloat(si.cx) << ", y=" << fixedToFloat(si.cy) ;
586 rightSlides.resize(3);
587 for(int i = 0; i < rightSlides.count(); i++)
589 SlideInfo& si = rightSlides[i];
591 si.cx = offsetX + spacing*i*PFREAL_ONE;
593 si.slideIndex = centerIndex+1+i;
594 //qDebug() << "Right[" << i << "] x=" << fixedToFloat(si.cx) << ", y=" << fixedToFloat(si.cy) ;
598 #define BILINEAR_STRETCH_HOR 4
599 #define BILINEAR_STRETCH_VER 4
601 static QImage prepareSurface(QImage img, int w, int h)
603 Qt::TransformationMode mode = Qt::SmoothTransformation;
604 img = img.scaled(w, h, Qt::IgnoreAspectRatio, mode);
606 // slightly larger, to accommodate for the reflection
610 // offscreen buffer: black is sweet
611 QImage result(hs, w, QImage::Format_RGB16);
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));
621 // create the reflection
622 int ht = hs - h - hofs;
624 for(int x = 0; x < w; x++)
625 for(int y = 0; y < ht; y++)
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));
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);
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)
652 if(slideIndex >= slideImages.count())
655 if(surfaceCache.contains(slideIndex))
656 return surfaceCache[slideIndex];
658 QImage img = widget->slide(slideIndex);
661 if(blankSurface.isNull())
663 blankSurface = QImage(slideWidth, slideHeight, QImage::Format_RGB16);
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));
674 painter.setPen(QPen(QColor(64,64,64), 4));
675 painter.setBrush(QBrush());
676 painter.drawRect(2, 2, slideWidth-3, slideHeight-3);
678 blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight);
680 return &blankSurface;
683 surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight)));
684 return surfaceCache[slideIndex];
688 // Schedules rendering the slides. Call this function to avoid immediate
689 // render and thus cause less flicker.
690 void PictureFlowPrivate::triggerRender()
692 triggerTimer.start();
695 // Render the slides. Updates only the offscreen buffer.
696 void PictureFlowPrivate::render()
700 int nleft = leftSlides.count();
701 int nright = rightSlides.count();
703 QRect r = renderSlide(centerSlide);
709 // no animation, boring plain rendering
710 for(int index = 0; index < nleft-1; index++)
712 int alpha = (index < nleft-2) ? 256 : 128;
713 QRect rs = renderSlide(leftSlides[index], alpha, 0, c1-1);
717 for(int index = 0; index < nright-1; index++)
719 int alpha = (index < nright-2) ? 256 : 128;
720 QRect rs = renderSlide(rightSlides[index], alpha, c2+1, buffer.width());
726 painter.begin(&buffer);
728 QFont font("Arial", captionFontSize);
730 painter.setFont(font);
731 painter.setPen(Qt::white);
732 //painter.setPen(QColor(255,255,255,127));
734 if (!captions.isEmpty())
735 painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/4),
736 Qt::AlignCenter, captions[centerIndex]);
743 // the first and last slide must fade in/fade out
744 for(int index = 0; index < nleft; index++)
748 alpha = (step > 0) ? 0 : 128-fade/2;
750 alpha = (step > 0) ? 128-fade/2 : 256-fade/2;
752 alpha = (step > 0) ? 256-fade/2 : 256;
753 QRect rs = renderSlide(leftSlides[index], alpha, 0, c1-1);
757 alpha = (step > 0) ? 256-fade/2 : 256;
759 for(int index = 0; index < nright; index++)
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());
774 painter.begin(&buffer);
776 QFont font("Arial", captionFontSize);
778 painter.setFont(font);
780 int leftTextIndex = (step>0) ? centerIndex : centerIndex-1;
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]);
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]);
795 static inline uint BYTE_MUL_RGB16(uint x, uint a) {
797 uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
798 t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
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;
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,
815 QImage* src = surface(slide.slideIndex);
819 QRect rect(0, 0, 0, 0);
821 #ifdef PICTUREFLOW_BILINEAR_FILTER
822 int sw = src->height() / BILINEAR_STRETCH_HOR;
823 int sh = src->width() / BILINEAR_STRETCH_VER;
825 int sw = src->height();
826 int sh = src->width();
828 int h = buffer.height();
829 int w = buffer.width();
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);
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;
850 int xi = qMax((PFreal)0, ((w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys)) >> PFREAL_SHIFT);
856 for(int x = qMax(xi, col1); x <= col2; x++)
862 fk = fk - fdiv(sdx,sdy);
863 hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk);
866 dist = distance*PFREAL_ONE + hity;
870 PFreal hitx = fmul(dist, rays[x]);
871 PFreal hitdist = fdiv(hitx - slide.cx, sdx);
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)
878 int column = sw/2 + (hitdist >> PFREAL_SHIFT);
892 QRgb565* pixel1 = (QRgb565*)(buffer.scanLine(y1)) + x;
893 QRgb565* pixel2 = (QRgb565*)(buffer.scanLine(y2)) + x;
894 int pixelstep = pixel2 - pixel1;
896 #ifdef PICTUREFLOW_BILINEAR_FILTER
897 int center = (sh*BILINEAR_STRETCH_VER/2);
898 int dy = dist*BILINEAR_STRETCH_VER / h;
903 int p1 = center*PFREAL_ONE - dy/2;
904 int p2 = center*PFREAL_ONE + dy/2;
906 const QRgb565 *ptr = (const QRgb565*)(src->scanLine(column));
908 while((y1 >= 0) && (y2 < h) && (p1 >= 0))
910 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
911 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
920 while((y1 >= 0) && (y2 < h) && (p1 >= 0))
922 QRgb565 c1 = ptr[p1 >> PFREAL_SHIFT];
923 QRgb565 c2 = ptr[p2 >> PFREAL_SHIFT];
925 *pixel1 = BYTE_MUL_RGB16(c1, alpha);
926 *pixel2 = BYTE_MUL_RGB16(c2, alpha);
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);
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)
958 buffer = QImage(ww, wh, QImage::Format_RGB16);
963 for(int i = 0; i < w; i++)
965 PFreal gg = (PFREAL_HALF + i * PFREAL_ONE) / (2*h);
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;
974 pixelsToMovePerSlide = ww / 3;
975 // qDebug() << "pixelsToMovePerSlide now set to " << pixelsToMovePerSlide;
977 itilt = 80 * IANGLE_MAX / 360; // approx. 80 degrees tilted
979 offsetY = slideWidth/2 * fsin(itilt);
980 offsetY += slideWidth * PFREAL_ONE / 4;
982 // offsetX = slideWidth/2 * (PFREAL_ONE-fcos(itilt));
983 // offsetX += slideWidth * PFREAL_ONE;
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);
992 spacing = slideWidth/5;
994 surfaceCache.clear();
995 blankSurface = QImage();
998 void PictureFlowPrivate::startAnimation()
1000 if(!animateTimer.isActive())
1002 step = (target < centerSlide.slideIndex) ? -1 : 1;
1003 animateTimer.start(30, widget);
1007 // Updates the animation effect. Call this periodically from a timer.
1008 void PictureFlowPrivate::updateAnimation()
1010 if(!animateTimer.isActive())
1017 // deaccelerate when approaching the target
1020 const int max = 2 * 65536;
1022 int fi = slideFrame;
1023 fi -= (target << 16);
1028 int ia = IANGLE_MAX * (fi-max/2) / (max*2);
1029 speed = 512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE;
1032 slideFrame += speed*step;
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;
1040 // the leftmost and rightmost slide must fade away
1045 if(centerIndex != index)
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;
1056 centerSlide.angle = (step * tick * itilt) >> 16;
1057 centerSlide.cx = -step * fmul(offsetX, ftick);
1058 centerSlide.cy = fmul(offsetY, ftick);
1060 if(centerIndex == target)
1063 animateTimer.stop();
1070 for(int i = 0; i < leftSlides.count(); i++)
1072 SlideInfo& si = leftSlides[i];
1074 si.cx = -(offsetX + spacing*i*PFREAL_ONE + step*spacing*ftick);
1078 for(int i = 0; i < rightSlides.count(); i++)
1080 SlideInfo& si = rightSlides[i];
1082 si.cx = offsetX + spacing*i*PFREAL_ONE - step*spacing*ftick;
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);
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);
1101 // must change direction ?
1102 if(target < index) if(step > 0)
1104 if(target > index) if(step < 0)
1111 void PictureFlowPrivate::clearSurfaceCache()
1113 surfaceCache.clear();
1116 // -----------------------------------------
1118 PictureFlow::PictureFlow(QWidget* parent): QWidget(parent)
1120 d = new PictureFlowPrivate(this);
1122 setAttribute(Qt::WA_StaticContents, true);
1123 setAttribute(Qt::WA_OpaquePaintEvent, true);
1124 setAttribute(Qt::WA_NoSystemBackground, true);
1127 if (QScreen::instance()->pixelFormat() != QImage::Format_Invalid)
1128 setAttribute(Qt::WA_PaintOnScreen, true);
1132 PictureFlow::~PictureFlow()
1137 int PictureFlow::slideCount() const
1139 return d->slideCount();
1142 void PictureFlow::setSlideCount(int count)
1144 d->setSlideCount(count);
1147 QSize PictureFlow::slideSize() const
1149 return d->slideSize();
1152 void PictureFlow::setSlideSize(QSize size)
1154 d->setSlideSize(size);
1157 int PictureFlow::zoomFactor() const
1159 return d->zoomFactor();
1162 void PictureFlow::setZoomFactor(int z)
1164 d->setZoomFactor(z);
1167 QImage PictureFlow::slide(int index) const
1169 return d->slide(index);
1172 void PictureFlow::setSlide(int index, const QImage& image)
1174 d->setSlide(index, image);
1177 void PictureFlow::setSlide(int index, const QPixmap& pixmap)
1179 d->setSlide(index, pixmap.toImage());
1182 void PictureFlow::setSlideCaption(int index, QString caption)
1184 d->captions[index] = caption;
1188 int PictureFlow::currentSlide() const
1190 return d->currentSlide();
1193 void PictureFlow::setCurrentSlide(int index)
1195 d->setCurrentSlide(index);
1198 void PictureFlow::clear()
1200 d->setSlideCount(0);
1203 void PictureFlow::clearCaches()
1205 d->clearSurfaceCache();
1208 void PictureFlow::render()
1214 void PictureFlow::showPrevious()
1219 void PictureFlow::showNext()
1224 void PictureFlow::showSlide(int index)
1226 d->showSlide(index);
1229 void PictureFlow::keyPressEvent(QKeyEvent* event)
1231 if(event->key() == Qt::Key_Left)
1233 if(event->modifiers() == Qt::ControlModifier)
1234 showSlide(currentSlide()-10);
1241 if(event->key() == Qt::Key_Right)
1243 if(event->modifiers() == Qt::ControlModifier)
1244 showSlide(currentSlide()+10);
1251 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Select) {
1252 emit itemActivated(d->getTarget());
1260 #define SPEED_LOWER_THRESHOLD 10
1261 #define SPEED_UPPER_LIMIT 40
1263 void PictureFlow::mouseMoveEvent(QMouseEvent* event)
1265 int distanceMovedSinceLastEvent = event->pos().x() - d->previousPos.x();
1267 // Check to see if we need to switch from single press mode to a drag mode
1270 // Increment the distance moved for this event
1271 d->pixelDistanceMoved += distanceMovedSinceLastEvent;
1273 // Check against threshold
1274 if (qAbs(d->pixelDistanceMoved) > d->singlePressThreshold)
1276 d->singlePress = false;
1277 // qDebug() << "DRAG MODE ON";
1281 if (!d->singlePress)
1284 // Calculate velocity in a 10th of a window width per second
1285 if (d->previousPosTimestamp.elapsed() == 0)
1286 speed = SPEED_LOWER_THRESHOLD;
1289 speed = ((qAbs(event->pos().x()-d->previousPos.x())*1000) / d->previousPosTimestamp.elapsed())
1290 / (d->buffer.width() / 10);
1292 if (speed < SPEED_LOWER_THRESHOLD)
1293 speed = SPEED_LOWER_THRESHOLD;
1294 else if (speed > SPEED_UPPER_LIMIT)
1295 speed = SPEED_UPPER_LIMIT;
1297 speed = SPEED_LOWER_THRESHOLD + (speed / 3);
1298 // qDebug() << "ACCELERATION ENABLED Speed = " << speed << ", Distance = " << distanceMovedSinceLastEvent;
1302 // qDebug() << "Speed = " << speed;
1304 // int incr = ((event->pos().x() - d->previousPos.x())/10) * speed;
1306 // qDebug() << "Incremented by " << incr;
1308 int incr = (distanceMovedSinceLastEvent * speed);
1310 //qDebug() << "(distanceMovedSinceLastEvent * speed) = " << incr;
1312 if (incr > d->pixelsToMovePerSlide*2) {
1313 incr = d->pixelsToMovePerSlide*2;
1314 //qDebug() << "Limiting incr to " << incr;
1318 d->pixelDistanceMoved += (distanceMovedSinceLastEvent * speed);
1319 // qDebug() << "distance: " << d->pixelDistanceMoved;
1323 slideInc = d->pixelDistanceMoved / (d->pixelsToMovePerSlide * 10);
1325 if (slideInc != 0) {
1326 int targetSlide = d->getTarget() - slideInc;
1327 showSlide(targetSlide);
1328 // qDebug() << "TargetSlide = " << targetSlide;
1330 //qDebug() << "Decrementing pixelDistanceMoved by " << (d->pixelsToMovePerSlide *10) * slideInc;
1332 d->pixelDistanceMoved -= (d->pixelsToMovePerSlide *10) * slideInc;
1335 if ( (targetSlide <= 0) || (targetSlide >= d->slideCount()-1) )
1336 d->pixelDistanceMoved = 0;
1341 d->previousPos = event->pos();
1342 d->previousPosTimestamp.restart();
1344 emit inputReceived();
1347 void PictureFlow::mousePressEvent(QMouseEvent* event)
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;
1356 emit inputReceived();
1359 void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
1361 int sideWidth = (d->buffer.width() - slideSize().width()) /2;
1365 if (event->x() < sideWidth )
1368 } else if ( event->x() > sideWidth + slideSize().width() ) {
1371 emit itemActivated(d->getTarget());
1377 emit inputReceived();
1381 void PictureFlow::paintEvent(QPaintEvent* event)
1384 QPainter painter(this);
1385 painter.setRenderHint(QPainter::Antialiasing, false);
1386 painter.drawImage(QPoint(0,0), d->buffer);
1389 void PictureFlow::resizeEvent(QResizeEvent* event)
1391 d->resize(width(), height());
1392 QWidget::resizeEvent(event);
1395 void PictureFlow::timerEvent(QTimerEvent* event)
1397 if(event->timerId() == d->animateTimer.timerId())
1399 // QTime now = QTime::currentTime();
1400 d->updateAnimation();
1401 // d->animateTimer.start(qMax(0, 30-now.elapsed() ), this);
1404 QWidget::timerEvent(event);