2 PictureFlow - animated image show widget
3 http://pictureflow.googlecode.com
5 Copyright (C) 2009 Ariya Hidayat (ariya@kde.org)
6 Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
7 Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 #include "pictureflow.hpp"
31 #if QT_VERSION < 0x040300
32 #error PictureFlow widgets need Qt 4.3 or later
35 #include <QApplication>
46 // for fixed-point arithmetic, we need minimum 32-bit long
47 // long long (64-bit) might be useful for multiplication and division
49 #define PFREAL_SHIFT 10
50 #define PFREAL_ONE (1 << PFREAL_SHIFT)
52 #define IANGLE_MAX 1024
53 #define IANGLE_MASK 1023
55 inline PFreal fmul(PFreal a, PFreal b)
57 return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
60 inline PFreal fdiv(PFreal num, PFreal den)
62 long long p = (long long)(num) << (PFREAL_SHIFT * 2);
63 long long q = p / (long long)den;
64 long long r = q >> PFREAL_SHIFT;
69 inline PFreal fsin(int iangle)
71 // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
72 static const PFreal tab[] = {
73 3, 103, 202, 300, 394, 485, 571, 652,
74 726, 793, 853, 904, 947, 980, 1004, 1019,
75 1023, 1018, 1003, 978, 944, 901, 849, 789,
76 721, 647, 566, 479, 388, 294, 196, 97,
77 -4, -104, -203, -301, -395, -486, -572, -653,
78 -727, -794, -854, -905, -948, -981, -1005, -1020,
79 -1024, -1019, -1004, -979, -945, -902, -850, -790,
80 -722, -648, -567, -480, -389, -295, -197, -98,
86 iangle &= IANGLE_MASK;
88 int i = (iangle >> 4);
90 PFreal q = tab[(i+1)];
92 return p + g *(iangle - i*16) / 16;
95 inline PFreal fcos(int iangle)
97 return fsin(iangle + (IANGLE_MAX >> 2));
100 /* ----------------------------------------------------------
102 PictureFlowState stores the state of all slides, i.e. all the necessary
103 information to be able to render them.
105 PictureFlowAnimator is responsible to move the slides during the
106 transition between slides, to achieve the effect similar to Cover Flow,
107 by changing the state.
109 PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
110 the actual 3-d renderer. It should render all slides given the state
111 (an instance of PictureFlowState).
113 Instances of all the above three classes are stored in
116 ------------------------------------------------------- */
126 class PictureFlowState
135 QRgb backgroundColor;
138 PictureFlow::ReflectionEffect reflectionEffect;
139 QVector<QImage*> slideImages;
146 SlideInfo centerSlide;
147 QVector<SlideInfo> leftSlides;
148 QVector<SlideInfo> rightSlides;
152 class PictureFlowAnimator
155 PictureFlowAnimator();
156 PictureFlowState* state;
158 void start(int slide);
159 void stop(int slide);
168 class PictureFlowAbstractRenderer
171 PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {}
172 virtual ~PictureFlowAbstractRenderer() {}
174 PictureFlowState* state;
178 virtual void init() = 0;
179 virtual void paint() = 0;
182 class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
185 PictureFlowSoftwareRenderer();
186 ~PictureFlowSoftwareRenderer();
189 virtual void paint();
196 QVector<PFreal> rays;
197 QImage* blankSurface;
198 QCache<int, QImage> surfaceCache;
199 QHash<int, QImage*> imageHash;
203 QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
204 QImage* surface(int slideIndex);
207 // ------------- PictureFlowState ---------------------------------------
209 PictureFlowState::PictureFlowState():
210 backgroundColor(0), slideWidth(150), slideHeight(200),
211 reflectionEffect(PictureFlow::BlurredReflection), centerIndex(0)
215 PictureFlowState::~PictureFlowState()
217 for (int i = 0; i < (int)slideImages.count(); i++)
218 delete slideImages[i];
221 // readjust the settings, call this when slide dimension is changed
222 void PictureFlowState::reposition()
224 angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted
226 offsetX = slideWidth / 2 * (PFREAL_ONE - fcos(angle));
227 offsetY = slideWidth / 2 * fsin(angle);
228 offsetX += slideWidth * PFREAL_ONE;
229 offsetY += slideWidth * PFREAL_ONE / 4;
233 // adjust slides so that they are in "steady state" position
234 void PictureFlowState::reset()
236 centerSlide.angle = 0;
239 centerSlide.slideIndex = centerIndex;
240 centerSlide.blend = 256;
242 leftSlides.resize(6);
243 for (int i = 0; i < (int)leftSlides.count(); i++) {
244 SlideInfo& si = leftSlides[i];
246 si.cx = -(offsetX + spacing * i * PFREAL_ONE);
248 si.slideIndex = centerIndex - 1 - i;
250 if (i == (int)leftSlides.count() - 2)
252 if (i == (int)leftSlides.count() - 1)
256 rightSlides.resize(6);
257 for (int i = 0; i < (int)rightSlides.count(); i++) {
258 SlideInfo& si = rightSlides[i];
260 si.cx = offsetX + spacing * i * PFREAL_ONE;
262 si.slideIndex = centerIndex + 1 + i;
264 if (i == (int)rightSlides.count() - 2)
266 if (i == (int)rightSlides.count() - 1)
271 // ------------- PictureFlowAnimator ---------------------------------------
273 PictureFlowAnimator::PictureFlowAnimator():
274 state(0), target(0), step(0), frame(0)
278 void PictureFlowAnimator::start(int slide)
281 if (!animateTimer.isActive() && state) {
282 step = (target < state->centerSlide.slideIndex) ? -1 : 1;
283 animateTimer.start(30);
287 void PictureFlowAnimator::stop(int slide)
295 void PictureFlowAnimator::update()
297 if (!animateTimer.isActive())
304 int speed = 16384 / 4;
307 // deaccelerate when approaching the target
308 const int max = 2 * 65536;
311 fi -= (target << 16);
316 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
317 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
320 frame += speed * step;
322 int index = frame >> 16;
323 int pos = frame & 0xffff;
324 int neg = 65536 - pos;
325 int tick = (step < 0) ? neg : pos;
326 PFreal ftick = (tick * PFREAL_ONE) >> 16;
331 if (state->centerIndex != index) {
332 state->centerIndex = index;
334 state->centerSlide.slideIndex = state->centerIndex;
335 for (int i = 0; i < (int)state->leftSlides.count(); i++)
336 state->leftSlides[i].slideIndex = state->centerIndex - 1 - i;
337 for (int i = 0; i < (int)state->rightSlides.count(); i++)
338 state->rightSlides[i].slideIndex = state->centerIndex + 1 + i;
341 state->centerSlide.angle = (step * tick * state->angle) >> 16;
342 state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
343 state->centerSlide.cy = fmul(state->offsetY, ftick);
345 if (state->centerIndex == target) {
351 for (int i = 0; i < (int)state->leftSlides.count(); i++) {
352 SlideInfo& si = state->leftSlides[i];
353 si.angle = state->angle;
354 si.cx = -(state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick);
355 si.cy = state->offsetY;
358 for (int i = 0; i < (int)state->rightSlides.count(); i++) {
359 SlideInfo& si = state->rightSlides[i];
360 si.angle = -state->angle;
361 si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick;
362 si.cy = state->offsetY;
366 PFreal ftick = (neg * PFREAL_ONE) >> 16;
367 state->rightSlides[0].angle = -(neg * state->angle) >> 16;
368 state->rightSlides[0].cx = fmul(state->offsetX, ftick);
369 state->rightSlides[0].cy = fmul(state->offsetY, ftick);
371 PFreal ftick = (pos * PFREAL_ONE) >> 16;
372 state->leftSlides[0].angle = (pos * state->angle) >> 16;
373 state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
374 state->leftSlides[0].cy = fmul(state->offsetY, ftick);
377 // must change direction ?
378 if (target < index) if (step > 0)
380 if (target > index) if (step < 0)
383 // the first and last slide must fade in/fade out
384 int nleft = state->leftSlides.count();
385 int nright = state->rightSlides.count();
386 int fade = pos / 256;
388 for (int index = 0; index < nleft; index++) {
390 if (index == nleft - 1)
391 blend = (step > 0) ? 0 : 128 - fade / 2;
392 if (index == nleft - 2)
393 blend = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
394 if (index == nleft - 3)
395 blend = (step > 0) ? 256 - fade / 2 : 256;
396 state->leftSlides[index].blend = blend;
398 for (int index = 0; index < nright; index++) {
399 int blend = (index < nright - 2) ? 256 : 128;
400 if (index == nright - 1)
401 blend = (step > 0) ? fade / 2 : 0;
402 if (index == nright - 2)
403 blend = (step > 0) ? 128 + fade / 2 : fade / 2;
404 if (index == nright - 3)
405 blend = (step > 0) ? 256 : 128 + fade / 2;
406 state->rightSlides[index].blend = blend;
411 // ------------- PictureFlowSoftwareRenderer ---------------------------------------
413 PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
414 PictureFlowAbstractRenderer(), size(0, 0), bgcolor(0), effect(-1), blankSurface(0)
418 PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
420 surfaceCache.clear();
425 void PictureFlowSoftwareRenderer::paint()
430 if (widget->size() != size)
433 if (state->backgroundColor != bgcolor) {
434 bgcolor = state->backgroundColor;
435 surfaceCache.clear();
438 if ((int)(state->reflectionEffect) != effect) {
439 effect = (int)state->reflectionEffect;
440 surfaceCache.clear();
446 QPainter painter(widget);
447 painter.drawImage(QPoint(0, 0), buffer);
450 void PictureFlowSoftwareRenderer::init()
455 surfaceCache.clear();
458 size = widget->size();
459 int ww = size.width();
460 int wh = size.height();
461 int w = (ww + 1) / 2;
462 int h = (wh + 1) / 2;
464 buffer = QImage(ww, wh, QImage::Format_RGB32);
465 buffer.fill(bgcolor);
468 for (int i = 0; i < w; i++) {
469 PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2 * h);
477 // TODO: optimize this with lookup tables
478 static QRgb blendColor(QRgb c1, QRgb c2, int blend)
480 int r = qRed(c1) * blend / 256 + qRed(c2) * (256 - blend) / 256;
481 int g = qGreen(c1) * blend / 256 + qGreen(c2) * (256 - blend) / 256;
482 int b = qBlue(c1) * blend / 256 + qBlue(c2) * (256 - blend) / 256;
483 return qRgb(r, g, b);
487 static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
488 PictureFlow::ReflectionEffect reflectionEffect)
490 Qt::TransformationMode mode = Qt::SmoothTransformation;
491 QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
493 // slightly larger, to accomodate for the reflection
497 // offscreen buffer: black is sweet
498 QImage* result = new QImage(hs, w, QImage::Format_RGB32);
499 result->fill(bgcolor);
501 // transpose the image, this is to speed-up the rendering
502 // because we process one column at a time
503 // (and much better and faster to work row-wise, i.e in one scanline)
504 for (int x = 0; x < w; x++)
505 for (int y = 0; y < h; y++)
506 result->setPixel(hofs + y, x, img.pixel(x, y));
508 if (reflectionEffect != PictureFlow::NoReflection) {
509 // create the reflection
510 int ht = hs - h - hofs;
512 for (int x = 0; x < w; x++)
513 for (int y = 0; y < ht; y++) {
514 QRgb color = img.pixel(x, img.height() - y - 1);
515 result->setPixel(h + hofs + y, x, blendColor(color, bgcolor, 128*(hte - y) / hte));
518 if (reflectionEffect == PictureFlow::BlurredReflection) {
519 // blur the reflection everything first
520 // Based on exponential blur algorithm by Jani Huhtanen
521 QRect rect(hs / 2, 0, hs / 2, w);
522 rect &= result->rect();
525 int r2 = rect.bottom();
526 int c1 = rect.left();
527 int c2 = rect.right();
529 int bpl = result->bytesPerLine();
533 // how many times blur is applied?
534 // for low-end system, limit this to only 1 loop
535 for (int loop = 0; loop < 2; loop++) {
536 for (int col = c1; col <= c2; col++) {
537 p = result->scanLine(r1) + col * 4;
538 for (int i = 0; i < 3; i++)
542 for (int j = r1; j < r2; j++, p += bpl)
543 for (int i = 0; i < 3; i++)
544 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
547 for (int row = r1; row <= r2; row++) {
548 p = result->scanLine(row) + c1 * 4;
549 for (int i = 0; i < 3; i++)
553 for (int j = c1; j < c2; j++, p += 4)
554 for (int i = 0; i < 3; i++)
555 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
558 for (int col = c1; col <= c2; col++) {
559 p = result->scanLine(r2) + col * 4;
560 for (int i = 0; i < 3; i++)
564 for (int j = r1; j < r2; j++, p -= bpl)
565 for (int i = 0; i < 3; i++)
566 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
569 for (int row = r1; row <= r2; row++) {
570 p = result->scanLine(row) + c2 * 4;
571 for (int i = 0; i < 3; i++)
575 for (int j = c1; j < c2; j++, p -= 4)
576 for (int i = 0; i < 3; i++)
577 p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4;
581 // overdraw to leave only the reflection blurred (but not the actual image)
582 for (int x = 0; x < w; x++)
583 for (int y = 0; y < h; y++)
584 result->setPixel(hofs + y, x, img.pixel(x, y));
591 QImage* PictureFlowSoftwareRenderer::surface(int slideIndex)
597 if (slideIndex >= (int)state->slideImages.count())
600 int key = slideIndex;
602 QImage* img = state->slideImages.at(slideIndex);
603 bool empty = img ? img->isNull() : true;
605 surfaceCache.remove(key);
606 imageHash.remove(slideIndex);
608 int sw = state->slideWidth;
609 int sh = state->slideHeight;
611 QImage img = QImage(sw, sh, QImage::Format_RGB32);
613 QPainter painter(&img);
614 QPoint p1(sw*4 / 10, 0);
615 QPoint p2(sw*6 / 10, sh);
616 QLinearGradient linearGrad(p1, p2);
617 linearGrad.setColorAt(0, Qt::black);
618 linearGrad.setColorAt(1, Qt::white);
619 painter.setBrush(linearGrad);
620 painter.fillRect(0, 0, sw, sh, QBrush(linearGrad));
622 painter.setPen(QPen(QColor(64, 64, 64), 4));
623 painter.setBrush(QBrush());
624 painter.drawRect(2, 2, sw - 3, sh - 3);
627 blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect);
632 bool exist = imageHash.contains(slideIndex);
634 if (img == imageHash.find(slideIndex).value())
635 if (surfaceCache.contains(key))
636 return surfaceCache[key];
638 QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect);
639 surfaceCache.insert(key, sr);
640 imageHash.insert(slideIndex, img);
645 // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
646 // col1 and col2 limit the column for rendering.
647 QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
649 int blend = slide.blend;
653 QImage* src = surface(slide.slideIndex);
657 QRect rect(0, 0, 0, 0);
659 int sw = src->height();
660 int sh = src->width();
661 int h = buffer.height();
662 int w = buffer.width();
670 col1 = (col1 >= 0) ? col1 : 0;
671 col2 = (col2 >= 0) ? col2 : w - 1;
672 col1 = qMin(col1, w - 1);
673 col2 = qMin(col2, w - 1);
676 int distance = h * 100 / zoom;
677 PFreal sdx = fcos(slide.angle);
678 PFreal sdy = fsin(slide.angle);
679 PFreal xs = slide.cx - state->slideWidth * sdx / 2;
680 PFreal ys = slide.cy - state->slideWidth * sdy / 2;
681 PFreal dist = distance * PFREAL_ONE;
683 int xi = qMax((PFreal)0, (w * PFREAL_ONE / 2) + fdiv(xs * h, dist + ys) >> PFREAL_SHIFT);
689 for (int x = qMax(xi, col1); x <= col2; x++) {
693 fk = fk - fdiv(sdx, sdy);
694 hity = -fdiv((rays[x] * distance - slide.cx + slide.cy * sdx / sdy), fk);
697 dist = distance * PFREAL_ONE + hity;
701 PFreal hitx = fmul(dist, rays[x]);
702 PFreal hitdist = fdiv(hitx - slide.cx, sdx);
704 int column = sw / 2 + (hitdist >> PFREAL_SHIFT);
717 QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
718 QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
719 QRgb pixelstep = pixel2 - pixel1;
721 int center = (sh / 2);
723 int p1 = center * PFREAL_ONE - dy / 2;
724 int p2 = center * PFREAL_ONE + dy / 2;
726 const QRgb *ptr = (const QRgb*)(src->scanLine(column));
728 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
729 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
730 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
739 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
740 QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
741 QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
742 *pixel1 = blendColor(c1, bgcolor, blend);
743 *pixel2 = blendColor(c2, bgcolor, blend);
754 rect.setBottom(h - 1);
758 void PictureFlowSoftwareRenderer::renderSlides()
760 int nleft = state->leftSlides.count();
761 int nright = state->rightSlides.count();
763 QRect r = renderSlide(state->centerSlide);
767 for (int index = 0; index < nleft; index++) {
768 QRect rs = renderSlide(state->leftSlides[index], 0, c1 - 1);
772 for (int index = 0; index < nright; index++) {
773 QRect rs = renderSlide(state->rightSlides[index], c2 + 1, buffer.width());
779 // Render the slides. Updates only the offscreen buffer.
780 void PictureFlowSoftwareRenderer::render()
782 buffer.fill(state->backgroundColor);
787 // -----------------------------------------
789 class PictureFlowPrivate
792 PictureFlowState* state;
793 PictureFlowAnimator* animator;
794 PictureFlowAbstractRenderer* renderer;
799 PictureFlow::PictureFlow(QWidget* parent): QWidget(parent)
801 d = new PictureFlowPrivate;
803 d->state = new PictureFlowState;
805 d->state->reposition();
807 d->renderer = new PictureFlowSoftwareRenderer;
808 d->renderer->state = d->state;
809 d->renderer->widget = this;
812 d->animator = new PictureFlowAnimator;
813 d->animator->state = d->state;
814 QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
816 QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
818 setAttribute(Qt::WA_StaticContents, true);
819 setAttribute(Qt::WA_OpaquePaintEvent, true);
820 setAttribute(Qt::WA_NoSystemBackground, true);
823 PictureFlow::~PictureFlow()
831 int PictureFlow::slideCount() const
833 return d->state->slideImages.count();
836 QColor PictureFlow::backgroundColor() const
838 return QColor(d->state->backgroundColor);
841 void PictureFlow::setBackgroundColor(const QColor& c)
843 d->state->backgroundColor = c.rgb();
847 QSize PictureFlow::slideSize() const
849 return QSize(d->state->slideWidth, d->state->slideHeight);
852 void PictureFlow::setSlideSize(QSize size)
854 d->state->slideWidth = size.width();
855 d->state->slideHeight = size.height();
856 d->state->reposition();
860 PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
862 return d->state->reflectionEffect;
865 void PictureFlow::setReflectionEffect(ReflectionEffect effect)
867 d->state->reflectionEffect = effect;
871 QImage PictureFlow::slide(int index) const
874 if ((index >= 0) && (index < slideCount()))
875 i = d->state->slideImages[index];
876 return i ? QImage(*i) : QImage();
879 void PictureFlow::addSlide(const QImage& image)
881 int c = d->state->slideImages.count();
882 d->state->slideImages.resize(c + 1);
883 d->state->slideImages[c] = new QImage(image);
887 void PictureFlow::addSlide(const QPixmap& pixmap)
889 addSlide(pixmap.toImage());
892 void PictureFlow::removeSlide(int index)
894 int c = d->state->slideImages.count();
895 if (index >= 0 && index < c) {
896 d->state->slideImages.remove(index);
901 void PictureFlow::setSlide(int index, const QImage& image)
903 if ((index >= 0) && (index < slideCount())) {
904 QImage* i = image.isNull() ? 0 : new QImage(image);
905 delete d->state->slideImages[index];
906 d->state->slideImages[index] = i;
911 void PictureFlow::setSlide(int index, const QPixmap& pixmap)
913 setSlide(index, pixmap.toImage());
916 int PictureFlow::centerIndex() const
918 return d->state->centerIndex;
921 void PictureFlow::setCenterIndex(int index)
923 index = qMin(index, slideCount() - 1);
924 index = qMax(index, 0);
925 d->state->centerIndex = index;
927 d->animator->stop(index);
931 void PictureFlow::clear()
933 int c = d->state->slideImages.count();
934 for (int i = 0; i < c; i++)
935 delete d->state->slideImages[i];
936 d->state->slideImages.resize(0);
942 void PictureFlow::render()
944 d->renderer->dirty = true;
948 void PictureFlow::triggerRender()
950 d->triggerTimer.setSingleShot(true);
951 d->triggerTimer.start(0);
954 void PictureFlow::showPrevious()
956 int step = d->animator->step;
957 int center = d->state->centerIndex;
960 d->animator->start(center);
964 d->animator->start(center - 1);
967 d->animator->target = qMax(0, center - 2);
970 void PictureFlow::showNext()
972 int step = d->animator->step;
973 int center = d->state->centerIndex;
976 d->animator->start(center);
979 if (center < slideCount() - 1)
980 d->animator->start(center + 1);
983 d->animator->target = qMin(center + 2, slideCount() - 1);
986 void PictureFlow::showSlide(int index)
988 index = qMax(index, 0);
989 index = qMin(slideCount() - 1, index);
990 if (index == d->state->centerSlide.slideIndex)
993 d->animator->start(index);
996 void PictureFlow::keyPressEvent(QKeyEvent* event)
998 if (event->key() == Qt::Key_Left) {
999 if (event->modifiers() == Qt::ControlModifier)
1000 showSlide(centerIndex() - 10);
1007 if (event->key() == Qt::Key_Right) {
1008 if (event->modifiers() == Qt::ControlModifier)
1009 showSlide(centerIndex() + 10);
1019 void PictureFlow::mousePressEvent(QMouseEvent* event)
1021 if (event->x() > width() / 2)
1027 void PictureFlow::paintEvent(QPaintEvent* event)
1030 d->renderer->paint();
1033 void PictureFlow::resizeEvent(QResizeEvent* event)
1036 QWidget::resizeEvent(event);
1039 void PictureFlow::updateAnimation()
1041 int old_center = d->state->centerIndex;
1042 d->animator->update();
1044 if (d->state->centerIndex != old_center)
1045 emit centerIndexChanged(d->state->centerIndex);