summaryrefslogtreecommitdiff
path: root/apps/plugins/pictureflow.c
diff options
context:
space:
mode:
authorNicolas Pennequin <nicolas.pennequin@free.fr>2007-12-09 18:48:02 +0000
committerNicolas Pennequin <nicolas.pennequin@free.fr>2007-12-09 18:48:02 +0000
commit90ce8269033dd1f06a5054392f35e45e45a5c560 (patch)
tree0583f4c56c2b8d73078f2112cc851862b859385d /apps/plugins/pictureflow.c
parent19f487b451b026833a6d9aaeabcec756c9e9f2b7 (diff)
downloadrockbox-90ce8269033dd1f06a5054392f35e45e45a5c560.tar.gz
rockbox-90ce8269033dd1f06a5054392f35e45e45a5c560.zip
Initial commit of the PictureFlow plugin, a nice visualization for album covers.
Original code by Ariya Hidayat. Rockbox port by Jonas Hurrelmann and a few tweaks by me. On the first startup the database is scanned and album art is searched for each album. This process will take some time (dircache enabled and/or database in RAM speed it up, and it is possible to abort by pressing MENU), but the results are cached and the next startups will be very quick. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15900 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pictureflow.c')
-rw-r--r--apps/plugins/pictureflow.c1586
1 files changed, 1586 insertions, 0 deletions
diff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.c
new file mode 100644
index 0000000000..a4b75b2ace
--- /dev/null
+++ b/apps/plugins/pictureflow.c
@@ -0,0 +1,1586 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2007 Jonas Hurrelmann (j@outpo.st)
11* Copyright (C) 2007 Nicolas Pennequin
12* Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) (original Qt Version)
13*
14* Original code: http://code.google.com/p/pictureflow/
15*
16* All files in this archive are subject to the GNU General Public License.
17* See the file COPYING in the source tree root for full license agreement.
18*
19* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20* KIND, either express or implied.
21*
22****************************************************************************/
23
24#include "plugin.h"
25#include "pluginlib_actions.h"
26#include "helper.h"
27#include "lib/bmp.h"
28#include "picture.h"
29#include "pictureflow_logo.h"
30#include "pictureflow_emptyslide.h"
31
32
33PLUGIN_HEADER
34
35/******************************* Globals ***********************************/
36
37static struct plugin_api *rb; /* global api struct pointer */
38
39const struct button_mapping *plugin_contexts[]
40= {generic_directions, generic_actions};
41
42#define NB_ACTION_CONTEXTS sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
43
44/* Key assignement */
45#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
46 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
47 || (CONFIG_KEYPAD == IPOD_4G_PAD) \
48 || (CONFIG_KEYPAD == SANSA_E200_PAD)
49#define PICTUREFLOW_NEXT_ALBUM PLA_UP
50#define PICTUREFLOW_NEXT_ALBUM_REPEAT PLA_UP_REPEAT
51#define PICTUREFLOW_PREV_ALBUM PLA_DOWN
52#define PICTUREFLOW_PREV_ALBUM_REPEAT PLA_DOWN_REPEAT
53#else
54#define PICTUREFLOW_NEXT_ALBUM PLA_RIGHT
55#define PICTUREFLOW_NEXT_ALBUM_REPEAT PLA_RIGHT_REPEAT
56#define PICTUREFLOW_PREV_ALBUM PLA_LEFT
57#define PICTUREFLOW_PREV_ALBUM_REPEAT PLA_LEFT_REPEAT
58#endif
59#define PICTUREFLOW_MENU PLA_MENU
60#define PICTUREFLOW_QUIT PLA_QUIT
61#define PICTUREFLOW_SELECT_ALBUM PLA_FIRE
62
63
64/* for fixed-point arithmetic, we need minimum 32-bit long
65 long long (64-bit) might be useful for multiplication and division */
66#define PFreal long
67#define PFREAL_SHIFT 10
68#define PFREAL_FACTOR (1 << PFREAL_SHIFT)
69#define PFREAL_ONE (1 << PFREAL_SHIFT)
70#define PFREAL_HALF (PFREAL_ONE >> 1)
71
72
73#define IANGLE_MAX 1024
74#define IANGLE_MASK 1023
75
76/* maximum size of an slide */
77#define MAX_IMG_WIDTH 100
78#define MAX_IMG_HEIGHT 100
79
80#define BUFFER_WIDTH LCD_WIDTH
81#define BUFFER_HEIGHT LCD_HEIGHT
82
83#define SLIDE_CACHE_SIZE 100
84
85#define LEFT_SLIDES_COUNT 3
86#define RIGHT_SLIDES_COUNT 3
87
88#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
89#define CACHE_PREFIX PLUGIN_DEMOS_DIR "/pictureflow"
90
91#define EV_EXIT 9999
92#define EV_WAKEUP 1337
93
94/* maximum number of albums */
95#define MAX_ALBUMS 1024
96#define AVG_ALBUM_NAME_LENGTH 20
97
98#define UNIQBUF_SIZE (64*1024)
99
100#define EMPTY_SLIDE CACHE_PREFIX "/emptyslide.pfraw"
101
102
103/** structs we use */
104
105struct slide_data {
106 int slide_index;
107 int angle;
108 PFreal cx;
109 PFreal cy;
110};
111
112struct slide_cache {
113 int index; /* index of the cached slide */
114 int hid; /* handle ID of the cached slide */
115 long touched; /* last time the slide was touched */
116};
117
118struct album_data {
119 int name_idx;
120 long seek;
121};
122
123struct rect {
124 int left;
125 int right;
126 int top;
127 int bottom;
128};
129
130struct load_slide_event_data {
131 int slide_index;
132 int cache_index;
133};
134
135
136struct pfraw_header {
137 int32_t width; /* bmap width in pixels */
138 int32_t height; /* bmap height in pixels */
139};
140
141const struct picture logos[]={
142 {pictureflow_logo, BMPWIDTH_pictureflow_logo, BMPHEIGHT_pictureflow_logo},
143};
144
145
146
147/** below we allocate the memory we want to use **/
148
149static fb_data *buffer; /* for now it always points to the lcd framebuffer */
150static PFreal rays[BUFFER_WIDTH];
151static bool animation_is_active; /* an animation is currently running */
152static struct slide_data center_slide;
153static struct slide_data left_slides[LEFT_SLIDES_COUNT];
154static struct slide_data right_slides[RIGHT_SLIDES_COUNT];
155static int slide_frame;
156static int step;
157static int target;
158static int fade;
159static int center_index; /* index of the slide that is in the center */
160static int itilt;
161static int spacing; /* spacing between slides */
162static int zoom;
163static PFreal offsetX;
164static PFreal offsetY;
165static bool show_fps; /* show fps in the main screen */
166static int number_of_slides;
167
168static struct slide_cache cache[SLIDE_CACHE_SIZE+1];
169static int slide_cache_in_use;
170
171/* use long for aligning */
172unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)];
173static int slide_cache_stack[SLIDE_CACHE_SIZE]; /* queue (as array) for scheduling load_surface */
174static int slide_cache_stack_index;
175struct mutex slide_cache_stack_lock;
176
177static int empty_slide_hid;
178
179struct thread_entry *thread_id;
180struct event_queue thread_q;
181
182static char tmp_path_name[MAX_PATH];
183
184static long uniqbuf[UNIQBUF_SIZE];
185static struct tagcache_search tcs;
186
187static struct album_data album[MAX_ALBUMS];
188static char album_names[MAX_ALBUMS*AVG_ALBUM_NAME_LENGTH];
189static int album_count;
190
191static fb_data input_bmp_buffer[MAX_IMG_WIDTH * MAX_IMG_HEIGHT]; /* static buffer for reading the bitmaps */
192static fb_data output_bmp_buffer[MAX_IMG_WIDTH * MAX_IMG_HEIGHT * 2]; /* static buffer for reading the bitmaps */
193
194static bool thread_is_running;
195
196
197PFreal sinTable[] = { /* 10 */
198 3, 9, 15, 21, 28, 34, 40, 47,
199 53, 59, 65, 72, 78, 84, 90, 97,
200 103, 109, 115, 122, 128, 134, 140, 147,
201 153, 159, 165, 171, 178, 184, 190, 196,
202 202, 209, 215, 221, 227, 233, 239, 245,
203 251, 257, 264, 270, 276, 282, 288, 294,
204 300, 306, 312, 318, 324, 330, 336, 342,
205 347, 353, 359, 365, 371, 377, 383, 388,
206 394, 400, 406, 412, 417, 423, 429, 434,
207 440, 446, 451, 457, 463, 468, 474, 479,
208 485, 491, 496, 501, 507, 512, 518, 523,
209 529, 534, 539, 545, 550, 555, 561, 566,
210 571, 576, 581, 587, 592, 597, 602, 607,
211 612, 617, 622, 627, 632, 637, 642, 647,
212 652, 656, 661, 666, 671, 675, 680, 685,
213 690, 694, 699, 703, 708, 712, 717, 721,
214 726, 730, 735, 739, 743, 748, 752, 756,
215 760, 765, 769, 773, 777, 781, 785, 789,
216 793, 797, 801, 805, 809, 813, 816, 820,
217 824, 828, 831, 835, 839, 842, 846, 849,
218 853, 856, 860, 863, 866, 870, 873, 876,
219 879, 883, 886, 889, 892, 895, 898, 901,
220 904, 907, 910, 913, 916, 918, 921, 924,
221 927, 929, 932, 934, 937, 939, 942, 944,
222 947, 949, 951, 954, 956, 958, 960, 963,
223 965, 967, 969, 971, 973, 975, 977, 978,
224 980, 982, 984, 986, 987, 989, 990, 992,
225 994, 995, 997, 998, 999, 1001, 1002, 1003,
226 1004, 1006, 1007, 1008, 1009, 1010, 1011, 1012,
227 1013, 1014, 1015, 1015, 1016, 1017, 1018, 1018,
228 1019, 1019, 1020, 1020, 1021, 1021, 1022, 1022,
229 1022, 1023, 1023, 1023, 1023, 1023, 1023, 1023,
230 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1022,
231 1022, 1022, 1021, 1021, 1020, 1020, 1019, 1019,
232 1018, 1018, 1017, 1016, 1015, 1015, 1014, 1013,
233 1012, 1011, 1010, 1009, 1008, 1007, 1006, 1004,
234 1003, 1002, 1001, 999, 998, 997, 995, 994,
235 992, 990, 989, 987, 986, 984, 982, 980,
236 978, 977, 975, 973, 971, 969, 967, 965,
237 963, 960, 958, 956, 954, 951, 949, 947,
238 944, 942, 939, 937, 934, 932, 929, 927,
239 924, 921, 918, 916, 913, 910, 907, 904,
240 901, 898, 895, 892, 889, 886, 883, 879,
241 876, 873, 870, 866, 863, 860, 856, 853,
242 849, 846, 842, 839, 835, 831, 828, 824,
243 820, 816, 813, 809, 805, 801, 797, 793,
244 789, 785, 781, 777, 773, 769, 765, 760,
245 756, 752, 748, 743, 739, 735, 730, 726,
246 721, 717, 712, 708, 703, 699, 694, 690,
247 685, 680, 675, 671, 666, 661, 656, 652,
248 647, 642, 637, 632, 627, 622, 617, 612,
249 607, 602, 597, 592, 587, 581, 576, 571,
250 566, 561, 555, 550, 545, 539, 534, 529,
251 523, 518, 512, 507, 501, 496, 491, 485,
252 479, 474, 468, 463, 457, 451, 446, 440,
253 434, 429, 423, 417, 412, 406, 400, 394,
254 388, 383, 377, 371, 365, 359, 353, 347,
255 342, 336, 330, 324, 318, 312, 306, 300,
256 294, 288, 282, 276, 270, 264, 257, 251,
257 245, 239, 233, 227, 221, 215, 209, 202,
258 196, 190, 184, 178, 171, 165, 159, 153,
259 147, 140, 134, 128, 122, 115, 109, 103,
260 97, 90, 84, 78, 72, 65, 59, 53,
261 47, 40, 34, 28, 21, 15, 9, 3,
262 -4, -10, -16, -22, -29, -35, -41, -48,
263 -54, -60, -66, -73, -79, -85, -91, -98,
264 -104, -110, -116, -123, -129, -135, -141, -148,
265 -154, -160, -166, -172, -179, -185, -191, -197,
266 -203, -210, -216, -222, -228, -234, -240, -246,
267 -252, -258, -265, -271, -277, -283, -289, -295,
268 -301, -307, -313, -319, -325, -331, -337, -343,
269 -348, -354, -360, -366, -372, -378, -384, -389,
270 -395, -401, -407, -413, -418, -424, -430, -435,
271 -441, -447, -452, -458, -464, -469, -475, -480,
272 -486, -492, -497, -502, -508, -513, -519, -524,
273 -530, -535, -540, -546, -551, -556, -562, -567,
274 -572, -577, -582, -588, -593, -598, -603, -608,
275 -613, -618, -623, -628, -633, -638, -643, -648,
276 -653, -657, -662, -667, -672, -676, -681, -686,
277 -691, -695, -700, -704, -709, -713, -718, -722,
278 -727, -731, -736, -740, -744, -749, -753, -757,
279 -761, -766, -770, -774, -778, -782, -786, -790,
280 -794, -798, -802, -806, -810, -814, -817, -821,
281 -825, -829, -832, -836, -840, -843, -847, -850,
282 -854, -857, -861, -864, -867, -871, -874, -877,
283 -880, -884, -887, -890, -893, -896, -899, -902,
284 -905, -908, -911, -914, -917, -919, -922, -925,
285 -928, -930, -933, -935, -938, -940, -943, -945,
286 -948, -950, -952, -955, -957, -959, -961, -964,
287 -966, -968, -970, -972, -974, -976, -978, -979,
288 -981, -983, -985, -987, -988, -990, -991, -993,
289 -995, -996, -998, -999, -1000, -1002, -1003, -1004,
290 -1005, -1007, -1008, -1009, -1010, -1011, -1012, -1013,
291 -1014, -1015, -1016, -1016, -1017, -1018, -1019, -1019,
292 -1020, -1020, -1021, -1021, -1022, -1022, -1023, -1023,
293 -1023, -1024, -1024, -1024, -1024, -1024, -1024, -1024,
294 -1024, -1024, -1024, -1024, -1024, -1024, -1024, -1023,
295 -1023, -1023, -1022, -1022, -1021, -1021, -1020, -1020,
296 -1019, -1019, -1018, -1017, -1016, -1016, -1015, -1014,
297 -1013, -1012, -1011, -1010, -1009, -1008, -1007, -1005,
298 -1004, -1003, -1002, -1000, -999, -998, -996, -995,
299 -993, -991, -990, -988, -987, -985, -983, -981,
300 -979, -978, -976, -974, -972, -970, -968, -966,
301 -964, -961, -959, -957, -955, -952, -950, -948,
302 -945, -943, -940, -938, -935, -933, -930, -928,
303 -925, -922, -919, -917, -914, -911, -908, -905,
304 -902, -899, -896, -893, -890, -887, -884, -880,
305 -877, -874, -871, -867, -864, -861, -857, -854,
306 -850, -847, -843, -840, -836, -832, -829, -825,
307 -821, -817, -814, -810, -806, -802, -798, -794,
308 -790, -786, -782, -778, -774, -770, -766, -761,
309 -757, -753, -749, -744, -740, -736, -731, -727,
310 -722, -718, -713, -709, -704, -700, -695, -691,
311 -686, -681, -676, -672, -667, -662, -657, -653,
312 -648, -643, -638, -633, -628, -623, -618, -613,
313 -608, -603, -598, -593, -588, -582, -577, -572,
314 -567, -562, -556, -551, -546, -540, -535, -530,
315 -524, -519, -513, -508, -502, -497, -492, -486,
316 -480, -475, -469, -464, -458, -452, -447, -441,
317 -435, -430, -424, -418, -413, -407, -401, -395,
318 -389, -384, -378, -372, -366, -360, -354, -348,
319 -343, -337, -331, -325, -319, -313, -307, -301,
320 -295, -289, -283, -277, -271, -265, -258, -252,
321 -246, -240, -234, -228, -222, -216, -210, -203,
322 -197, -191, -185, -179, -172, -166, -160, -154,
323 -148, -141, -135, -129, -123, -116, -110, -104,
324 -98, -91, -85, -79, -73, -66, -60, -54,
325 -48, -41, -35, -29, -22, -16, -10, -4,
326};
327
328
329/** code */
330
331
332bool create_bmp(struct bitmap* input_bmp, char *target_path);
333int load_surface(int);
334
335/* There are some precision issues when not using (long long) which in turn
336 takes very long to compute... I guess the best solution would be to optimize
337 the computations so it only requires a single long */
338static inline PFreal fmul(PFreal a, PFreal b)
339{
340 return ((long long) (a)) * ((long long) (b)) >> PFREAL_SHIFT;
341}
342
343static inline PFreal fdiv(PFreal num, PFreal den)
344{
345 long long p = (long long) (num) << (PFREAL_SHIFT * 2);
346 long long q = p / (long long) den;
347 long long r = q >> PFREAL_SHIFT;
348
349 return r;
350}
351
352#define fmin(a,b) (((a) < (b)) ? (a) : (b))
353#define fmax(a,b) (((a) > (b)) ? (a) : (b))
354#define fbound(min,val,max) (fmax((min),fmin((max),(val))))
355
356
357#if 0
358#define fmul(a,b) ( ((a)*(b)) >> PFREAL_SHIFT )
359#define fdiv(n,m) ( ((n)<< PFREAL_SHIFT ) / m )
360
361#define fconv(a, q1, q2) (((q2)>(q1)) ? (a)<<((q2)-(q1)) : (a)>>((q1)-(q2)))
362#define tofloat(a, q) ( (float)(a) / (float)(1<<(q)) )
363
364static inline PFreal fmul(PFreal a, PFreal b)
365{
366 return (a*b) >> PFREAL_SHIFT;
367}
368
369static inline PFreal fdiv(PFreal n, PFreal m)
370{
371 return (n<<(PFREAL_SHIFT))/m;
372}
373#endif
374
375inline PFreal fsin(int iangle)
376{
377 while (iangle < 0)
378 iangle += IANGLE_MAX;
379 return sinTable[iangle & IANGLE_MASK];
380}
381
382inline PFreal fcos(int iangle)
383{
384 /* quarter phase shift */
385 return fsin(iangle + (IANGLE_MAX >> 2));
386}
387
388
389/**
390 Create an index of all albums from the database.
391 Also store the album names so we can access them later.
392 */
393bool create_album_index(void)
394{
395 rb->memset(&tcs, 0, sizeof(struct tagcache_search) );
396 album_count = 0;
397 rb->tagcache_search(&tcs, tag_album);
398 rb->tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE);
399 int l, old_l = 0;
400 album[0].name_idx = 0;
401 while (rb->tagcache_get_next(&tcs) && album_count < MAX_ALBUMS)
402 {
403 l = rb->strlen(tcs.result) + 1;
404 if ( album_count > 0 )
405 album[album_count].name_idx = album[album_count-1].name_idx + old_l;
406
407 if ( (album[album_count].name_idx + l) > MAX_ALBUMS*AVG_ALBUM_NAME_LENGTH )
408 /* not enough memory */
409 return false;
410
411 rb->strcpy(album_names + album[album_count].name_idx, tcs.result);
412 album[album_count].seek = tcs.result_seek;
413 old_l = l;
414 album_count++;
415 }
416 rb->tagcache_search_finish(&tcs);
417 return true;
418}
419
420/**
421 Return a pointer to the album name of the given slide_index
422 */
423char* get_album_name(int slide_index)
424{
425 return album_names + album[slide_index].name_idx;
426}
427
428
429/**
430 Determine filename of the album art for the given slide_index and
431 store the result in buf.
432 The algorithm looks for the first track of the given album uses
433 find_albumart to find the filename.
434 */
435bool get_albumart_for_index_from_db(int slide_index, char *buf, int buflen)
436{
437 if ( slide_index == -1 )
438 {
439 rb->strncpy( buf, EMPTY_SLIDE, buflen );
440 }
441
442 if (!rb->tagcache_search(&tcs, tag_filename))
443 return false;
444
445 bool result;
446 /* find the first track of the album */
447 rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek);
448
449 if ( rb->tagcache_get_next(&tcs) ) {
450 struct mp3entry id3;
451 char size[9];
452 rb->snprintf(size, sizeof(size), ".%dx%d", MAX_IMG_WIDTH, MAX_IMG_HEIGHT);
453 rb->strncpy( (char*)&id3.path, tcs.result, MAX_PATH );
454 id3.album = get_album_name(slide_index);
455 if ( rb->search_albumart_files(&id3, size, buf, buflen) )
456 result = true;
457 else if ( rb->search_albumart_files(&id3, "", buf, buflen) )
458 result = true;
459 else
460 result = false;
461 }
462 else {
463 /* did not find a matching track */
464 result = false;
465 }
466 rb->tagcache_search_finish(&tcs);
467 return result;
468}
469
470/**
471 Draw the PictureFlow logo
472 */
473void draw_splashscreen(void)
474{
475 int txt_w, txt_h;
476 struct screen* display = rb->screens[0];
477
478 rb->lcd_set_background(LCD_RGBPACK(0,0,0));
479 rb->lcd_set_foreground(LCD_RGBPACK(255,255,255));
480 rb->lcd_clear_display();
481
482 const struct picture* logo = &(logos[display->screen_type]);
483 picture_draw(display, logo, (LCD_WIDTH - logo->width) / 2, 20);
484
485 rb->lcd_getstringsize("Preparing album artwork", &txt_w, &txt_h);
486 rb->lcd_putsxy((LCD_WIDTH - txt_w)/2, 100, "Preparing album artwork");
487
488 rb->lcd_update();
489}
490
491
492/**
493 Draw a simple progress bar
494 */
495void draw_progressbar(int step)
496{
497 const int bar_height = 22;
498 const int w = LCD_WIDTH - 20;
499 const int y = 130;
500 const int x = 10;
501
502 rb->lcd_set_foreground(LCD_RGBPACK(100,100,100));
503 rb->lcd_drawrect(x, y, w+2, bar_height);
504 rb->lcd_set_foreground(LCD_RGBPACK(165, 231, 82));
505
506 rb->lcd_fillrect(x+1, y+1, step * w / album_count, bar_height-2);
507 rb->lcd_update();
508 rb->yield();
509}
510
511
512/**
513 Precomupte the album art images and store them in CACHE_PREFIX.
514 */
515bool create_albumart_cache(void)
516{
517 /* FIXME: currently we check for the file CACHE_PREFIX/ready
518 We need a real menu etc. to recreate cache. For now, delete
519 the file to recreate the cache. */
520
521 number_of_slides = album_count;
522
523 if ( rb->file_exists( CACHE_PREFIX "/ready" ) ) return true;
524
525 int i;
526 struct bitmap input_bmp;
527 for (i=0; i < album_count; i++)
528 {
529 draw_progressbar(i);
530 if (!get_albumart_for_index_from_db(i, tmp_path_name, MAX_PATH))
531 continue;
532
533 int ret;
534 input_bmp.data = (char *) &input_bmp_buffer;
535 ret = rb->read_bmp_file(tmp_path_name, &input_bmp, sizeof(input_bmp_buffer), FORMAT_NATIVE);
536 if (ret <= 0) continue; /* skip missing/broken files */
537
538 rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", i);
539 create_bmp(&input_bmp, tmp_path_name);
540 if ( rb->button_get(false) == PICTUREFLOW_MENU ) return false;
541 }
542 int fh = rb->creat( CACHE_PREFIX "/ready" );
543 rb->close(fh);
544
545 return true;
546}
547
548/**
549 Return the index on the stack of slide_index.
550 Return -1 if slide_index is not on the stack.
551 */
552int slide_stack_get_index(int slide_index)
553{
554 int i = slide_cache_stack_index + 1;
555 while (i--) {
556 if ( slide_cache_stack[i] == slide_index ) return i;
557 };
558 return -1;
559}
560
561/**
562 Push the slide_index on the stack so the image will be loaded.
563 The algorithm tries to keep the center_index on top and the
564 slide_index as high as possible (so second if center_index is
565 on the stack).
566 */
567void slide_stack_push(int slide_index)
568{
569 rb->mutex_lock(&slide_cache_stack_lock);
570
571 if ( slide_cache_stack_index == -1 ) {
572 /* empty stack, no checks at all */
573 slide_cache_stack[ ++slide_cache_stack_index ] = slide_index;
574 rb->mutex_unlock(&slide_cache_stack_lock);
575 return;
576 }
577
578 int i = slide_stack_get_index( slide_index );
579
580 if ( i == slide_cache_stack_index ) {
581 /* slide_index is on top, so we do not change anything */
582 rb->mutex_unlock(&slide_cache_stack_lock);
583 return;
584 }
585
586 if ( i >= 0 ) {
587 /* slide_index is already on the stack, but not on top */
588 int tmp = slide_cache_stack[ slide_cache_stack_index ];
589 if ( tmp == center_index ) {
590 /* the center_index is on top of the stack so do not touch that */
591 if ( slide_cache_stack_index > 0 ) {
592 /* but maybe it is possible to swap the given slide_index to the second place */
593 tmp = slide_cache_stack[ slide_cache_stack_index -1 ];
594 slide_cache_stack[ slide_cache_stack_index - 1 ] = slide_cache_stack[ i ];
595 slide_cache_stack[ i ] = tmp;
596 }
597 }
598 else {
599 /* if the center_index is not on top (i.e. already loaded) bring the slide_index to the top */
600 slide_cache_stack[ slide_cache_stack_index ] = slide_cache_stack[ i ];
601 slide_cache_stack[ i ] = tmp;
602 }
603 }
604 else {
605 /* slide_index is not on the stack */
606 if ( slide_cache_stack_index >= SLIDE_CACHE_SIZE-1 ) {
607 /* if we exceeded the stack size, clear the first half of the stack */
608 slide_cache_stack_index = SLIDE_CACHE_SIZE/2;
609 for (i = 0; i <= slide_cache_stack_index ; i++)
610 slide_cache_stack[ i ] = slide_cache_stack[ i + slide_cache_stack_index ];
611 }
612 if ( slide_cache_stack[ slide_cache_stack_index ] == center_index ) {
613 /* if the center_index is on top leave it there */
614 slide_cache_stack[ slide_cache_stack_index ] = slide_index;
615 slide_cache_stack[ ++slide_cache_stack_index ] = center_index;
616 }
617 else {
618 /* usual stack case: push the slide_index on top */
619 slide_cache_stack[ ++slide_cache_stack_index ] = slide_index;
620 }
621 }
622 rb->mutex_unlock(&slide_cache_stack_lock);
623}
624
625
626/**
627 Pop the topmost item from the stack and decrease the stack size
628 */
629inline int slide_stack_pop(void)
630{
631 rb->mutex_lock(&slide_cache_stack_lock);
632 int result;
633 if ( slide_cache_stack_index >= 0 )
634 result = slide_cache_stack[ slide_cache_stack_index-- ];
635 else
636 result = -1;
637 rb->mutex_unlock(&slide_cache_stack_lock);
638 return result;
639}
640
641
642/**
643 Load the slide into the cache.
644 Thus we have to queue the loading request in our thread while discarding the oldest slide.
645 */
646void request_surface(int slide_index)
647{
648 slide_stack_push(slide_index);
649 rb->queue_post(&thread_q, EV_WAKEUP, 0);
650}
651
652
653/**
654 Thread used for loading and preparing bitmaps in the background
655 */
656void thread(void)
657{
658 long sleep_time = 5 * HZ;
659 struct queue_event ev;
660 while (1) {
661 rb->queue_wait_w_tmo(&thread_q, &ev, sleep_time);
662 switch (ev.id) {
663 case EV_EXIT:
664 return;
665 case EV_WAKEUP:
666 /* we just woke up */
667 break;
668 }
669 int slide_index;
670 while ( (slide_index = slide_stack_pop()) != -1 ) {
671 load_surface( slide_index );
672 rb->queue_wait_w_tmo(&thread_q, &ev, HZ/10);
673 switch (ev.id) {
674 case EV_EXIT:
675 return;
676 }
677 }
678 }
679}
680
681
682/**
683 End the thread by posting the EV_EXIT event
684 */
685void end_pf_thread(void)
686{
687 if ( thread_is_running ) {
688 rb->queue_post(&thread_q, EV_EXIT, 0);
689 rb->thread_wait(thread_id);
690 /* remove the thread's queue from the broadcast list */
691 rb->queue_delete(&thread_q);
692 thread_is_running = false;
693 }
694
695}
696
697
698/**
699 Create the thread an setup the event queue
700 */
701bool create_pf_thread(void)
702{
703 rb->queue_init(&thread_q, true); /* put the thread's queue in the bcast list */
704 if ((thread_id = rb->create_thread(
705 thread,
706 thread_stack,
707 sizeof(thread_stack),
708 0,
709 "Picture load thread"
710 IF_PRIO(, PRIORITY_BACKGROUND)
711 IF_COP(, CPU)
712 )
713 ) == NULL) {
714 return false;
715 }
716 thread_is_running = true;
717 return true;
718}
719
720/**
721 Safe the given bitmap as filename in the pfraw format
722 */
723int save_pfraw(char* filename, struct bitmap *bm)
724{
725 struct pfraw_header bmph;
726 bmph.width = bm->width;
727 bmph.height = bm->height;
728 int fh = rb->creat( filename );
729 if( fh < 0 ) return -1;
730 rb->write( fh, &bmph, sizeof( struct pfraw_header ) );
731 int y;
732 for( y = 0; y < bm->height; y++ )
733 {
734 fb_data *d = (fb_data*)( bm->data ) + (y*bm->width);
735 rb->write( fh, d, sizeof( fb_data ) * bm->width );
736 }
737 rb->close( fh );
738 return 0;
739}
740
741
742/**
743 Read the pfraw image given as filename and return the hid of the buffer
744 */
745int read_pfraw(char* filename)
746{
747 struct pfraw_header bmph;
748 int fh = rb->open(filename, O_RDONLY);
749 rb->read(fh, &bmph, sizeof(struct pfraw_header));
750 if( fh < 0 ) {
751 return empty_slide_hid;
752 }
753
754 int size = sizeof(struct bitmap) + sizeof( fb_data ) * bmph.width * bmph.height;
755
756 int hid = rb->bufalloc(NULL, size, TYPE_BITMAP);
757 if (hid < 0)
758 return -1;
759
760 struct bitmap *bm;
761 if (rb->bufgetdata(hid, 0, (void *)&bm) < size)
762 return -1;
763
764 bm->width = bmph.width;
765 bm->height = bmph.height;
766 bm->format = FORMAT_NATIVE;
767 bm->data = ((unsigned char *)bm + sizeof(struct bitmap));
768
769 int y;
770 for( y = 0; y < bm->height; y++ )
771 {
772 fb_data *d = (fb_data*)( bm->data ) + (y*bm->width);
773 rb->read( fh, d , sizeof( fb_data ) * bm->width );
774 }
775 rb->close( fh );
776 return hid;
777}
778
779
780/**
781 Create the slide with it's reflection for the given slide_index and filename
782 and store it as pfraw in CACHE_PREFIX/[slide_index].pfraw
783 */
784bool create_bmp(struct bitmap *input_bmp, char *target_path)
785{
786 fb_data *src = (fb_data *)input_bmp->data;
787 struct bitmap output_bmp;
788
789 output_bmp.width = input_bmp->width * 2;
790 output_bmp.height = input_bmp->height;
791 output_bmp.format = input_bmp->format;
792 output_bmp.data = (char*) &output_bmp_buffer;
793
794 /* transpose the image, this is to speed-up the rendering
795 because we process one column at a time
796 (and much better and faster to work row-wise, i.e in one scanline) */
797 int hofs = input_bmp->width / 3;
798 rb->memset(output_bmp_buffer, 0, sizeof(fb_data) * output_bmp.width * output_bmp.height);
799 int x, y;
800 for (x = 0; x < input_bmp->width; x++)
801 for (y = 0; y < input_bmp->height; y++)
802 output_bmp_buffer[output_bmp.width * x + (hofs + y)] =
803 src[y * input_bmp->width + x];
804
805 /* create the reflection */
806 int ht = input_bmp->height - hofs;
807 int hte = ht;
808 for (x = 0; x < input_bmp->width; x++) {
809 for (y = 0; y < ht; y++) {
810 fb_data color = src[x + input_bmp->width * (input_bmp->height - y - 1)];
811 int r = RGB_UNPACK_RED(color) * (hte - y) / hte * 3 / 5;
812 int g = RGB_UNPACK_GREEN(color) * (hte - y) / hte * 3 / 5;
813 int b = RGB_UNPACK_BLUE(color) * (hte - y) / hte * 3 / 5;
814 output_bmp_buffer[output_bmp.height + hofs + y + output_bmp.width * x] =
815 LCD_RGBPACK(r, g, b);
816 }
817 }
818 save_pfraw(target_path, &output_bmp);
819 return true;
820}
821
822
823/**
824 Load the surface for the given slide_index into the cache at cache_index.
825 */
826static bool load_and_prepare_surface(int slide_index, int cache_index)
827{
828 rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", slide_index);
829 int hid = read_pfraw(tmp_path_name);
830 if (hid < 0)
831 return false;
832
833 cache[cache_index].hid = hid;
834
835 if ( cache_index < SLIDE_CACHE_SIZE ) {
836 cache[cache_index].index = slide_index;
837 cache[cache_index].touched = *rb->current_tick;
838 }
839
840 return true;
841}
842
843
844/**
845 Load the surface from a bmp and overwrite the oldest slide in the cache
846 if necessary.
847 */
848int load_surface(int slide_index)
849{
850 long oldest_tick = *rb->current_tick;
851 int oldest_slide = 0;
852 int i;
853 if ( slide_cache_in_use < SLIDE_CACHE_SIZE ) { /* initial fill */
854 oldest_slide = slide_cache_in_use;
855 load_and_prepare_surface(slide_index, slide_cache_in_use++);
856 }
857 else {
858 for (i = 0; i < SLIDE_CACHE_SIZE; i++) { /* look for oldest slide */
859 if (cache[i].touched < oldest_tick) {
860 oldest_slide = i;
861 oldest_tick = cache[i].touched;
862 }
863 }
864 if (cache[oldest_slide].hid != empty_slide_hid) {
865 rb->bufclose(cache[oldest_slide].hid);
866 cache[oldest_slide].hid = -1;
867 }
868 load_and_prepare_surface(slide_index, oldest_slide);
869 }
870 return oldest_slide;
871}
872
873
874/**
875 Get a slide from the buffer
876 */
877struct bitmap *get_slide(int hid)
878{
879 if (hid < 0)
880 return NULL;
881
882 struct bitmap *bmp;
883
884 ssize_t ret = rb->bufgetdata(hid, 0, (void *)&bmp);
885 if (ret < 0)
886 return NULL;
887
888 return bmp;
889}
890
891
892/**
893 Return the requested surface
894*/
895struct bitmap *surface(int slide_index)
896{
897 if (slide_index < 0)
898 return 0;
899 if (slide_index >= number_of_slides)
900 return 0;
901
902 int i;
903 for (i = 0; i < slide_cache_in_use; i++) { /* maybe do the inverse mapping => implies dynamic allocation? */
904 if ( cache[i].index == slide_index ) {
905 /* We have already loaded our slide, so touch it and return it. */
906 cache[i].touched = *rb->current_tick;
907 return get_slide(cache[i].hid);
908 }
909 }
910 request_surface(slide_index);
911 return get_slide(empty_slide_hid);
912}
913
914/**
915 adjust slides so that they are in "steady state" position
916 */
917void reset_slides(void)
918{
919 center_slide.angle = 0;
920 center_slide.cx = 0;
921 center_slide.cy = 0;
922 center_slide.slide_index = center_index;
923
924 int i;
925 for (i = 0; i < LEFT_SLIDES_COUNT; i++) {
926 struct slide_data *si = &left_slides[i];
927 si->angle = itilt;
928 si->cx = -(offsetX + spacing * i * PFREAL_ONE);
929 si->cy = offsetY;
930 si->slide_index = center_index - 1 - i;
931 }
932
933 for (i = 0; i < RIGHT_SLIDES_COUNT; i++) {
934 struct slide_data *si = &right_slides[i];
935 si->angle = -itilt;
936 si->cx = offsetX + spacing * i * PFREAL_ONE;
937 si->cy = offsetY;
938 si->slide_index = center_index + 1 + i;
939 }
940}
941
942
943/**
944 Updates look-up table and other stuff necessary for the rendering.
945 Call this when the viewport size or slide dimension is changed.
946 */
947void recalc_table(void)
948{
949 int w = (BUFFER_WIDTH + 1) / 2;
950 int h = (BUFFER_HEIGHT + 1) / 2;
951 int i;
952 for (i = 0; i < w; i++) {
953 PFreal gg = (PFREAL_HALF + i * PFREAL_ONE) / (2 * h);
954 rays[w - i - 1] = -gg;
955 rays[w + i] = gg;
956 }
957
958 itilt = 70 * IANGLE_MAX / 360; /* approx. 70 degrees tilted */
959
960 offsetX = MAX_IMG_WIDTH / 2 * (PFREAL_ONE - fcos(itilt));
961 offsetY = MAX_IMG_WIDTH / 2 * fsin(itilt);
962 offsetX += MAX_IMG_WIDTH * PFREAL_ONE;
963 offsetY += MAX_IMG_WIDTH * PFREAL_ONE / 4;
964 spacing = 40;
965}
966
967
968/**
969 Render a single slide
970*/
971void render_slide(struct slide_data *slide, struct rect *result_rect,
972 int alpha, int col1, int col2)
973{
974 rb->memset(result_rect, 0, sizeof(struct rect));
975 struct bitmap *bmp = surface(slide->slide_index);
976 if (!bmp) {
977 return;
978 }
979 fb_data *src = (fb_data *)bmp->data;
980
981 int sw = bmp->height;
982 int sh = bmp->width;
983
984 int h = LCD_HEIGHT;
985 int w = LCD_WIDTH;
986
987 if (col1 > col2) {
988 int c = col2;
989 col2 = col1;
990 col1 = c;
991 }
992
993 col1 = (col1 >= 0) ? col1 : 0;
994 col2 = (col2 >= 0) ? col2 : w - 1;
995 col1 = fmin(col1, w - 1);
996 col2 = fmin(col2, w - 1);
997
998 int distance = h * 100 / zoom;
999 PFreal sdx = fcos(slide->angle);
1000 PFreal sdy = fsin(slide->angle);
1001 PFreal xs = slide->cx - bmp->width * sdx / 4;
1002 PFreal ys = slide->cy - bmp->width * sdy / 4;
1003 PFreal dist = distance * PFREAL_ONE;
1004
1005
1006 int xi = fmax((PFreal) 0,
1007 ((w * PFREAL_ONE / 2) +
1008 fdiv(xs * h, dist + ys)) >> PFREAL_SHIFT);
1009 if (xi >= w) {
1010 return;
1011 }
1012
1013 bool flag = false;
1014 result_rect->left = xi;
1015 int x;
1016 for (x = fmax(xi, col1); x <= col2; x++) {
1017 PFreal hity = 0;
1018 PFreal fk = rays[x];
1019 if (sdy) {
1020 fk = fk - fdiv(sdx, sdy);
1021 hity = -fdiv(( rays[x] * distance
1022 - slide->cx
1023 + slide->cy * sdx / sdy), fk);
1024 }
1025
1026 dist = distance * PFREAL_ONE + hity;
1027 if (dist < 0)
1028 continue;
1029
1030 PFreal hitx = fmul(dist, rays[x]);
1031
1032 PFreal hitdist = fdiv(hitx - slide->cx, sdx);
1033
1034 int column = sw / 2 + (hitdist >> PFREAL_SHIFT);
1035 if (column >= sw)
1036 break;
1037
1038 if (column < 0)
1039 continue;
1040
1041 result_rect->right = x;
1042 if (!flag)
1043 result_rect->left = x;
1044 flag = true;
1045
1046 int y1 = h / 2;
1047 int y2 = y1 + 1;
1048 fb_data *pixel1 = &buffer[y1 * BUFFER_WIDTH + x];
1049 fb_data *pixel2 = &buffer[y2 * BUFFER_WIDTH + x];
1050 int pixelstep = pixel2 - pixel1;
1051
1052 int center = (sh / 2);
1053 int dy = dist / h;
1054 int p1 = center * PFREAL_ONE - dy / 2;
1055 int p2 = center * PFREAL_ONE + dy / 2;
1056
1057
1058 const fb_data *ptr = &src[column * bmp->width];
1059 if (alpha == 256)
1060 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
1061 *pixel1 = ptr[p1 >> PFREAL_SHIFT];
1062 *pixel2 = ptr[p2 >> PFREAL_SHIFT];
1063 p1 -= dy;
1064 p2 += dy;
1065 y1--;
1066 y2++;
1067 pixel1 -= pixelstep;
1068 pixel2 += pixelstep;
1069 } else
1070 while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {
1071 fb_data c1 = ptr[p1 >> PFREAL_SHIFT];
1072 fb_data c2 = ptr[p2 >> PFREAL_SHIFT];
1073
1074 int r1 = RGB_UNPACK_RED(c1) * alpha / 256;
1075 int g1 = RGB_UNPACK_GREEN(c1) * alpha / 256;
1076 int b1 = RGB_UNPACK_BLUE(c1) * alpha / 256;
1077 int r2 = RGB_UNPACK_RED(c2) * alpha / 256;
1078 int g2 = RGB_UNPACK_GREEN(c2) * alpha / 256;
1079 int b2 = RGB_UNPACK_BLUE(c2) * alpha / 256;
1080
1081 *pixel1 = LCD_RGBPACK(r1, g1, b1);
1082 *pixel2 = LCD_RGBPACK(r2, g2, b2);
1083 p1 -= dy;
1084 p2 += dy;
1085 y1--;
1086 y2++;
1087 pixel1 -= pixelstep;
1088 pixel2 += pixelstep;
1089 }
1090 }
1091 /* let the music play... */
1092 rb->yield();
1093
1094 result_rect->top = 0;
1095 result_rect->bottom = h - 1;
1096 return;
1097}
1098
1099
1100/**
1101 Jump the the given slide_index
1102 */
1103static inline void set_current_slide(int slide_index)
1104{
1105 step = 0;
1106 center_index = fbound(slide_index, 0, number_of_slides - 1);
1107 target = center_index;
1108 slide_frame = slide_index << 16;
1109 reset_slides();
1110}
1111
1112/**
1113 Start the animation for changing slides
1114 */
1115void start_animation(void)
1116{
1117 if (!animation_is_active) {
1118 step = (target < center_slide.slide_index) ? -1 : 1;
1119 animation_is_active = true;
1120 }
1121}
1122
1123/**
1124 Go to the previous slide
1125 */
1126void show_previous_slide(void)
1127{
1128 if (step >= 0) {
1129 if (center_index > 0) {
1130 target = center_index - 1;
1131 start_animation();
1132 }
1133 } else {
1134 target = fmax(0, center_index - 2);
1135 }
1136}
1137
1138
1139/**
1140 Go to the next slide
1141 */
1142void show_next_slide(void)
1143{
1144 if (step <= 0) {
1145 if (center_index < number_of_slides - 1) {
1146 target = center_index + 1;
1147 start_animation();
1148 }
1149 } else {
1150 target = fmin(center_index + 2, number_of_slides - 1);
1151 }
1152}
1153
1154
1155/**
1156 Return true if the rect has size 0
1157*/
1158bool is_empty_rect(struct rect *r)
1159{
1160 return ((r->left == 0) && (r->right == 0) && (r->top == 0)
1161 && (r->bottom == 0));
1162}
1163
1164
1165/**
1166 Render the slides. Updates only the offscreen buffer.
1167*/
1168void render(void)
1169{
1170 rb->lcd_set_background(LCD_RGBPACK(0,0,0));
1171 rb->lcd_clear_display(); /* TODO: Optimizes this by e.g. invalidating rects */
1172
1173 int nleft = LEFT_SLIDES_COUNT;
1174 int nright = RIGHT_SLIDES_COUNT;
1175
1176 struct rect r;
1177 render_slide(&center_slide, &r, 256, -1, -1);
1178#ifdef DEBUG_DRAW
1179 rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);
1180#endif
1181 int c1 = r.left;
1182 int c2 = r.right;
1183 int index;
1184 if (step == 0) {
1185 /* no animation, boring plain rendering */
1186 for (index = 0; index < nleft - 1; index++) {
1187 int alpha = (index < nleft - 2) ? 256 : 128;
1188 render_slide(&left_slides[index], &r, alpha, 0, c1 - 1);
1189 if (!is_empty_rect(&r)) {
1190#ifdef DEBUG_DRAW
1191 rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);
1192#endif
1193 c1 = r.left;
1194 }
1195 }
1196 for (index = 0; index < nright - 1; index++) {
1197 int alpha = (index < nright - 2) ? 256 : 128;
1198 render_slide(&right_slides[index], &r, alpha, c2 + 1,
1199 BUFFER_WIDTH);
1200 if (!is_empty_rect(&r)) {
1201#ifdef DEBUG_DRAW
1202 rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);
1203#endif
1204 c2 = r.right;
1205 }
1206 }
1207 } else {
1208 /* the first and last slide must fade in/fade out */
1209 for (index = 0; index < nleft; index++) {
1210 int alpha = 256;
1211 if (index == nleft - 1)
1212 alpha = (step > 0) ? 0 : 128 - fade / 2;
1213 if (index == nleft - 2)
1214 alpha = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
1215 if (index == nleft - 3)
1216 alpha = (step > 0) ? 256 - fade / 2 : 256;
1217 render_slide(&left_slides[index], &r, alpha, 0, c1 - 1);
1218
1219 if (!is_empty_rect(&r)) {
1220#ifdef DEBUG_DRAW
1221 rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);
1222#endif
1223 c1 = r.left;
1224 }
1225 }
1226 for (index = 0; index < nright; index++) {
1227 int alpha = (index < nright - 2) ? 256 : 128;
1228 if (index == nright - 1)
1229 alpha = (step > 0) ? fade / 2 : 0;
1230 if (index == nright - 2)
1231 alpha = (step > 0) ? 128 + fade / 2 : fade / 2;
1232 if (index == nright - 3)
1233 alpha = (step > 0) ? 256 : 128 + fade / 2;
1234 render_slide(&right_slides[index], &r, alpha, c2 + 1,
1235 BUFFER_WIDTH);
1236 if (!is_empty_rect(&r)) {
1237#ifdef DEBUG_DRAW
1238 rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);
1239#endif
1240 c2 = r.right;
1241 }
1242 }
1243 }
1244}
1245
1246
1247/**
1248 Updates the animation effect. Call this periodically from a timer.
1249*/
1250void update_animation(void)
1251{
1252 if (!animation_is_active)
1253 return;
1254 if (step == 0)
1255 return;
1256
1257 int speed = 16384;
1258 int i;
1259
1260 /* deaccelerate when approaching the target */
1261 if (true) {
1262 const int max = 2 * 65536;
1263
1264 int fi = slide_frame;
1265 fi -= (target << 16);
1266 if (fi < 0)
1267 fi = -fi;
1268 fi = fmin(fi, max);
1269
1270 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
1271 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
1272 }
1273
1274 slide_frame += speed * step;
1275
1276 int index = slide_frame >> 16;
1277 int pos = slide_frame & 0xffff;
1278 int neg = 65536 - pos;
1279 int tick = (step < 0) ? neg : pos;
1280 PFreal ftick = (tick * PFREAL_ONE) >> 16;
1281
1282 /* the leftmost and rightmost slide must fade away */
1283 fade = pos / 256;
1284
1285 if (step < 0)
1286 index++;
1287 if (center_index != index) {
1288 center_index = index;
1289 slide_frame = index << 16;
1290 center_slide.slide_index = center_index;
1291 for (i = 0; i < LEFT_SLIDES_COUNT; i++)
1292 left_slides[i].slide_index = center_index - 1 - i;
1293 for (i = 0; i < RIGHT_SLIDES_COUNT; i++)
1294 right_slides[i].slide_index = center_index + 1 + i;
1295 }
1296
1297 center_slide.angle = (step * tick * itilt) >> 16;
1298 center_slide.cx = -step * fmul(offsetX, ftick);
1299 center_slide.cy = fmul(offsetY, ftick);
1300
1301 if (center_index == target) {
1302 reset_slides();
1303 animation_is_active = false;
1304 step = 0;
1305 fade = 256;
1306 return;
1307 }
1308
1309 for (i = 0; i < LEFT_SLIDES_COUNT; i++) {
1310 struct slide_data *si = &left_slides[i];
1311 si->angle = itilt;
1312 si->cx =
1313 -(offsetX + spacing * i * PFREAL_ONE + step * spacing * ftick);
1314 si->cy = offsetY;
1315 }
1316
1317 for (i = 0; i < RIGHT_SLIDES_COUNT; i++) {
1318 struct slide_data *si = &right_slides[i];
1319 si->angle = -itilt;
1320 si->cx =
1321 offsetX + spacing * i * PFREAL_ONE - step * spacing * ftick;
1322 si->cy = offsetY;
1323 }
1324
1325 if (step > 0) {
1326 PFreal ftick = (neg * PFREAL_ONE) >> 16;
1327 right_slides[0].angle = -(neg * itilt) >> 16;
1328 right_slides[0].cx = fmul(offsetX, ftick);
1329 right_slides[0].cy = fmul(offsetY, ftick);
1330 } else {
1331 PFreal ftick = (pos * PFREAL_ONE) >> 16;
1332 left_slides[0].angle = (pos * itilt) >> 16;
1333 left_slides[0].cx = -fmul(offsetX, ftick);
1334 left_slides[0].cy = fmul(offsetY, ftick);
1335 }
1336
1337 /* must change direction ? */
1338 if (target < index)
1339 if (step > 0)
1340 step = -1;
1341 if (target > index)
1342 if (step < 0)
1343 step = 1;
1344}
1345
1346
1347/**
1348 Cleanup the plugin
1349*/
1350void cleanup(void *parameter)
1351{
1352 (void) parameter;
1353#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1354 rb->cpu_boost(false);
1355#endif
1356 /* Turn on backlight timeout (revert to settings) */
1357 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1358
1359 int i;
1360 for (i = 0; i < slide_cache_in_use; i++) {
1361 rb->bufclose(cache[i].hid);
1362 }
1363 if ( empty_slide_hid != - 1)
1364 rb->bufclose(empty_slide_hid);
1365}
1366
1367
1368int create_empty_slide(void)
1369{
1370 if ( ! rb->file_exists( EMPTY_SLIDE ) ) {
1371 struct bitmap input_bmp;
1372 input_bmp.width = BMPWIDTH_pictureflow_emptyslide;
1373 input_bmp.height = BMPHEIGHT_pictureflow_emptyslide;
1374 input_bmp.format = FORMAT_NATIVE;
1375 DEBUGF("The empty slide is %d x %d\n", input_bmp.width, input_bmp.height);
1376 input_bmp.data = (char*) &pictureflow_emptyslide;
1377 if ( ! create_bmp(&input_bmp, EMPTY_SLIDE) ) return false;
1378 }
1379
1380 empty_slide_hid = read_pfraw( EMPTY_SLIDE );
1381 if (empty_slide_hid == -1 ) return false;
1382
1383 return true;
1384}
1385
1386/**
1387 Shows the settings menu
1388 */
1389int settings_menu(void) {
1390 int selection = 0;
1391
1392 MENUITEM_STRINGLIST(settings_menu,"PictureFlow Settings",NULL,"Show FPS");
1393
1394 selection=rb->do_menu(&settings_menu,&selection);
1395 switch(selection) {
1396 case 0:
1397 rb->set_bool("Show FPS", &show_fps);
1398 break;
1399
1400 case MENU_ATTACHED_USB:
1401 return PLUGIN_USB_CONNECTED;
1402 }
1403 return 0;
1404}
1405
1406/**
1407 Show the main menu
1408 */
1409int main_menu(void) {
1410 int selection = 0;
1411 int result;
1412
1413 MENUITEM_STRINGLIST(main_menu,"PictureFlow Main Menu",NULL,
1414 "Settings", "Return", "Quit");
1415
1416 while (1) {
1417 switch (rb->do_menu(&main_menu,&selection)) {
1418 case 0:
1419 result = settings_menu();
1420 if ( result != 0 ) return result;
1421 break;
1422
1423 case 1:
1424 return 0;
1425
1426 case 2:
1427 return -1;
1428
1429 case MENU_ATTACHED_USB:
1430 return PLUGIN_USB_CONNECTED;
1431
1432 default:
1433 return 0;
1434 }
1435 }
1436}
1437
1438/**
1439 Main function that also contain the main plasma
1440 algorithm.
1441 */
1442int main(void)
1443{
1444 draw_splashscreen();
1445
1446 if ( ! rb->dir_exists( CACHE_PREFIX ) ) {
1447 if ( rb->mkdir( CACHE_PREFIX ) < 0 ) {
1448 rb->splash(HZ, "Could not create directory");
1449 return PLUGIN_ERROR;
1450 }
1451 }
1452
1453 if (!create_empty_slide()) {
1454 rb->splash(HZ, "Could not load the empty slide");
1455 return PLUGIN_ERROR;
1456 }
1457
1458 if (!create_album_index()) {
1459 rb->splash(HZ, "Not enough memory for album names");
1460 return PLUGIN_ERROR;
1461 }
1462
1463 if (!create_albumart_cache()) {
1464 rb->splash(HZ, "Could not create album art cache");
1465 return PLUGIN_ERROR;
1466 }
1467
1468 if (!create_pf_thread()) {
1469 rb->splash(HZ, "Cannot create thread!");
1470 return PLUGIN_ERROR;
1471 }
1472
1473 int i;
1474
1475 /* initialize */
1476 for (i = 0; i < SLIDE_CACHE_SIZE; i++) {
1477 cache[i].hid = -1;
1478 cache[i].touched = 0;
1479 slide_cache_stack[i] = SLIDE_CACHE_SIZE-i-1;
1480 }
1481 slide_cache_stack_index = SLIDE_CACHE_SIZE-2;
1482 slide_cache_in_use = 0;
1483 buffer = rb->lcd_framebuffer;
1484 animation_is_active = false;
1485 zoom = 100;
1486 center_index = 0;
1487 slide_frame = 0;
1488 step = 0;
1489 target = 0;
1490 fade = 256;
1491 show_fps = false;
1492
1493 recalc_table();
1494 reset_slides();
1495
1496 char fpstxt[10];
1497 char *albumtxt;
1498 int button;
1499
1500#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1501 rb->cpu_boost(true);
1502#endif
1503 int frames = 0;
1504 long last_update = *rb->current_tick;
1505 long current_update;
1506 long update_interval = 100;
1507 int fps = 0;
1508 int albumtxt_w, albumtxt_h;
1509 int ret;
1510
1511 while (true) {
1512 current_update = *rb->current_tick;
1513 frames++;
1514 update_animation();
1515 render();
1516
1517 if (current_update - last_update > update_interval) {
1518 fps = frames * HZ / (current_update - last_update);
1519 last_update = current_update;
1520 frames = 0;
1521 }
1522
1523 if (show_fps) {
1524 rb->lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
1525 rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps);
1526 rb->lcd_putsxy(0, 0, fpstxt);
1527 }
1528
1529 albumtxt = get_album_name(center_index);
1530 rb->lcd_set_foreground(LCD_RGBPACK(255, 255, 255));
1531 rb->lcd_getstringsize(albumtxt, &albumtxt_w, &albumtxt_h);
1532 rb->lcd_putsxy((LCD_WIDTH - albumtxt_w) /2, LCD_HEIGHT-albumtxt_h-10, albumtxt);
1533
1534 rb->lcd_update();
1535 rb->yield();
1536
1537 button = pluginlib_getaction(rb, animation_is_active ? 0 : HZ/10,
1538 plugin_contexts, NB_ACTION_CONTEXTS);
1539
1540 switch (button) {
1541 case PICTUREFLOW_QUIT:
1542 return PLUGIN_OK;
1543
1544 case PICTUREFLOW_MENU:
1545 ret = main_menu();
1546 if ( ret == -1 ) return PLUGIN_OK;
1547 if ( ret != 0 ) return i;
1548 break;
1549
1550 case PICTUREFLOW_NEXT_ALBUM:
1551 case PICTUREFLOW_NEXT_ALBUM_REPEAT:
1552 show_next_slide();
1553 break;
1554
1555 case PICTUREFLOW_PREV_ALBUM:
1556 case PICTUREFLOW_PREV_ALBUM_REPEAT:
1557 show_previous_slide();
1558 break;
1559
1560 default:
1561 if (rb->default_event_handler_ex(button, cleanup, NULL)
1562 == SYS_USB_CONNECTED)
1563 return PLUGIN_USB_CONNECTED;
1564 break;
1565 }
1566 }
1567}
1568
1569/*************************** Plugin entry point ****************************/
1570
1571enum plugin_status plugin_start(struct plugin_api *api, void *parameter)
1572{
1573 int ret;
1574
1575 rb = api; /* copy to global api pointer */
1576 (void) parameter;
1577#if LCD_DEPTH > 1
1578 rb->lcd_set_backdrop(NULL);
1579#endif
1580 /* Turn off backlight timeout */
1581 backlight_force_on(rb); /* backlight control in lib/helper.c */
1582 ret = main();
1583 end_pf_thread();
1584 cleanup(NULL);
1585 return ret;
1586}