pictureflow.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. /*
  2. Copyright (c) 2010 Embedded Systems and Pervasive Laboratory, Federal
  3. University of Campina Grande, Brazil, Angelo Perkusich, Mirko Perkusich,
  4. Taciana Rached
  5. Permission is hereby granted, free of charge, to any person obtaining a
  6. copy of this software and associated documentation files (the
  7. "Software"), to deal in the Software without restriction, including
  8. without limitation the rights to use, copy, modify, merge, publish,
  9. distribute, sublicense, and/or sell copies of the Software, and to
  10. permit persons to whom the Software is furnished to do so, subject to
  11. the following conditions:
  12. The above copyright notice and this permission notice shall be included
  13. in all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  15. OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  17. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  18. CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  19. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  20. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. */
  22. /*
  23. PictureFlow - animated image show widget
  24. http://pictureflow.googlecode.com
  25. Copyright (C) 2008 Ariya Hidayat (ariya@kde.org)
  26. Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
  27. Permission is hereby granted, free of charge, to any person obtaining a copy
  28. of this software and associated documentation files (the "Software"), to deal
  29. in the Software without restriction, including without limitation the rights
  30. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  31. copies of the Software, and to permit persons to whom the Software is
  32. furnished to do so, subject to the following conditions:
  33. The above copyright notice and this permission notice shall be included in
  34. all copies or substantial portions of the Software.
  35. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  36. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  37. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  38. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  39. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  40. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  41. THE SOFTWARE.
  42. */
  43. #include "pictureflow.h"
  44. // detect Qt version
  45. #if QT_VERSION >= 0x040000
  46. #define PICTUREFLOW_QT4
  47. #elif QT_VERSION >= 0x030000
  48. #define PICTUREFLOW_QT3
  49. #elif QT_VERSION >= 235
  50. #define PICTUREFLOW_QT2
  51. #else
  52. #error PictureFlow widgets need Qt 2, Qt 3 or Qt 4
  53. #endif
  54. #ifdef PICTUREFLOW_QT4
  55. #include <QApplication>
  56. #include <QCache>
  57. #include <QHash>
  58. #include <QImage>
  59. #include <QKeyEvent>
  60. #include <QPainter>
  61. #include <QPixmap>
  62. #include <QTimer>
  63. #include <QVector>
  64. #include <QWidget>
  65. #endif
  66. #ifdef PICTUREFLOW_QT3
  67. #include <qapplication.h>
  68. #include <qcache.h>
  69. #include <qimage.h>
  70. #include <qpainter.h>
  71. #include <qpixmap.h>
  72. #include <qdatetime.h>
  73. #include <qtimer.h>
  74. #include <qvaluevector.h>
  75. #include <qwidget.h>
  76. #define qMax(x,y) ((x) > (y)) ? (x) : (y)
  77. #define qMin(x,y) ((x) < (y)) ? (x) : (y)
  78. #define QVector QValueVector
  79. #define toImage convertToImage
  80. #define contains find
  81. #define modifiers state
  82. #define ControlModifier ControlButton
  83. #endif
  84. #ifdef PICTUREFLOW_QT2
  85. #include <qapplication.h>
  86. #include <qarray.h>
  87. #include <qcache.h>
  88. #include <qimage.h>
  89. #include <qintdict.h>
  90. #include <qpainter.h>
  91. #include <qpixmap.h>
  92. #include <qdatetime.h>
  93. #include <qtimer.h>
  94. #include <qwidget.h>
  95. #define qMax(x,y) ((x) > (y)) ? (x) : (y)
  96. #define qMin(x,y) ((x) < (y)) ? (x) : (y)
  97. #define QVector QArray
  98. #define toImage convertToImage
  99. #define contains find
  100. #define modifiers state
  101. #define ControlModifier ControlButton
  102. #define flush flushX
  103. #endif
  104. static const int captionFontSize =
  105. #ifdef Q_WS_S60
  106. 8;
  107. #else
  108. 18;
  109. #endif
  110. // for fixed-point arithmetic, we need minimum 32-bit long
  111. // long long (64-bit) might be useful for multiplication and division
  112. typedef long PFreal;
  113. #define PFREAL_SHIFT 10
  114. #define PFREAL_ONE (1 << PFREAL_SHIFT)
  115. #define IANGLE_MAX 1024
  116. #define IANGLE_MASK 1023
  117. inline PFreal fmul(PFreal a, PFreal b)
  118. {
  119. return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT;
  120. }
  121. inline PFreal fdiv(PFreal num, PFreal den)
  122. {
  123. long long p = (long long)(num) << (PFREAL_SHIFT*2);
  124. long long q = p / (long long)den;
  125. long long r = q >> PFREAL_SHIFT;
  126. return r;
  127. }
  128. inline PFreal fsin(int iangle)
  129. {
  130. // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed!
  131. static const PFreal tab[] = {
  132. 3, 103, 202, 300, 394, 485, 571, 652,
  133. 726, 793, 853, 904, 947, 980, 1004, 1019,
  134. 1023, 1018, 1003, 978, 944, 901, 849, 789,
  135. 721, 647, 566, 479, 388, 294, 196, 97,
  136. -4, -104, -203, -301, -395, -486, -572, -653,
  137. -727, -794, -854, -905, -948, -981, -1005, -1020,
  138. -1024, -1019, -1004, -979, -945, -902, -850, -790,
  139. -722, -648, -567, -480, -389, -295, -197, -98,
  140. 3
  141. };
  142. while(iangle < 0)
  143. iangle += IANGLE_MAX;
  144. iangle &= IANGLE_MASK;
  145. int i = (iangle >> 4);
  146. PFreal p = tab[i];
  147. PFreal q = tab[(i+1)];
  148. PFreal g = (q - p);
  149. return p + g * (iangle-i*16)/16;
  150. }
  151. inline PFreal fcos(int iangle)
  152. {
  153. return fsin(iangle + (IANGLE_MAX >> 2));
  154. }
  155. /* ----------------------------------------------------------
  156. PictureFlowState stores the state of all slides, i.e. all the necessary
  157. information to be able to render them.
  158. PictureFlowAnimator is responsible to move the slides during the
  159. transition between slides, to achieve the effect similar to Cover Flow,
  160. by changing the state.
  161. PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is
  162. the actual 3-d renderer. It should render all slides given the state
  163. (an instance of PictureFlowState).
  164. Instances of all the above three classes are stored in
  165. PictureFlowPrivate.
  166. ------------------------------------------------------- */
  167. struct SlideInfo
  168. {
  169. int slideIndex;
  170. int angle;
  171. PFreal cx;
  172. PFreal cy;
  173. int blend;
  174. };
  175. class PictureFlowState
  176. {
  177. public:
  178. PictureFlowState();
  179. ~PictureFlowState();
  180. void reposition();
  181. void reset();
  182. QRgb backgroundColor;
  183. int slideWidth;
  184. int slideHeight;
  185. PictureFlow::ReflectionEffect reflectionEffect;
  186. QVector<QImage*> slideImages;
  187. int angle;
  188. int spacing;
  189. PFreal offsetX;
  190. PFreal offsetY;
  191. SlideInfo centerSlide;
  192. QVector<SlideInfo> leftSlides;
  193. QVector<SlideInfo> rightSlides;
  194. int centerIndex;
  195. };
  196. class PictureFlowAnimator
  197. {
  198. public:
  199. PictureFlowAnimator();
  200. PictureFlowState* state;
  201. void start(int slide);
  202. void stop(int slide);
  203. void update();
  204. int target;
  205. int step;
  206. int frame;
  207. QTimer animateTimer;
  208. };
  209. class PictureFlowAbstractRenderer
  210. {
  211. public:
  212. PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {}
  213. virtual ~PictureFlowAbstractRenderer() {}
  214. PictureFlowState* state;
  215. bool dirty;
  216. QWidget* widget;
  217. QVector<QString> captions;
  218. virtual void init() = 0;
  219. virtual void paint() = 0;
  220. };
  221. class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer
  222. {
  223. public:
  224. PictureFlowSoftwareRenderer();
  225. ~PictureFlowSoftwareRenderer();
  226. virtual void init();
  227. virtual void paint();
  228. private:
  229. QSize size;
  230. QRgb bgcolor;
  231. int effect;
  232. QImage buffer;
  233. QVector<PFreal> rays;
  234. QImage* blankSurface;
  235. #ifdef PICTUREFLOW_QT4
  236. QCache<int,QImage> surfaceCache;
  237. QHash<int,QImage*> imageHash;
  238. #endif
  239. #ifdef PICTUREFLOW_QT3
  240. QCache<QImage> surfaceCache;
  241. QMap<int,QImage*> imageHash;
  242. #endif
  243. #ifdef PICTUREFLOW_QT2
  244. QCache<QImage> surfaceCache;
  245. QIntDict<QImage> imageHash;
  246. #endif
  247. void render();
  248. void renderSlides();
  249. QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1);
  250. QImage* surface(int slideIndex);
  251. };
  252. // ------------- PictureFlowState ---------------------------------------
  253. PictureFlowState::PictureFlowState():
  254. backgroundColor(0), slideWidth(150), slideHeight(200),
  255. reflectionEffect(PictureFlow::BlurredReflection), centerIndex(0)
  256. {
  257. }
  258. PictureFlowState::~PictureFlowState()
  259. {
  260. for(int i = 0; i < (int)slideImages.count(); i++)
  261. delete slideImages[i];
  262. }
  263. // readjust the settings, call this when slide dimension is changed
  264. void PictureFlowState::reposition()
  265. {
  266. angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted
  267. offsetX = slideWidth/2 * (PFREAL_ONE-fcos(angle));
  268. offsetY = slideWidth/2 * fsin(angle);
  269. offsetX += slideWidth * PFREAL_ONE;
  270. offsetY += slideWidth * PFREAL_ONE / 4;
  271. spacing = 40;
  272. }
  273. // adjust slides so that they are in "steady state" position
  274. void PictureFlowState::reset()
  275. {
  276. centerSlide.angle = 0;
  277. centerSlide.cx = 0;
  278. centerSlide.cy = 0;
  279. centerSlide.slideIndex = centerIndex;
  280. centerSlide.blend = 256;
  281. leftSlides.resize(6);
  282. for(int i = 0; i < (int)leftSlides.count(); i++)
  283. {
  284. SlideInfo& si = leftSlides[i];
  285. si.angle = angle;
  286. si.cx = -(offsetX + spacing*i*PFREAL_ONE);
  287. si.cy = offsetY;
  288. si.slideIndex = centerIndex-1-i;
  289. si.blend = 256;
  290. if(i == (int)leftSlides.count()-2)
  291. si.blend = 128;
  292. if(i == (int)leftSlides.count()-1)
  293. si.blend = 0;
  294. }
  295. rightSlides.resize(6);
  296. for(int i = 0; i < (int)rightSlides.count(); i++)
  297. {
  298. SlideInfo& si = rightSlides[i];
  299. si.angle = -angle;
  300. si.cx = offsetX + spacing*i*PFREAL_ONE;
  301. si.cy = offsetY;
  302. si.slideIndex = centerIndex+1+i;
  303. si.blend = 256;
  304. if(i == (int)rightSlides.count()-2)
  305. si.blend = 128;
  306. if(i == (int)rightSlides.count()-1)
  307. si.blend = 0;
  308. }
  309. }
  310. // ------------- PictureFlowAnimator ---------------------------------------
  311. PictureFlowAnimator::PictureFlowAnimator():
  312. state(0), target(0), step(0), frame(0)
  313. {
  314. }
  315. void PictureFlowAnimator::start(int slide)
  316. {
  317. target = slide;
  318. if(!animateTimer.isActive() && state)
  319. {
  320. step = (target < state->centerSlide.slideIndex) ? -1 : 1;
  321. animateTimer.start(30);
  322. }
  323. }
  324. void PictureFlowAnimator::stop(int slide)
  325. {
  326. step = 0;
  327. target = slide;
  328. frame = slide << 16;
  329. animateTimer.stop();
  330. }
  331. void PictureFlowAnimator::update()
  332. {
  333. if(!animateTimer.isActive())
  334. return;
  335. if(step == 0)
  336. return;
  337. if(!state)
  338. return;
  339. int speed = 16384/4;
  340. #if 1
  341. // deaccelerate when approaching the target
  342. const int max = 2 * 65536;
  343. int fi = frame;
  344. fi -= (target << 16);
  345. if(fi < 0)
  346. fi = -fi;
  347. fi = qMin(fi, max);
  348. int ia = IANGLE_MAX * (fi-max/2) / (max*2);
  349. speed = 18*512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE;
  350. #endif
  351. frame += speed*step;
  352. int index = frame >> 16;
  353. int pos = frame & 0xffff;
  354. int neg = 65536 - pos;
  355. int tick = (step < 0) ? neg : pos;
  356. PFreal ftick = (tick * PFREAL_ONE) >> 16;
  357. if(step < 0)
  358. index++;
  359. if(state->centerIndex != index)
  360. {
  361. state->centerIndex = index;
  362. frame = index << 16;
  363. state->centerSlide.slideIndex = state->centerIndex;
  364. for(int i = 0; i < (int)state->leftSlides.count(); i++)
  365. state->leftSlides[i].slideIndex = state->centerIndex-1-i;
  366. for(int i = 0; i < (int)state->rightSlides.count(); i++)
  367. state->rightSlides[i].slideIndex = state->centerIndex+1+i;
  368. }
  369. state->centerSlide.angle = (step * tick * state->angle) >> 16;
  370. state->centerSlide.cx = -step * fmul(state->offsetX, ftick);
  371. state->centerSlide.cy = fmul(state->offsetY, ftick);
  372. if(state->centerIndex == target)
  373. {
  374. stop(target);
  375. state->reset();
  376. return;
  377. }
  378. for(int i = 0; i < (int)state->leftSlides.count(); i++)
  379. {
  380. SlideInfo& si = state->leftSlides[i];
  381. si.angle = state->angle;
  382. si.cx = -(state->offsetX + state->spacing*i*PFREAL_ONE + step*state->spacing*ftick);
  383. si.cy = state->offsetY;
  384. }
  385. for(int i = 0; i < (int)state->rightSlides.count(); i++)
  386. {
  387. SlideInfo& si = state->rightSlides[i];
  388. si.angle = -state->angle;
  389. si.cx = state->offsetX + state->spacing*i*PFREAL_ONE - step*state->spacing*ftick;
  390. si.cy = state->offsetY;
  391. }
  392. if(step > 0)
  393. {
  394. PFreal ftick = (neg * PFREAL_ONE) >> 16;
  395. state->rightSlides[0].angle = -(neg * state->angle) >> 16;
  396. state->rightSlides[0].cx = fmul(state->offsetX, ftick);
  397. state->rightSlides[0].cy = fmul(state->offsetY, ftick);
  398. }
  399. else
  400. {
  401. PFreal ftick = (pos * PFREAL_ONE) >> 16;
  402. state->leftSlides[0].angle = (pos * state->angle) >> 16;
  403. state->leftSlides[0].cx = -fmul(state->offsetX, ftick);
  404. state->leftSlides[0].cy = fmul(state->offsetY, ftick);
  405. }
  406. // must change direction ?
  407. if(target < index) if(step > 0)
  408. step = -1;
  409. if(target > index) if(step < 0)
  410. step = 1;
  411. // the first and last slide must fade in/fade out
  412. int nleft = state->leftSlides.count();
  413. int nright = state->rightSlides.count();
  414. int fade = pos / 256;
  415. for(int index = 0; index < nleft; index++)
  416. {
  417. int blend = 256;
  418. if(index == nleft-1)
  419. blend = (step > 0) ? 0 : 128-fade/2;
  420. if(index == nleft-2)
  421. blend = (step > 0) ? 128-fade/2 : 256-fade/2;
  422. if(index == nleft-3)
  423. blend = (step > 0) ? 256-fade/2 : 256;
  424. state->leftSlides[index].blend = blend;
  425. }
  426. for(int index = 0; index < nright; index++)
  427. {
  428. int blend = (index < nright-2) ? 256 : 128;
  429. if(index == nright-1)
  430. blend = (step > 0) ? fade/2 : 0;
  431. if(index == nright-2)
  432. blend = (step > 0) ? 128+fade/2 : fade/2;
  433. if(index == nright-3)
  434. blend = (step > 0) ? 256 : 128+fade/2;
  435. state->rightSlides[index].blend = blend;
  436. }
  437. }
  438. // ------------- PictureFlowSoftwareRenderer ---------------------------------------
  439. PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer():
  440. PictureFlowAbstractRenderer(), size(0,0), bgcolor(0), effect(-1), blankSurface(0)
  441. {
  442. #ifdef PICTUREFLOW_QT3
  443. surfaceCache.setAutoDelete(true);
  444. #endif
  445. }
  446. PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer()
  447. {
  448. surfaceCache.clear();
  449. buffer = QImage();
  450. delete blankSurface;
  451. }
  452. void PictureFlowSoftwareRenderer::paint()
  453. {
  454. if(!widget)
  455. return;
  456. if(widget->size() != size)
  457. init();
  458. if(state->backgroundColor != bgcolor)
  459. {
  460. bgcolor = state->backgroundColor;
  461. surfaceCache.clear();
  462. }
  463. if((int)(state->reflectionEffect) != effect)
  464. {
  465. effect = (int)state->reflectionEffect;
  466. surfaceCache.clear();
  467. }
  468. if(dirty)
  469. render();
  470. QPainter painter(widget);
  471. painter.drawImage(QPoint(0,0), buffer);
  472. }
  473. void PictureFlowSoftwareRenderer::init()
  474. {
  475. if(!widget)
  476. return;
  477. surfaceCache.clear();
  478. blankSurface = 0;
  479. size = widget->size();
  480. int ww = size.width();
  481. int wh = size.height();
  482. int w = (ww+1)/2;
  483. int h = (wh+1)/2;
  484. #ifdef PICTUREFLOW_QT4
  485. buffer = QImage(ww, wh, QImage::Format_RGB32);
  486. #endif
  487. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  488. buffer.create(ww, wh, 32);
  489. #endif
  490. buffer.fill(bgcolor);
  491. rays.resize(w*2);
  492. for(int i = 0; i < w; i++)
  493. {
  494. PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2*h);
  495. rays[w-i-1] = -gg;
  496. rays[w+i] = gg;
  497. }
  498. dirty = true;
  499. }
  500. // TODO: optimize this with lookup tables
  501. static QRgb blendColor(QRgb c1, QRgb c2, int blend)
  502. {
  503. int r = qRed(c1) * blend/256 + qRed(c2)*(256-blend)/256;
  504. int g = qGreen(c1) * blend/256 + qGreen(c2)*(256-blend)/256;
  505. int b = qBlue(c1) * blend/256 + qBlue(c2)*(256-blend)/256;
  506. return qRgb(r, g, b);
  507. }
  508. static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor,
  509. PictureFlow::ReflectionEffect reflectionEffect)
  510. {
  511. #ifdef PICTUREFLOW_QT4
  512. Qt::TransformationMode mode = Qt::SmoothTransformation;
  513. QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode);
  514. #endif
  515. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  516. QImage img = slideImage->smoothScale(w, h);
  517. #endif
  518. // slightly larger, to accomodate for the reflection
  519. //int hs = h * 2;
  520. int hs = h * 1.5;
  521. int hofs = h / 3;
  522. // offscreen buffer: black is sweet
  523. #ifdef PICTUREFLOW_QT4
  524. QImage* result = new QImage(hs, w, QImage::Format_RGB32);
  525. #endif
  526. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  527. QImage* result = new QImage;
  528. result->create(hs, w, 32);
  529. #endif
  530. result->fill(bgcolor);
  531. // transpose the image, this is to speed-up the rendering
  532. // because we process one column at a time
  533. // (and much better and faster to work row-wise, i.e in one scanline)
  534. for(int x = 0; x < w; x++)
  535. for(int y = 0; y < h; y++)
  536. result->setPixel(hofs + y, x, img.pixel(x, y));
  537. if(reflectionEffect != PictureFlow::NoReflection)
  538. {
  539. // create the reflection
  540. int ht = hs - h - hofs;
  541. int hte = ht;
  542. for(int x = 0; x < w; x++)
  543. for(int y = 0; y < ht; y++)
  544. {
  545. QRgb color = img.pixel(x, img.height()-y-1);
  546. result->setPixel(h+hofs+y, x, blendColor(color,bgcolor,128*(hte-y)/hte));
  547. }
  548. if(reflectionEffect == PictureFlow::BlurredReflection)
  549. {
  550. // blur the reflection everything first
  551. // Based on exponential blur algorithm by Jani Huhtanen
  552. QRect rect(hs/2, 0, hs/2, w);
  553. rect &= result->rect();
  554. int r1 = rect.top();
  555. int r2 = rect.bottom();
  556. int c1 = rect.left();
  557. int c2 = rect.right();
  558. int bpl = result->bytesPerLine();
  559. int rgba[4];
  560. unsigned char* p;
  561. // how many times blur is applied?
  562. // for low-end system, limit this to only 1 loop
  563. for(int loop = 0; loop < 2; loop++)
  564. {
  565. for(int col = c1; col <= c2; col++)
  566. {
  567. p = result->scanLine(r1) + col*4;
  568. for(int i = 0; i < 3; i++)
  569. rgba[i] = p[i] << 4;
  570. p += bpl;
  571. for(int j = r1; j < r2; j++, p += bpl)
  572. for(int i = 0; i < 3; i++)
  573. p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
  574. }
  575. for(int row = r1; row <= r2; row++)
  576. {
  577. p = result->scanLine(row) + c1*4;
  578. for(int i = 0; i < 3; i++)
  579. rgba[i] = p[i] << 4;
  580. p += 4;
  581. for(int j = c1; j < c2; j++, p+=4)
  582. for(int i = 0; i < 3; i++)
  583. p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
  584. }
  585. for(int col = c1; col <= c2; col++)
  586. {
  587. p = result->scanLine(r2) + col*4;
  588. for(int i = 0; i < 3; i++)
  589. rgba[i] = p[i] << 4;
  590. p -= bpl;
  591. for(int j = r1; j < r2; j++, p -= bpl)
  592. for(int i = 0; i < 3; i++)
  593. p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
  594. }
  595. for(int row = r1; row <= r2; row++)
  596. {
  597. p = result->scanLine(row) + c2*4;
  598. for(int i = 0; i < 3; i++)
  599. rgba[i] = p[i] << 4;
  600. p -= 4;
  601. for(int j = c1; j < c2; j++, p-=4)
  602. for(int i = 0; i < 3; i++)
  603. p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4;
  604. }
  605. }
  606. // overdraw to leave only the reflection blurred (but not the actual image)
  607. for(int x = 0; x < w; x++)
  608. for(int y = 0; y < h; y++)
  609. result->setPixel(hofs + y, x, img.pixel(x, y));
  610. }
  611. }
  612. return result;
  613. }
  614. QImage* PictureFlowSoftwareRenderer::surface(int slideIndex)
  615. {
  616. if(!state)
  617. return 0;
  618. if(slideIndex < 0)
  619. return 0;
  620. if(slideIndex >= (int)state->slideImages.count())
  621. return 0;
  622. #ifdef PICTUREFLOW_QT4
  623. int key = slideIndex;
  624. #endif
  625. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  626. QString key = QString::number(slideIndex);
  627. #endif
  628. QImage* img = state->slideImages.at(slideIndex);
  629. bool empty = img ? img->isNull() : true;
  630. if(empty)
  631. {
  632. surfaceCache.remove(key);
  633. imageHash.remove(slideIndex);
  634. if(!blankSurface)
  635. {
  636. int sw = state->slideWidth;
  637. int sh = state->slideHeight;
  638. #ifdef PICTUREFLOW_QT4
  639. QImage img = QImage(sw, sh, QImage::Format_RGB32);
  640. QPainter painter(&img);
  641. QPoint p1(sw*4/10, 0);
  642. QPoint p2(sw*6/10, sh);
  643. QLinearGradient linearGrad(p1, p2);
  644. linearGrad.setColorAt(0, Qt::black);
  645. linearGrad.setColorAt(1, Qt::white);
  646. painter.setBrush(linearGrad);
  647. painter.fillRect(0, 0, sw, sh, QBrush(linearGrad));
  648. painter.setPen(QPen(QColor(64,64,64), 4));
  649. painter.setBrush(QBrush());
  650. painter.drawRect(2, 2, sw-3, sh-3);
  651. painter.end();
  652. #endif
  653. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  654. QPixmap pixmap(sw, sh, 32);
  655. QPainter painter(&pixmap);
  656. painter.fillRect(pixmap.rect(), QColor(192,192,192));
  657. painter.fillRect(5, 5, sw-10, sh-10, QColor(64,64,64));
  658. painter.end();
  659. QImage img = pixmap.convertToImage();
  660. #endif
  661. blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect);
  662. }
  663. return blankSurface;
  664. }
  665. #ifdef PICTUREFLOW_QT4
  666. bool exist = imageHash.contains(slideIndex);
  667. if(exist)
  668. if(img == imageHash.find(slideIndex).value())
  669. #endif
  670. #ifdef PICTUREFLOW_QT3
  671. bool exist = imageHash.find(slideIndex) != imageHash.end();
  672. if(exist)
  673. if(img == imageHash.find(slideIndex).data())
  674. #endif
  675. #ifdef PICTUREFLOW_QT2
  676. if(img == imageHash[slideIndex])
  677. #endif
  678. if(surfaceCache.contains(key))
  679. return surfaceCache[key];
  680. QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect);
  681. surfaceCache.insert(key, sr);
  682. imageHash.insert(slideIndex, img);
  683. return sr;
  684. }
  685. // Renders a slide to offscreen buffer. Returns a rect of the rendered area.
  686. // col1 and col2 limit the column for rendering.
  687. QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2)
  688. {
  689. int blend = slide.blend;
  690. if(!blend)
  691. return QRect();
  692. QImage* src = surface(slide.slideIndex);
  693. if(!src)
  694. return QRect();
  695. QRect rect(0, 0, 0, 0);
  696. int sw = src->height();
  697. int sh = src->width();
  698. int h = buffer.height();
  699. int w = buffer.width();
  700. if(col1 > col2)
  701. {
  702. int c = col2;
  703. col2 = col1;
  704. col1 = c;
  705. }
  706. col1 = (col1 >= 0) ? col1 : 0;
  707. col2 = (col2 >= 0) ? col2 : w-1;
  708. col1 = qMin(col1, w-1);
  709. col2 = qMin(col2, w-1);
  710. int zoom = 100;
  711. int distance = h * 100 / zoom;
  712. PFreal sdx = fcos(slide.angle);
  713. PFreal sdy = fsin(slide.angle);
  714. PFreal xs = slide.cx - state->slideWidth * sdx/2;
  715. PFreal ys = slide.cy - state->slideWidth * sdy/2;
  716. PFreal dist = distance * PFREAL_ONE;
  717. int xi = qMax((PFreal)0, ((w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys)) >> PFREAL_SHIFT);
  718. if(xi >= w)
  719. return rect;
  720. bool flag = false;
  721. rect.setLeft(xi);
  722. for(int x = qMax(xi, col1); x <= col2; x++)
  723. {
  724. PFreal hity = 0;
  725. PFreal fk = rays[x];
  726. if(sdy)
  727. {
  728. fk = fk - fdiv(sdx,sdy);
  729. hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk);
  730. }
  731. dist = distance*PFREAL_ONE + hity;
  732. if(dist < 0)
  733. continue;
  734. PFreal hitx = fmul(dist, rays[x]);
  735. PFreal hitdist = fdiv(hitx - slide.cx, sdx);
  736. int column = sw/2 + (hitdist >> PFREAL_SHIFT);
  737. if(column >= sw)
  738. break;
  739. if(column < 0)
  740. continue;
  741. rect.setRight(x);
  742. if(!flag)
  743. rect.setLeft(x);
  744. flag = true;
  745. int y1 = h/2;
  746. int y2 = y1+ 1;
  747. QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x;
  748. QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x;
  749. QRgb pixelstep = pixel2 - pixel1;
  750. int center = (sh/2);
  751. int dy = dist / h;
  752. int p1 = center*PFREAL_ONE - dy/2;
  753. int p2 = center*PFREAL_ONE + dy/2;
  754. const QRgb *ptr = (const QRgb*)(src->scanLine(column));
  755. if(blend == 256)
  756. while((y1 >= 0) && (y2 < h) && (p1 >= 0))
  757. {
  758. *pixel1 = ptr[p1 >> PFREAL_SHIFT];
  759. *pixel2 = ptr[p2 >> PFREAL_SHIFT];
  760. p1 -= dy;
  761. p2 += dy;
  762. y1--;
  763. y2++;
  764. pixel1 -= pixelstep;
  765. pixel2 += pixelstep;
  766. }
  767. else
  768. while((y1 >= 0) && (y2 < h) && (p1 >= 0))
  769. {
  770. QRgb c1 = ptr[p1 >> PFREAL_SHIFT];
  771. QRgb c2 = ptr[p2 >> PFREAL_SHIFT];
  772. *pixel1 = blendColor(c1, bgcolor, blend);
  773. *pixel2 = blendColor(c2, bgcolor, blend);
  774. p1 -= dy;
  775. p2 += dy;
  776. y1--;
  777. y2++;
  778. pixel1 -= pixelstep;
  779. pixel2 += pixelstep;
  780. }
  781. }
  782. rect.setTop(0);
  783. rect.setBottom(h-1);
  784. QFont font("Arial", captionFontSize);
  785. font.setBold(true);
  786. QPainter painter;
  787. painter.begin(&buffer);
  788. painter.setFont(font);
  789. painter.setPen(Qt::gray);
  790. if (!captions.isEmpty())
  791. painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - QSize(200,200).height())/4),
  792. Qt::AlignCenter, captions[state->centerIndex]);
  793. return rect;
  794. }
  795. void PictureFlowSoftwareRenderer::renderSlides()
  796. {
  797. int nleft = state->leftSlides.count();
  798. int nright = state->rightSlides.count();
  799. QRect r = renderSlide(state->centerSlide);
  800. int c1 = r.left();
  801. int c2 = r.right();
  802. for(int index = 0; index < nleft; index++)
  803. {
  804. QRect rs = renderSlide(state->leftSlides[index], 0, c1-1);
  805. if(!rs.isEmpty())
  806. c1 = rs.left();
  807. }
  808. for(int index = 0; index < nright; index++)
  809. {
  810. QRect rs = renderSlide(state->rightSlides[index], c2+1, buffer.width());
  811. if(!rs.isEmpty())
  812. c2 = rs.right();
  813. }
  814. }
  815. // Render the slides. Updates only the offscreen buffer.
  816. void PictureFlowSoftwareRenderer::render()
  817. {
  818. buffer.fill(state->backgroundColor);
  819. renderSlides();
  820. dirty = false;
  821. }
  822. // -----------------------------------------
  823. class PictureFlowPrivate
  824. {
  825. public:
  826. PictureFlowState* state;
  827. PictureFlowAnimator* animator;
  828. PictureFlowAbstractRenderer* renderer;
  829. QTimer triggerTimer;
  830. };
  831. PictureFlow::PictureFlow(QWidget* parent): QWidget(parent)
  832. {
  833. d = new PictureFlowPrivate;
  834. d->state = new PictureFlowState;
  835. d->state->reset();
  836. d->state->reposition();
  837. d->renderer = new PictureFlowSoftwareRenderer;
  838. d->renderer->state = d->state;
  839. d->renderer->widget = this;
  840. d->renderer->init();
  841. d->animator = new PictureFlowAnimator;
  842. d->animator->state = d->state;
  843. QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
  844. QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render()));
  845. #ifdef PICTUREFLOW_QT4
  846. setAttribute(Qt::WA_StaticContents, true);
  847. setAttribute(Qt::WA_OpaquePaintEvent, true);
  848. setAttribute(Qt::WA_NoSystemBackground, true);
  849. #endif
  850. #ifdef PICTUREFLOW_QT3
  851. setWFlags(getWFlags() | Qt::WStaticContents);
  852. setWFlags(getWFlags() | Qt::WNoAutoErase);
  853. #endif
  854. #ifdef PICTUREFLOW_QT2
  855. setWFlags(getWFlags() | Qt::WPaintClever);
  856. setWFlags(getWFlags() | Qt::WRepaintNoErase);
  857. setWFlags(getWFlags() | Qt::WResizeNoErase);
  858. #endif
  859. }
  860. PictureFlow::~PictureFlow()
  861. {
  862. delete d->renderer;
  863. delete d->animator;
  864. delete d->state;
  865. delete d;
  866. }
  867. int PictureFlow::slideCount() const
  868. {
  869. return d->state->slideImages.count();
  870. }
  871. QColor PictureFlow::backgroundColor() const
  872. {
  873. return QColor(d->state->backgroundColor);
  874. }
  875. void PictureFlow::setBackgroundColor(const QColor& c)
  876. {
  877. d->state->backgroundColor = c.rgb();
  878. triggerRender();
  879. }
  880. QSize PictureFlow::slideSize() const
  881. {
  882. return QSize(d->state->slideWidth, d->state->slideHeight);
  883. }
  884. void PictureFlow::setSlideSize(QSize size)
  885. {
  886. d->state->slideWidth = size.width();
  887. d->state->slideHeight = size.height();
  888. d->state->reposition();
  889. triggerRender();
  890. }
  891. PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const
  892. {
  893. return d->state->reflectionEffect;
  894. }
  895. void PictureFlow::setReflectionEffect(ReflectionEffect effect)
  896. {
  897. d->state->reflectionEffect = effect;
  898. triggerRender();
  899. }
  900. QImage PictureFlow::slide(int index) const
  901. {
  902. QImage* i = 0;
  903. if((index >= 0) && (index < slideCount()))
  904. i = d->state->slideImages[index];
  905. return i ? QImage(*i) : QImage();
  906. }
  907. void PictureFlow::addSlide(const QImage& image)
  908. {
  909. int c = d->state->slideImages.count();
  910. d->state->slideImages.resize(c+1);
  911. d->state->slideImages[c] = new QImage(image);
  912. triggerRender();
  913. }
  914. void PictureFlow::addSlideCaption(QString caption)
  915. {
  916. d->renderer->captions.append(caption);
  917. qDebug() << "Added contact: " << d->renderer->captions.last();
  918. }
  919. void PictureFlow::addSlide(const QPixmap& pixmap)
  920. {
  921. addSlide(pixmap.toImage());
  922. }
  923. void PictureFlow::setSlide(int index, const QImage& image)
  924. {
  925. if((index >= 0) && (index < slideCount()))
  926. {
  927. QImage* i = image.isNull() ? 0 : new QImage(image);
  928. delete d->state->slideImages[index];
  929. d->state->slideImages[index] = i;
  930. triggerRender();
  931. }
  932. }
  933. void PictureFlow::setSlide(int index, const QPixmap& pixmap)
  934. {
  935. setSlide(index, pixmap.toImage());
  936. }
  937. void PictureFlow::setSlideCaption(int index, QString caption)
  938. {
  939. d->renderer->captions.insert(index, caption);
  940. }
  941. int PictureFlow::centerIndex() const
  942. {
  943. return d->state->centerIndex;
  944. }
  945. void PictureFlow::setCenterIndex(int index)
  946. {
  947. index = qMin(index, slideCount()-1);
  948. index = qMax(index, 0);
  949. d->state->centerIndex = index;
  950. d->state->reset();
  951. d->animator->stop(index);
  952. triggerRender();
  953. }
  954. void PictureFlow::clear()
  955. {
  956. int c = d->state->slideImages.count();
  957. for(int i = 0; i < c; i++)
  958. delete d->state->slideImages[i];
  959. d->state->slideImages.resize(0);
  960. d->state->reset();
  961. triggerRender();
  962. }
  963. void PictureFlow::render()
  964. {
  965. d->renderer->dirty = true;
  966. update();
  967. }
  968. void PictureFlow::triggerRender()
  969. {
  970. #ifdef PICTUREFLOW_QT4
  971. d->triggerTimer.setSingleShot(true);
  972. d->triggerTimer.start(0);
  973. #endif
  974. #if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2)
  975. d->triggerTimer.start(0, true);
  976. #endif
  977. }
  978. void PictureFlow::showPrevious()
  979. {
  980. int step = d->animator->step;
  981. int center = d->state->centerIndex;
  982. if(step > 0)
  983. d->animator->start(center);
  984. if(step == 0)
  985. if(center > 0)
  986. d->animator->start(center - 1);
  987. if(step < 0)
  988. d->animator->target = qMax(0, center - 2);
  989. }
  990. void PictureFlow::showNext()
  991. {
  992. int step = d->animator->step;
  993. int center = d->state->centerIndex;
  994. if(step < 0)
  995. d->animator->start(center);
  996. if(step == 0)
  997. if(center < slideCount()-1)
  998. d->animator->start(center + 1);
  999. if(step > 0)
  1000. d->animator->target = qMin(center + 2, slideCount()-1);
  1001. }
  1002. void PictureFlow::showSlide(int index)
  1003. {
  1004. index = qMax(index, 0);
  1005. index = qMin(slideCount()-1, index);
  1006. if(index == d->state->centerSlide.slideIndex)
  1007. return;
  1008. d->animator->start(index);
  1009. }
  1010. void PictureFlow::keyPressEvent(QKeyEvent* event)
  1011. {
  1012. if(event->key() == Qt::Key_Left)
  1013. {
  1014. if(event->modifiers() == Qt::ControlModifier)
  1015. showSlide(centerIndex()-10);
  1016. else
  1017. showPrevious();
  1018. event->accept();
  1019. return;
  1020. }
  1021. if(event->key() == Qt::Key_Right)
  1022. {
  1023. if(event->modifiers() == Qt::ControlModifier)
  1024. showSlide(centerIndex()+10);
  1025. else
  1026. showNext();
  1027. event->accept();
  1028. return;
  1029. }
  1030. event->ignore();
  1031. }
  1032. void PictureFlow::mousePressEvent(QMouseEvent* event)
  1033. {
  1034. if(event->x() > width()/2)
  1035. showNext();
  1036. else
  1037. showPrevious();
  1038. emit mouseClicked();
  1039. }
  1040. void PictureFlow::paintEvent(QPaintEvent* event)
  1041. {
  1042. Q_UNUSED(event);
  1043. d->renderer->paint();
  1044. }
  1045. void PictureFlow::resizeEvent(QResizeEvent* event)
  1046. {
  1047. triggerRender();
  1048. QWidget::resizeEvent(event);
  1049. }
  1050. void PictureFlow::updateAnimation()
  1051. {
  1052. int old_center = d->state->centerIndex;
  1053. d->animator->update();
  1054. triggerRender();
  1055. if(d->state->centerIndex != old_center)
  1056. emit centerIndexChanged(d->state->centerIndex);
  1057. }