Greyscale targets: Make the gradient a bit brighter and simplify the calculation.
[kugel-rb.git] / apps / plugins / pictureflow.c
blobfd8079e21e199d542caa2b0ab34b77770297934f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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)
14 * Original code: http://code.google.com/p/pictureflow/
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
24 ****************************************************************************/
26 #include "plugin.h"
27 #include <albumart.h>
28 #include "lib/pluginlib_actions.h"
29 #include "lib/helper.h"
30 #include "lib/configfile.h"
31 #include "lib/picture.h"
32 #include "pluginbitmaps/pictureflow_logo.h"
33 #include "lib/grey.h"
34 #include "lib/feature_wrappers.h"
35 #include "lib/buflib.h"
37 PLUGIN_HEADER
39 /******************************* Globals ***********************************/
41 const struct button_mapping *plugin_contexts[]
42 = {generic_actions, generic_directions};
44 #define NB_ACTION_CONTEXTS sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
46 #if LCD_DEPTH < 8
47 #if LCD_DEPTH > 1
48 #define N_BRIGHT(y) LCD_BRIGHTNESS(y)
49 #else /* LCD_DEPTH <= 1 */
50 #define N_BRIGHT(y) ((y > 127) ? 0 : 1)
51 #ifdef HAVE_NEGATIVE_LCD /* m:robe 100, Clip */
52 #define PICTUREFLOW_DRMODE DRMODE_SOLID
53 #else
54 #define PICTUREFLOW_DRMODE (DRMODE_SOLID|DRMODE_INVERSEVID)
55 #endif
56 #endif /* LCD_DEPTH <= 1 */
57 #define USEGSLIB
58 GREY_INFO_STRUCT
59 #define LCD_BUF _grey_info.buffer
60 #define MYLCD(fn) grey_ ## fn
61 #define G_PIX(r,g,b) \
62 (77 * (unsigned)(r) + 150 * (unsigned)(g) + 29 * (unsigned)(b)) / 256
63 #define N_PIX(r,g,b) N_BRIGHT(G_PIX(r,g,b))
64 #define G_BRIGHT(y) (y)
65 #define BUFFER_WIDTH _grey_info.width
66 #define BUFFER_HEIGHT _grey_info.height
67 typedef unsigned char pix_t;
68 #else /* LCD_DEPTH >= 8 */
69 #define LCD_BUF rb->lcd_framebuffer
70 #define MYLCD(fn) rb->lcd_ ## fn
71 #define G_PIX LCD_RGBPACK
72 #define N_PIX LCD_RGBPACK
73 #define G_BRIGHT(y) LCD_RGBPACK(y,y,y)
74 #define N_BRIGHT(y) LCD_RGBPACK(y,y,y)
75 #define BUFFER_WIDTH LCD_WIDTH
76 #define BUFFER_HEIGHT LCD_HEIGHT
77 typedef fb_data pix_t;
78 #endif /* LCD_DEPTH >= 8 */
80 #ifdef HAVE_SCROLLWHEEL
81 #define PICTUREFLOW_NEXT_ALBUM PLA_DOWN
82 #define PICTUREFLOW_NEXT_ALBUM_REPEAT PLA_DOWN_REPEAT
83 #define PICTUREFLOW_PREV_ALBUM PLA_UP
84 #define PICTUREFLOW_PREV_ALBUM_REPEAT PLA_UP_REPEAT
85 #else
86 #define PICTUREFLOW_NEXT_ALBUM PLA_RIGHT
87 #define PICTUREFLOW_NEXT_ALBUM_REPEAT PLA_RIGHT_REPEAT
88 #define PICTUREFLOW_PREV_ALBUM PLA_LEFT
89 #define PICTUREFLOW_PREV_ALBUM_REPEAT PLA_LEFT_REPEAT
90 #define PICTUREFLOW_NEXT_TRACK PLA_DOWN
91 #define PICTUREFLOW_NEXT_TRACK_REPEAT PLA_DOWN_REPEAT
92 #define PICTUREFLOW_PREV_TRACK PLA_UP
93 #define PICTUREFLOW_PREV_TRACK_REPEAT PLA_UP_REPEAT
94 #endif
95 #define PICTUREFLOW_MENU PLA_MENU
96 #define PICTUREFLOW_QUIT PLA_QUIT
97 #define PICTUREFLOW_SELECT_ALBUM PLA_FIRE
100 /* for fixed-point arithmetic, we need minimum 32-bit long
101 long long (64-bit) might be useful for multiplication and division */
102 #define PFreal long
103 #define PFREAL_SHIFT 10
104 #define PFREAL_FACTOR (1 << PFREAL_SHIFT)
105 #define PFREAL_ONE (1 << PFREAL_SHIFT)
106 #define PFREAL_HALF (PFREAL_ONE >> 1)
109 #define IANGLE_MAX 1024
110 #define IANGLE_MASK 1023
112 #define REFLECT_TOP (LCD_HEIGHT * 2 / 3)
113 #define REFLECT_HEIGHT (LCD_HEIGHT - REFLECT_TOP)
114 #define DISPLAY_HEIGHT REFLECT_TOP
115 #define DISPLAY_WIDTH MAX((LCD_HEIGHT * LCD_PIXEL_ASPECT_HEIGHT / \
116 LCD_PIXEL_ASPECT_WIDTH / 2), (LCD_WIDTH * 2 / 5))
117 #define REFLECT_SC ((0x10000U * 3 + (REFLECT_HEIGHT * 5 - 1)) / \
118 (REFLECT_HEIGHT * 5))
119 #define DISPLAY_OFFS ((LCD_HEIGHT / 2) - REFLECT_HEIGHT)
120 #define CAM_DIST MAX(MIN(LCD_HEIGHT,LCD_WIDTH),120)
121 #define CAM_DIST_R (CAM_DIST << PFREAL_SHIFT)
122 #define DISPLAY_LEFT_R (PFREAL_HALF - LCD_WIDTH * PFREAL_HALF)
123 #define MAXSLIDE_LEFT_R (PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF)
125 #define SLIDE_CACHE_SIZE 64 /* probably more than can be loaded */
127 #define MAX_SLIDES_COUNT 10
129 #define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
130 #define CACHE_PREFIX PLUGIN_DEMOS_DIR "/pictureflow"
132 #define EV_EXIT 9999
133 #define EV_WAKEUP 1337
135 /* maximum number of albums */
137 #define MAX_TRACKS 50
138 #define AVG_TRACK_NAME_LENGTH 20
141 #define UNIQBUF_SIZE (64*1024)
143 #define EMPTY_SLIDE CACHE_PREFIX "/emptyslide.pfraw"
144 #define EMPTY_SLIDE_BMP PLUGIN_DEMOS_DIR "/pictureflow_emptyslide.bmp"
146 /* Error return values */
147 #define ERROR_NO_ALBUMS -1
148 #define ERROR_BUFFER_FULL -2
150 /* current version for cover cache */
151 #define CACHE_VERSION 2
152 #define CONFIG_VERSION 1
153 #define CONFIG_FILE "pictureflow.cfg"
155 /** structs we use */
157 struct slide_data {
158 int slide_index;
159 int angle;
160 PFreal cx;
161 PFreal cy;
162 PFreal distance;
165 struct slide_cache {
166 int index; /* index of the cached slide */
167 int hid; /* handle ID of the cached slide */
168 short next; /* "next" slide, with LRU last */
169 short prev; /* "previous" slide */
172 struct album_data {
173 int name_idx;
174 long seek;
177 struct track_data {
178 int name_idx;
179 long seek;
182 struct rect {
183 int left;
184 int right;
185 int top;
186 int bottom;
189 struct load_slide_event_data {
190 int slide_index;
191 int cache_index;
195 struct pfraw_header {
196 int32_t width; /* bmap width in pixels */
197 int32_t height; /* bmap height in pixels */
200 const struct picture logos[]={
201 {pictureflow_logo, BMPWIDTH_pictureflow_logo, BMPHEIGHT_pictureflow_logo},
204 enum show_album_name_values { album_name_hide = 0, album_name_bottom,
205 album_name_top };
206 static char* show_album_name_conf[] =
208 "hide",
209 "bottom",
210 "top"
213 #define MAX_SPACING 40
214 #define MAX_MARGIN 80
216 /* config values and their defaults */
217 static int slide_spacing = DISPLAY_WIDTH / 4;
218 static int center_margin = (LCD_WIDTH - DISPLAY_WIDTH) / 12;
219 static int num_slides = 4;
220 static int zoom = 100;
221 static bool show_fps = false;
222 static bool resize = true;
223 static int cache_version = 0;
224 static int show_album_name = (LCD_HEIGHT > 100)
225 ? album_name_top : album_name_bottom;
227 static struct configdata config[] =
229 { TYPE_INT, 0, MAX_SPACING, { .int_p = &slide_spacing }, "slide spacing",
230 NULL },
231 { TYPE_INT, 0, MAX_MARGIN, { .int_p = &center_margin }, "center margin",
232 NULL },
233 { TYPE_INT, 0, MAX_SLIDES_COUNT, { .int_p = &num_slides }, "slides count",
234 NULL },
235 { TYPE_INT, 0, 300, { .int_p = &zoom }, "zoom", NULL },
236 { TYPE_BOOL, 0, 1, { .bool_p = &show_fps }, "show fps", NULL },
237 { TYPE_BOOL, 0, 1, { .bool_p = &resize }, "resize", NULL },
238 { TYPE_INT, 0, 100, { .int_p = &cache_version }, "cache version", NULL },
239 { TYPE_ENUM, 0, 2, { .int_p = &show_album_name }, "show album name",
240 show_album_name_conf }
243 #define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata))
245 /** below we allocate the memory we want to use **/
247 static pix_t *buffer; /* for now it always points to the lcd framebuffer */
248 static uint8_t reflect_table[REFLECT_HEIGHT];
249 static struct slide_data center_slide;
250 static struct slide_data left_slides[MAX_SLIDES_COUNT];
251 static struct slide_data right_slides[MAX_SLIDES_COUNT];
252 static int slide_frame;
253 static int step;
254 static int target;
255 static int fade;
256 static int center_index = 0; /* index of the slide that is in the center */
257 static int itilt;
258 static PFreal offsetX;
259 static PFreal offsetY;
260 static int number_of_slides;
262 static struct slide_cache cache[SLIDE_CACHE_SIZE];
263 static int cache_free;
264 static int cache_used = -1;
265 static int cache_left_index = -1;
266 static int cache_right_index = -1;
267 static int cache_center_index = -1;
269 /* use long for aligning */
270 unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)];
271 /* queue (as array) for scheduling load_surface */
273 static int empty_slide_hid;
275 unsigned int thread_id;
276 struct event_queue thread_q;
278 static struct tagcache_search tcs;
280 static struct buflib_context buf_ctx;
282 static struct album_data *album;
283 static char *album_names;
284 static int album_count;
286 static char track_names[MAX_TRACKS * AVG_TRACK_NAME_LENGTH];
287 static struct track_data tracks[MAX_TRACKS];
288 static int track_count;
289 static int track_index;
290 static int selected_track;
291 static int selected_track_pulse;
292 void reset_track_list(void);
294 void * buf;
295 size_t buf_size;
297 static int old_drawmode;
299 static bool thread_is_running;
301 static int cover_animation_keyframe;
302 static int extra_fade;
304 static int albumtxt_x = 0;
305 static int albumtxt_dir = -1;
306 static int prev_center_index = -1;
308 static int start_index_track_list = 0;
309 static int track_list_visible_entries = 0;
310 static int track_list_y;
311 static int track_list_h;
312 static int track_scroll_index = 0;
313 static int track_scroll_dir = 1;
316 Proposals for transitions:
318 pf_idle -> pf_scrolling : NEXT_ALBUM/PREV_ALBUM pressed
319 -> pf_cover_in -> pf_show_tracks : SELECT_ALBUM clicked
321 pf_scrolling -> pf_idle : NEXT_ALBUM/PREV_ALBUM released
323 pf_show_tracks -> pf_cover_out -> pf_idle : SELECT_ALBUM pressed
325 TODO:
326 pf_show_tracks -> pf_cover_out -> pf_idle : MENU_PRESSED pressed
327 pf_show_tracks -> play_track() -> exit() : SELECT_ALBUM pressed
329 pf_idle, pf_scrolling -> show_menu(): MENU_PRESSED
331 enum pf_states {
332 pf_idle = 0,
333 pf_scrolling,
334 pf_cover_in,
335 pf_show_tracks,
336 pf_cover_out
339 static int pf_state;
341 /** code */
342 static inline unsigned fade_color(pix_t c, unsigned a);
343 bool save_pfraw(char* filename, struct bitmap *bm);
344 bool load_new_slide(void);
345 int load_surface(int);
347 static inline PFreal fmul(PFreal a, PFreal b)
349 return (a*b) >> PFREAL_SHIFT;
353 * This version preshifts each operand, which is useful when we know how many
354 * of the least significant bits will be empty, or are worried about overflow
355 * in a particular calculation
357 static inline PFreal fmuln(PFreal a, PFreal b, int ps1, int ps2)
359 return ((a >> ps1) * (b >> ps2)) >> (PFREAL_SHIFT - ps1 - ps2);
362 /* ARMv5+ has a clz instruction equivalent to our function.
364 #if (defined(CPU_ARM) && (ARM_ARCH > 4))
365 static inline int clz(uint32_t v)
367 return __builtin_clz(v);
370 /* Otherwise, use our clz, which can be inlined */
371 #elif defined(CPU_COLDFIRE)
372 /* This clz is based on the log2(n) implementation at
373 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
374 * A clz benchmark plugin showed this to be about 14% faster on coldfire
375 * than the LUT-based version.
377 static inline int clz(uint32_t v)
379 int r = 32;
380 if (v >= 0x10000)
382 v >>= 16;
383 r -= 16;
385 if (v & 0xff00)
387 v >>= 8;
388 r -= 8;
390 if (v & 0xf0)
392 v >>= 4;
393 r -= 4;
395 if (v & 0xc)
397 v >>= 2;
398 r -= 2;
400 if (v & 2)
402 v >>= 1;
403 r -= 1;
405 r -= v;
406 return r;
408 #else
409 static const char clz_lut[16] = { 4, 3, 2, 2, 1, 1, 1, 1,
410 0, 0, 0, 0, 0, 0, 0, 0 };
411 /* This clz is based on the log2(n) implementation at
412 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
413 * It is not any faster than the one above, but trades 16B in the lookup table
414 * for a savings of 12B per each inlined call.
416 static inline int clz(uint32_t v)
418 int r = 28;
419 if (v >= 0x10000)
421 v >>= 16;
422 r -= 16;
424 if (v & 0xff00)
426 v >>= 8;
427 r -= 8;
429 if (v & 0xf0)
431 v >>= 4;
432 r -= 4;
434 return r + clz_lut[v];
436 #endif
438 /* Return the maximum possible left shift for a signed int32, without
439 * overflow
441 static inline int allowed_shift(int32_t val)
443 uint32_t uval = val ^ (val >> 31);
444 return clz(uval) - 1;
447 /* Calculate num/den, with the result shifted left by PFREAL_SHIFT, by shifting
448 * num and den before dividing.
450 static inline PFreal fdiv(PFreal num, PFreal den)
452 int shift = allowed_shift(num);
453 shift = MIN(PFREAL_SHIFT, shift);
454 num <<= shift;
455 den >>= PFREAL_SHIFT - shift;
456 return num / den;
459 #define fmin(a,b) (((a) < (b)) ? (a) : (b))
460 #define fmax(a,b) (((a) > (b)) ? (a) : (b))
461 #define fabs(a) (a < 0 ? -a : a)
462 #define fbound(min,val,max) (fmax((min),fmin((max),(val))))
464 #if CONFIG_CPU == SH7034
465 /* 16*16->32 bit multiplication is a single instrcution on the SH1 */
466 #define MULUQ(a, b) ((uint32_t) (((uint16_t) (a)) * ((uint16_t) (b))))
467 #else
468 #define MULUQ(a, b) ((a) * (b))
469 #endif
472 #if 0
473 #define fmul(a,b) ( ((a)*(b)) >> PFREAL_SHIFT )
474 #define fdiv(n,m) ( ((n)<< PFREAL_SHIFT ) / m )
476 #define fconv(a, q1, q2) (((q2)>(q1)) ? (a)<<((q2)-(q1)) : (a)>>((q1)-(q2)))
477 #define tofloat(a, q) ( (float)(a) / (float)(1<<(q)) )
479 static inline PFreal fmul(PFreal a, PFreal b)
481 return (a*b) >> PFREAL_SHIFT;
484 static inline PFreal fdiv(PFreal n, PFreal m)
486 return (n<<(PFREAL_SHIFT))/m;
488 #endif
490 /* warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! */
491 static const short sin_tab[] = {
492 0, 100, 200, 297, 392, 483, 569, 650,
493 724, 792, 851, 903, 946, 980, 1004, 1019,
494 1024, 1019, 1004, 980, 946, 903, 851, 792,
495 724, 650, 569, 483, 392, 297, 200, 100,
496 0, -100, -200, -297, -392, -483, -569, -650,
497 -724, -792, -851, -903, -946, -980, -1004, -1019,
498 -1024, -1019, -1004, -980, -946, -903, -851, -792,
499 -724, -650, -569, -483, -392, -297, -200, -100,
503 static inline PFreal fsin(int iangle)
505 while(iangle < 0)
506 iangle += IANGLE_MAX;
507 iangle &= IANGLE_MASK;
509 int i = (iangle >> 4);
510 PFreal p = sin_tab[i];
511 PFreal q = sin_tab[(i+1)];
512 PFreal g = (q - p);
513 return p + g * (iangle-i*16)/16;
516 static inline PFreal fcos(int iangle)
518 return fsin(iangle + (IANGLE_MAX >> 2));
521 static inline uint32_t div255(uint32_t val)
523 return ((((val >> 8) + val) >> 8) + val) >> 8;
526 #define SCALE_VAL(val,out) div255((val) * (out) + 127)
528 static void output_row_transposed(uint32_t row, void * row_in,
529 struct scaler_context *ctx)
531 pix_t *dest = (pix_t*)ctx->bm->data + row;
532 pix_t *end = dest + ctx->bm->height * ctx->bm->width;
533 #ifdef USEGSLIB
534 uint32_t *qp = (uint32_t*)row_in;
535 for (; dest < end; dest += ctx->bm->height)
536 *dest = SC_MUL((*qp++) + ctx->round, ctx->divisor);
537 #else
538 struct uint32_rgb *qp = (struct uint32_rgb*)row_in;
539 uint32_t rb_mul = SCALE_VAL(ctx->divisor, 31),
540 rb_rnd = SCALE_VAL(ctx->round, 31),
541 g_mul = SCALE_VAL(ctx->divisor, 63),
542 g_rnd = SCALE_VAL(ctx->round, 63);
543 int r, g, b;
544 for (; dest < end; dest += ctx->bm->height)
546 r = SC_MUL(qp->r + rb_rnd, rb_mul);
547 g = SC_MUL(qp->g + g_rnd, g_mul);
548 b = SC_MUL(qp->b + rb_rnd, rb_mul);
549 qp++;
550 *dest = LCD_RGBPACK_LCD(r,g,b);
552 #endif
555 static unsigned int get_size(struct bitmap *bm)
557 return bm->width * bm->height * sizeof(pix_t);
560 const struct custom_format format_transposed = {
561 .output_row = output_row_transposed,
562 .get_size = get_size
565 /* Create the lookup table with the scaling values for the reflections */
566 void init_reflect_table(void)
568 int i;
569 for (i = 0; i < REFLECT_HEIGHT; i++)
570 reflect_table[i] =
571 (768 * (REFLECT_HEIGHT - i) + (5 * REFLECT_HEIGHT / 2)) /
572 (5 * REFLECT_HEIGHT);
576 Create an index of all albums from the database.
577 Also store the album names so we can access them later.
579 int create_album_index(void)
581 buf_size -= UNIQBUF_SIZE * sizeof(long);
582 long *uniqbuf = (long *)(buf_size + (char *)buf);
583 album = ((struct album_data *)uniqbuf) - 1;
584 rb->memset(&tcs, 0, sizeof(struct tagcache_search) );
585 album_count = 0;
586 rb->tagcache_search(&tcs, tag_album);
587 rb->tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE);
588 unsigned int l, old_l = 0;
589 album_names = buf;
590 album[0].name_idx = 0;
591 while (rb->tagcache_get_next(&tcs))
593 buf_size -= sizeof(struct album_data);
594 l = rb->strlen(tcs.result) + 1;
595 if ( album_count > 0 )
596 album[-album_count].name_idx = album[1-album_count].name_idx + old_l;
598 if ( l > buf_size )
599 /* not enough memory */
600 return ERROR_BUFFER_FULL;
602 rb->strcpy(buf, tcs.result);
603 buf_size -= l;
604 buf = l + (char *)buf;
605 album[-album_count].seek = tcs.result_seek;
606 old_l = l;
607 album_count++;
609 rb->tagcache_search_finish(&tcs);
610 ALIGN_BUFFER(buf, buf_size, 4);
611 int i;
612 struct album_data* tmp_album = (struct album_data*)buf;
613 for (i = album_count - 1; i >= 0; i--)
614 tmp_album[i] = album[-i];
615 album = tmp_album;
616 buf = album + album_count;
617 buf_size += UNIQBUF_SIZE * sizeof(long);
618 return (album_count > 0) ? 0 : ERROR_NO_ALBUMS;
622 Return a pointer to the album name of the given slide_index
624 char* get_album_name(const int slide_index)
626 return album_names + album[slide_index].name_idx;
630 Return a pointer to the track name of the active album
631 create_track_index has to be called first.
633 char* get_track_name(const int track_index)
635 if ( track_index < track_count )
636 return track_names + tracks[track_index].name_idx;
637 return 0;
641 Create the track index of the given slide_index.
643 int create_track_index(const int slide_index)
645 if ( slide_index == track_index ) {
646 return -1;
649 if (!rb->tagcache_search(&tcs, tag_title))
650 return -1;
652 int ret = 0;
653 char temp_titles[MAX_TRACKS][AVG_TRACK_NAME_LENGTH*4];
654 int temp_seeks[MAX_TRACKS];
656 rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek);
657 track_count=0;
658 int string_index = 0;
659 int l, track_num, heighest_index = 0;
661 for(l=0;l<MAX_TRACKS;l++)
662 temp_titles[l][0] = '\0';
663 while (rb->tagcache_get_next(&tcs) && track_count < MAX_TRACKS)
665 track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber) - 1;
666 if (track_num >= 0)
668 rb->snprintf(temp_titles[track_num],sizeof(temp_titles[track_num]),
669 "%d: %s", track_num+1, tcs.result);
670 temp_seeks[track_num] = tcs.result_seek;
672 else
674 track_num = 0;
675 while (temp_titles[track_num][0] != '\0')
676 track_num++;
677 rb->strcpy(temp_titles[track_num], tcs.result);
678 temp_seeks[track_num] = tcs.result_seek;
680 if (track_num > heighest_index)
681 heighest_index = track_num;
682 track_count++;
685 rb->tagcache_search_finish(&tcs);
686 track_index = slide_index;
688 /* now fix the track list order */
689 l = 0;
690 track_count = 0;
691 while (l <= heighest_index &&
692 string_index < MAX_TRACKS*AVG_TRACK_NAME_LENGTH)
694 if (temp_titles[l][0] != '\0')
696 rb->strcpy(track_names + string_index, temp_titles[l]);
697 tracks[track_count].name_idx = string_index;
698 tracks[track_count].seek = temp_seeks[l];
699 string_index += rb->strlen(temp_titles[l]) + 1;
700 track_count++;
702 l++;
704 if (ret != 0)
705 return ret;
706 else
707 return (track_count > 0) ? 0 : -1;
711 Determine filename of the album art for the given slide_index and
712 store the result in buf.
713 The algorithm looks for the first track of the given album uses
714 find_albumart to find the filename.
716 bool get_albumart_for_index_from_db(const int slide_index, char *buf,
717 int buflen)
719 if ( slide_index == -1 )
721 rb->strncpy( buf, EMPTY_SLIDE, buflen );
724 if (!rb->tagcache_search(&tcs, tag_filename))
725 return false;
727 bool result;
728 /* find the first track of the album */
729 rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek);
731 if ( rb->tagcache_get_next(&tcs) ) {
732 struct mp3entry id3;
733 int fd;
735 fd = rb->open(tcs.result, O_RDONLY);
736 rb->get_metadata(&id3, fd, tcs.result);
737 rb->close(fd);
738 if ( search_albumart_files(&id3, "", buf, buflen) )
739 result = true;
740 else
741 result = false;
743 else {
744 /* did not find a matching track */
745 result = false;
747 rb->tagcache_search_finish(&tcs);
748 return result;
752 Draw the PictureFlow logo
754 void draw_splashscreen(void)
756 struct screen* display = rb->screens[0];
757 const struct picture* logo = &(logos[display->screen_type]);
759 #if LCD_DEPTH > 1
760 rb->lcd_set_background(N_BRIGHT(0));
761 rb->lcd_set_foreground(N_BRIGHT(255));
762 #else
763 rb->lcd_set_drawmode(PICTUREFLOW_DRMODE);
764 #endif
765 rb->lcd_clear_display();
767 #if LCD_DEPTH == 1 /* Mono LCDs need the logo inverted */
768 rb->lcd_set_drawmode(PICTUREFLOW_DRMODE ^ DRMODE_INVERSEVID);
769 picture_draw(display, logo, (LCD_WIDTH - logo->width) / 2, 10);
770 rb->lcd_set_drawmode(PICTUREFLOW_DRMODE);
771 #else
772 picture_draw(display, logo, (LCD_WIDTH - logo->width) / 2, 10);
773 #endif
775 rb->lcd_update();
780 Draw a simple progress bar
782 void draw_progressbar(int step)
784 int txt_w, txt_h;
785 const int bar_height = 22;
786 const int w = LCD_WIDTH - 20;
787 const int x = 10;
789 rb->lcd_getstringsize("Preparing album artwork", &txt_w, &txt_h);
791 int y = (LCD_HEIGHT - txt_h)/2;
793 rb->lcd_putsxy((LCD_WIDTH - txt_w)/2, y, "Preparing album artwork");
794 y += (txt_h + 5);
796 #if LCD_DEPTH > 1
797 rb->lcd_set_foreground(N_BRIGHT(100));
798 #endif
799 rb->lcd_drawrect(x, y, w+2, bar_height);
800 #if LCD_DEPTH > 1
801 rb->lcd_set_foreground(N_PIX(165, 231, 82));
802 #endif
804 rb->lcd_fillrect(x+1, y+1, step * w / album_count, bar_height-2);
805 #if LCD_DEPTH > 1
806 rb->lcd_set_foreground(N_BRIGHT(255));
807 #endif
808 rb->lcd_update();
809 rb->yield();
813 Precomupte the album art images and store them in CACHE_PREFIX.
815 bool create_albumart_cache(void)
817 int ret;
819 int i, slides = 0;
820 struct bitmap input_bmp;
822 char pfraw_file[MAX_PATH];
823 char albumart_file[MAX_PATH];
824 unsigned int format = FORMAT_NATIVE;
825 cache_version = 0;
826 configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
827 if (resize)
828 format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT;
829 for (i=0; i < album_count; i++)
831 rb->snprintf(pfraw_file, sizeof(pfraw_file), CACHE_PREFIX "/%d.pfraw",
833 /* delete existing cache, so it's a true rebuild */
834 if(rb->file_exists(pfraw_file))
835 rb->remove(pfraw_file);
836 draw_progressbar(i);
837 if (!get_albumart_for_index_from_db(i, albumart_file, MAX_PATH))
838 continue;
840 input_bmp.data = buf;
841 input_bmp.width = DISPLAY_WIDTH;
842 input_bmp.height = DISPLAY_HEIGHT;
843 ret = scaled_read_bmp_file(albumart_file, &input_bmp,
844 buf_size, format, &format_transposed);
845 if (ret <= 0) {
846 rb->splash(HZ, "Could not read bmp");
847 continue; /* skip missing/broken files */
849 if (!save_pfraw(pfraw_file, &input_bmp))
851 rb->splash(HZ, "Could not write bmp");
853 slides++;
854 if ( rb->button_get(false) == PICTUREFLOW_MENU ) return false;
856 if ( slides == 0 ) {
857 /* Warn the user that we couldn't find any albumart */
858 rb->splash(2*HZ, "No album art found");
859 return false;
861 return true;
865 Thread used for loading and preparing bitmaps in the background
867 void thread(void)
869 long sleep_time = 5 * HZ;
870 struct queue_event ev;
871 while (1) {
872 rb->queue_wait_w_tmo(&thread_q, &ev, sleep_time);
873 switch (ev.id) {
874 case EV_EXIT:
875 return;
876 case EV_WAKEUP:
877 /* we just woke up */
878 break;
880 while ( load_new_slide() ) {
881 rb->yield();
882 switch (ev.id) {
883 case EV_EXIT:
884 return;
892 End the thread by posting the EV_EXIT event
894 void end_pf_thread(void)
896 if ( thread_is_running ) {
897 rb->queue_post(&thread_q, EV_EXIT, 0);
898 rb->thread_wait(thread_id);
899 /* remove the thread's queue from the broadcast list */
900 rb->queue_delete(&thread_q);
901 thread_is_running = false;
908 Create the thread an setup the event queue
910 bool create_pf_thread(void)
912 /* put the thread's queue in the bcast list */
913 rb->queue_init(&thread_q, true);
914 if ((thread_id = rb->create_thread(
915 thread,
916 thread_stack,
917 sizeof(thread_stack),
919 "Picture load thread"
920 IF_PRIO(, MAX(PRIORITY_USER_INTERFACE / 2,
921 PRIORITY_REALTIME + 1))
922 IF_COP(, CPU)
924 ) == 0) {
925 return false;
927 thread_is_running = true;
928 rb->queue_post(&thread_q, EV_WAKEUP, 0);
929 return true;
933 Safe the given bitmap as filename in the pfraw format
935 bool save_pfraw(char* filename, struct bitmap *bm)
937 struct pfraw_header bmph;
938 bmph.width = bm->width;
939 bmph.height = bm->height;
940 int fh = rb->creat( filename );
941 if( fh < 0 ) return false;
942 rb->write( fh, &bmph, sizeof( struct pfraw_header ) );
943 int y;
944 for( y = 0; y < bm->height; y++ )
946 pix_t *d = (pix_t*)( bm->data ) + (y*bm->width);
947 rb->write( fh, d, sizeof( pix_t ) * bm->width );
949 rb->close( fh );
950 return true;
955 * The following functions implement the linked-list-in-array used to manage
956 * the LRU cache of slides, and the list of free cache slots.
959 #define seek_right_while(start, cond) \
960 ({ \
961 int ind_, next_ = (start); \
962 do { \
963 ind_ = next_; \
964 next_ = cache[ind_].next; \
965 } while (next_ != cache_used && (cond)); \
966 ind_; \
969 #define seek_left_while(start, cond) \
970 ({ \
971 int ind_, next_ = (start); \
972 do { \
973 ind_ = next_; \
974 next_ = cache[ind_].prev; \
975 } while (ind_ != cache_used && (cond)); \
976 ind_; \
980 Pop the given item from the linked list starting at *head, returning the next
981 item, or -1 if the list is now empty.
983 static inline int lla_pop_item (int *head, int i)
985 int prev = cache[i].prev;
986 int next = cache[i].next;
987 if (i == next)
989 *head = -1;
990 return -1;
992 else if (i == *head)
993 *head = next;
994 cache[next].prev = prev;
995 cache[prev].next = next;
996 return next;
1001 Pop the head item from the list starting at *head, returning the index of the
1002 item, or -1 if the list is already empty.
1004 static inline int lla_pop_head (int *head)
1006 int i = *head;
1007 if (i != -1)
1008 lla_pop_item(head, i);
1009 return i;
1013 Insert the item at index i before the one at index p.
1015 static inline void lla_insert (int i, int p)
1017 int next = p;
1018 int prev = cache[next].prev;
1019 cache[next].prev = i;
1020 cache[prev].next = i;
1021 cache[i].next = next;
1022 cache[i].prev = prev;
1027 Insert the item at index i at the end of the list starting at *head.
1029 static inline void lla_insert_tail (int *head, int i)
1031 if (*head == -1)
1033 *head = i;
1034 cache[i].next = i;
1035 cache[i].prev = i;
1036 } else
1037 lla_insert(i, *head);
1041 Insert the item at index i before the one at index p.
1043 static inline void lla_insert_after(int i, int p)
1045 p = cache[p].next;
1046 lla_insert(i, p);
1051 Insert the item at index i before the one at index p in the list starting at
1052 *head
1054 static inline void lla_insert_before(int *head, int i, int p)
1056 lla_insert(i, p);
1057 if (*head == p)
1058 *head = i;
1063 Free the used slide at index i, and its buffer, and move it to the free
1064 slides list.
1066 static inline void free_slide(int i)
1068 if (cache[i].hid != empty_slide_hid)
1069 buflib_free(&buf_ctx, cache[i].hid);
1070 cache[i].index = -1;
1071 lla_pop_item(&cache_used, i);
1072 lla_insert_tail(&cache_free, i);
1073 if (cache_used == -1)
1075 cache_right_index = -1;
1076 cache_left_index = -1;
1077 cache_center_index = -1;
1083 Free one slide ranked above the given priority. If no such slide can be found,
1084 return false.
1086 static inline int free_slide_prio(int prio)
1088 if (cache_used == -1)
1089 return false;
1090 int i, l = cache_used, r = cache[cache_used].prev, prio_max;
1091 int prio_l = cache[l].index < center_index ?
1092 center_index - cache[l].index : 0;
1093 int prio_r = cache[r].index > center_index ?
1094 cache[r].index - center_index : 0;
1095 if (prio_l > prio_r)
1097 i = l;
1098 prio_max = prio_l;
1099 } else {
1100 i = r;
1101 prio_max = prio_r;
1103 if (prio_max > prio)
1105 if (i == cache_left_index)
1106 cache_left_index = cache[i].next;
1107 if (i == cache_right_index)
1108 cache_right_index = cache[i].prev;
1109 free_slide(i);
1110 return true;
1111 } else
1112 return false;
1116 Read the pfraw image given as filename and return the hid of the buffer
1118 int read_pfraw(char* filename, int prio)
1120 struct pfraw_header bmph;
1121 int fh = rb->open(filename, O_RDONLY);
1122 if( fh < 0 )
1123 return empty_slide_hid;
1124 else
1125 rb->read(fh, &bmph, sizeof(struct pfraw_header));
1127 int size = sizeof(struct bitmap) + sizeof( pix_t ) *
1128 bmph.width * bmph.height;
1130 int hid;
1131 while (!(hid = buflib_alloc(&buf_ctx, size)) && free_slide_prio(prio));
1133 if (!hid) {
1134 rb->close( fh );
1135 return 0;
1138 struct dim *bm = buflib_get_data(&buf_ctx, hid);
1140 bm->width = bmph.width;
1141 bm->height = bmph.height;
1142 pix_t *data = (pix_t*)(sizeof(struct dim) + (char *)bm);
1144 int y;
1145 for( y = 0; y < bm->height; y++ )
1147 rb->read( fh, data , sizeof( pix_t ) * bm->width );
1148 data += bm->width;
1150 rb->close( fh );
1151 return hid;
1156 Load the surface for the given slide_index into the cache at cache_index.
1158 static inline bool load_and_prepare_surface(const int slide_index,
1159 const int cache_index,
1160 const int prio)
1162 char tmp_path_name[MAX_PATH+1];
1163 rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw",
1164 slide_index);
1166 int hid = read_pfraw(tmp_path_name, prio);
1167 if (!hid)
1168 return false;
1170 cache[cache_index].hid = hid;
1172 if ( cache_index < SLIDE_CACHE_SIZE ) {
1173 cache[cache_index].index = slide_index;
1176 return true;
1181 Load the "next" slide that we can load, freeing old slides if needed, provided
1182 that they are further from center_index than the current slide
1184 bool load_new_slide(void)
1186 int i = -1;
1187 if (cache_center_index != -1)
1189 int next, prev;
1190 if (cache[cache_center_index].index != center_index)
1192 if (cache[cache_center_index].index < center_index)
1194 cache_center_index = seek_right_while(cache_center_index,
1195 cache[next_].index <= center_index);
1196 prev = cache_center_index;
1197 next = cache[cache_center_index].next;
1199 else
1201 cache_center_index = seek_left_while(cache_center_index,
1202 cache[next_].index >= center_index);
1203 next = cache_center_index;
1204 prev = cache[cache_center_index].prev;
1206 if (cache[cache_center_index].index != center_index)
1208 if (cache_free == -1)
1209 free_slide_prio(0);
1210 i = lla_pop_head(&cache_free);
1211 if (!load_and_prepare_surface(center_index, i, 0))
1212 goto fail_and_refree;
1213 if (cache[next].index == -1)
1215 if (cache[prev].index == -1)
1216 goto insert_first_slide;
1217 else
1218 next = cache[prev].next;
1220 lla_insert(i, next);
1221 if (cache[i].index < cache[cache_used].index)
1222 cache_used = i;
1223 cache_center_index = i;
1224 cache_left_index = i;
1225 cache_right_index = i;
1226 return true;
1229 if (cache[cache_left_index].index >
1230 cache[cache_center_index].index)
1231 cache_left_index = cache_center_index;
1232 if (cache[cache_right_index].index <
1233 cache[cache_center_index].index)
1234 cache_right_index = cache_center_index;
1235 cache_left_index = seek_left_while(cache_left_index,
1236 cache[ind_].index - 1 == cache[next_].index);
1237 cache_right_index = seek_right_while(cache_right_index,
1238 cache[ind_].index - 1 == cache[next_].index);
1239 int prio_l = cache[cache_center_index].index -
1240 cache[cache_left_index].index + 1;
1241 int prio_r = cache[cache_right_index].index -
1242 cache[cache_center_index].index + 1;
1243 if ((prio_l < prio_r ||
1244 cache[cache_right_index].index >= number_of_slides) &&
1245 cache[cache_left_index].index > 0)
1247 if (cache_free == -1 && !free_slide_prio(prio_l))
1248 return false;
1249 i = lla_pop_head(&cache_free);
1250 if (load_and_prepare_surface(cache[cache_left_index].index
1251 - 1, i, prio_l))
1253 lla_insert_before(&cache_used, i, cache_left_index);
1254 cache_left_index = i;
1255 return true;
1257 } else if(cache[cache_right_index].index < number_of_slides - 1)
1259 if (cache_free == -1 && !free_slide_prio(prio_r))
1260 return false;
1261 i = lla_pop_head(&cache_free);
1262 if (load_and_prepare_surface(cache[cache_right_index].index
1263 + 1, i, prio_r))
1265 lla_insert_after(i, cache_right_index);
1266 cache_right_index = i;
1267 return true;
1270 } else {
1271 i = lla_pop_head(&cache_free);
1272 if (load_and_prepare_surface(center_index, i, 0))
1274 insert_first_slide:
1275 cache[i].next = i;
1276 cache[i].prev = i;
1277 cache_center_index = i;
1278 cache_left_index = i;
1279 cache_right_index = i;
1280 cache_used = i;
1281 return true;
1284 fail_and_refree:
1285 if (i != -1)
1287 lla_insert_tail(&cache_free, i);
1289 return false;
1294 Get a slide from the buffer
1296 static inline struct dim *get_slide(const int hid)
1298 if (!hid)
1299 return NULL;
1301 struct dim *bmp;
1303 bmp = buflib_get_data(&buf_ctx, hid);
1305 return bmp;
1310 Return the requested surface
1312 static inline struct dim *surface(const int slide_index)
1314 if (slide_index < 0)
1315 return 0;
1316 if (slide_index >= number_of_slides)
1317 return 0;
1318 int i;
1319 if ((i = cache_used ) != -1)
1321 do {
1322 if (cache[i].index == slide_index)
1323 return get_slide(cache[i].hid);
1324 i = cache[i].next;
1325 } while (i != cache_used);
1327 return get_slide(empty_slide_hid);
1331 adjust slides so that they are in "steady state" position
1333 void reset_slides(void)
1335 center_slide.angle = 0;
1336 center_slide.cx = 0;
1337 center_slide.cy = 0;
1338 center_slide.distance = 0;
1339 center_slide.slide_index = center_index;
1341 int i;
1342 for (i = 0; i < num_slides; i++) {
1343 struct slide_data *si = &left_slides[i];
1344 si->angle = itilt;
1345 si->cx = -(offsetX + slide_spacing * i * PFREAL_ONE);
1346 si->cy = offsetY;
1347 si->slide_index = center_index - 1 - i;
1348 si->distance = 0;
1351 for (i = 0; i < num_slides; i++) {
1352 struct slide_data *si = &right_slides[i];
1353 si->angle = -itilt;
1354 si->cx = offsetX + slide_spacing * i * PFREAL_ONE;
1355 si->cy = offsetY;
1356 si->slide_index = center_index + 1 + i;
1357 si->distance = 0;
1363 Updates look-up table and other stuff necessary for the rendering.
1364 Call this when the viewport size or slide dimension is changed.
1366 * To calculate the offset that will provide the proper margin, we use the same
1367 * projection used to render the slides. The solution for xc, the slide center,
1368 * is:
1369 * xp * (zo + xs * sin(r))
1370 * xc = xp - xs * cos(r) + ───────────────────────
1372 * TODO: support moving the side slides toward or away from the camera
1374 void recalc_offsets(void)
1376 PFreal xs = PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF;
1377 PFreal zo;
1378 PFreal xp = (DISPLAY_WIDTH * PFREAL_HALF - PFREAL_HALF + center_margin *
1379 PFREAL_ONE) * zoom / 100;
1380 PFreal cosr, sinr;
1382 itilt = 70 * IANGLE_MAX / 360; /* approx. 70 degrees tilted */
1383 cosr = fcos(-itilt);
1384 sinr = fsin(-itilt);
1385 zo = CAM_DIST_R * 100 / zoom - CAM_DIST_R +
1386 fmuln(MAXSLIDE_LEFT_R, sinr, PFREAL_SHIFT - 2, 0);
1387 offsetX = xp - fmul(xs, cosr) + fmuln(xp,
1388 zo + fmuln(xs, sinr, PFREAL_SHIFT - 2, 0), PFREAL_SHIFT - 2, 0)
1389 / CAM_DIST;
1390 offsetY = DISPLAY_WIDTH / 2 * (fsin(itilt) + PFREAL_ONE / 2);
1395 Fade the given color by spreading the fb_data (ushort)
1396 to an uint, multiply and compress the result back to a ushort.
1398 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
1399 static inline unsigned fade_color(pix_t c, unsigned a)
1401 unsigned int result;
1402 c = swap16(c);
1403 a = (a + 2) & 0x1fc;
1404 result = ((c & 0xf81f) * a) & 0xf81f00;
1405 result |= ((c & 0x7e0) * a) & 0x7e000;
1406 result >>= 8;
1407 return swap16(result);
1409 #elif LCD_PIXELFORMAT == RGB565
1410 static inline unsigned fade_color(pix_t c, unsigned a)
1412 unsigned int result;
1413 a = (a + 2) & 0x1fc;
1414 result = ((c & 0xf81f) * a) & 0xf81f00;
1415 result |= ((c & 0x7e0) * a) & 0x7e000;
1416 result >>= 8;
1417 return result;
1419 #else
1420 static inline unsigned fade_color(pix_t c, unsigned a)
1422 unsigned val = c;
1423 return MULUQ(val, a) >> 8;
1425 #endif
1428 * Render a single slide
1429 * Where xc is the slide's horizontal offset from center, xs is the horizontal
1430 * on the slide from its center, zo is the slide's depth offset from the plane
1431 * of the display, r is the angle at which the slide is tilted, and xp is the
1432 * point on the display corresponding to xs on the slide, the projection
1433 * formulas are:
1435 * z * (xc + xs * cos(r))
1436 * xp = ──────────────────────
1437 * z + zo + xs * sin(r)
1439 * z * (xc - xp) - xp * zo
1440 * xs = ────────────────────────
1441 * xp * sin(r) - z * cos(r)
1443 * We use the xp projection once, to find the left edge of the slide on the
1444 * display. From there, we use the xs reverse projection to find the horizontal
1445 * offset from the slide center of each column on the screen, until we reach
1446 * the right edge of the slide, or the screen. The reverse projection can be
1447 * optimized by saving the numerator and denominator of the fraction, which can
1448 * then be incremented by (z + zo) and sin(r) respectively.
1450 void render_slide(struct slide_data *slide, const int alpha)
1452 struct dim *bmp = surface(slide->slide_index);
1453 if (!bmp) {
1454 return;
1456 if (slide->angle > 255 || slide->angle < -255)
1457 return;
1458 pix_t *src = (pix_t*)(sizeof(struct dim) + (char *)bmp);
1460 const int sw = bmp->width;
1461 const int sh = bmp->height;
1462 const PFreal slide_left = -sw * PFREAL_HALF + PFREAL_HALF;
1464 const int h = LCD_HEIGHT;
1465 const int w = LCD_WIDTH;
1468 PFreal cosr = fcos(slide->angle);
1469 PFreal sinr = fsin(slide->angle);
1470 PFreal zo = PFREAL_ONE * slide->distance + CAM_DIST_R * 100 / zoom
1471 - CAM_DIST_R - fmuln(MAXSLIDE_LEFT_R, fabs(sinr), PFREAL_SHIFT - 2, 0);
1472 PFreal xs = slide_left, xsnum, xsnumi, xsden, xsdeni;
1473 PFreal xp = fdiv(CAM_DIST * (slide->cx + fmul(xs, cosr)),
1474 (CAM_DIST_R + zo + fmul(xs,sinr)));
1476 /* Since we're finding the screen position of the left edge of the slide,
1477 * we round up.
1479 int xi = (fmax(DISPLAY_LEFT_R, xp) - DISPLAY_LEFT_R + PFREAL_ONE - 1)
1480 >> PFREAL_SHIFT;
1481 xp = DISPLAY_LEFT_R + xi * PFREAL_ONE;
1482 if (xi >= w) {
1483 return;
1485 xsnum = CAM_DIST * (slide->cx - xp) - fmuln(xp, zo, PFREAL_SHIFT - 2, 0);
1486 xsden = fmuln(xp, sinr, PFREAL_SHIFT - 2, 0) - CAM_DIST * cosr;
1487 xs = fdiv(xsnum, xsden);
1489 xsnumi = -CAM_DIST_R - zo;
1490 xsdeni = sinr;
1491 int x;
1492 int dy = PFREAL_ONE;
1493 for (x = xi; x < w; x++) {
1494 int column = (xs - slide_left) / PFREAL_ONE;
1495 if (column >= sw)
1496 break;
1497 if (zo || slide->angle)
1498 dy = (CAM_DIST_R + zo + fmul(xs, sinr)) / CAM_DIST;
1499 int y1 = (LCD_HEIGHT / 2) - 1;
1500 int y2 = y1 + 1;
1501 pix_t *pixel1 = &buffer[y1 * BUFFER_WIDTH + x];
1502 pix_t *pixel2 = pixel1 + BUFFER_WIDTH;
1503 const int pixelstep = BUFFER_WIDTH;
1505 int p1 = (bmp->height - 1 - (DISPLAY_OFFS)) * PFREAL_ONE;
1506 int p2 = p1 + dy;
1507 const pix_t *ptr = &src[column * bmp->height];
1509 if (alpha == 256)
1511 while ((y1 >= 0) && (p1 >= 0))
1513 *pixel1 = ptr[((unsigned)p1) >> PFREAL_SHIFT];
1514 p1 -= dy;
1515 y1--;
1516 pixel1 -= pixelstep;
1518 while ((p2 < sh * PFREAL_ONE) && (y2 < h))
1520 *pixel2 = ptr[((unsigned)p2) >> PFREAL_SHIFT];
1521 p2 += dy;
1522 y2++;
1523 pixel2 += pixelstep;
1525 while ((p2 < MIN(sh + REFLECT_HEIGHT, sh * 2) * PFREAL_ONE) &&
1526 (y2 < h))
1528 int ty = (((unsigned)p2) >> PFREAL_SHIFT) - sh;
1529 int lalpha = reflect_table[ty];
1530 *pixel2 = fade_color(ptr[sh - 1 - ty],lalpha);
1531 p2 += dy;
1532 y2++;
1533 pixel2 += pixelstep;
1536 else
1538 while ((y1 >= 0) && (p1 >= 0))
1540 *pixel1 = fade_color(ptr[((unsigned)p1) >> PFREAL_SHIFT],alpha);
1541 p1 -= dy;
1542 y1--;
1543 pixel1 -= pixelstep;
1545 while ((p2 < sh * PFREAL_ONE) && (y2 < h))
1547 *pixel2 = fade_color(ptr[((unsigned)p2) >> PFREAL_SHIFT],alpha);
1548 p2 += dy;
1549 y2++;
1550 pixel2 += pixelstep;
1552 while ((p2 < MIN(sh + REFLECT_HEIGHT, sh * 2) * PFREAL_ONE) &&
1553 (y2 < h))
1555 int ty = (((unsigned)p2) >> PFREAL_SHIFT) - sh;
1556 int lalpha = reflect_table[ty];
1557 lalpha = (MULUQ(lalpha, alpha) + 128) >> 8;
1558 *pixel2 = fade_color(ptr[sh - 1 - ty],lalpha);
1559 p2 += dy;
1560 y2++;
1561 pixel2 += pixelstep;
1564 if (zo || slide->angle)
1566 xsnum += xsnumi;
1567 xsden += xsdeni;
1568 xs = fdiv(xsnum, xsden);
1569 } else
1570 xs += PFREAL_ONE;
1573 /* let the music play... */
1574 rb->yield();
1575 return;
1580 Jump the the given slide_index
1582 static inline void set_current_slide(const int slide_index)
1584 int old_center_index = center_index;
1585 step = 0;
1586 center_index = fbound(slide_index, 0, number_of_slides - 1);
1587 if (old_center_index != center_index)
1588 rb->queue_post(&thread_q, EV_WAKEUP, 0);
1589 target = center_index;
1590 slide_frame = slide_index << 16;
1591 reset_slides();
1595 Start the animation for changing slides
1597 void start_animation(void)
1599 step = (target < center_slide.slide_index) ? -1 : 1;
1600 pf_state = pf_scrolling;
1604 Go to the previous slide
1606 void show_previous_slide(void)
1608 if (step == 0) {
1609 if (center_index > 0) {
1610 target = center_index - 1;
1611 start_animation();
1613 } else if ( step > 0 ) {
1614 target = center_index;
1615 start_animation();
1616 } else {
1617 target = fmax(0, center_index - 2);
1623 Go to the next slide
1625 void show_next_slide(void)
1627 if (step == 0) {
1628 if (center_index < number_of_slides - 1) {
1629 target = center_index + 1;
1630 start_animation();
1632 } else if ( step < 0 ) {
1633 target = center_index;
1634 start_animation();
1635 } else {
1636 target = fmin(center_index + 2, number_of_slides - 1);
1642 Render the slides. Updates only the offscreen buffer.
1644 void render_all_slides(void)
1646 MYLCD(set_background)(G_BRIGHT(0));
1647 /* TODO: Optimizes this by e.g. invalidating rects */
1648 MYLCD(clear_display)();
1650 int nleft = num_slides;
1651 int nright = num_slides;
1653 int index;
1654 if (step == 0) {
1655 /* no animation, boring plain rendering */
1656 for (index = nleft - 2; index >= 0; index--) {
1657 int alpha = (index < nleft - 2) ? 256 : 128;
1658 alpha -= extra_fade;
1659 if (alpha > 0 )
1660 render_slide(&left_slides[index], alpha);
1662 for (index = nright - 2; index >= 0; index--) {
1663 int alpha = (index < nright - 2) ? 256 : 128;
1664 alpha -= extra_fade;
1665 if (alpha > 0 )
1666 render_slide(&right_slides[index], alpha);
1668 } else {
1669 /* the first and last slide must fade in/fade out */
1670 for (index = nleft - 1; index >= 0; index--) {
1671 int alpha = 256;
1672 if (index == nleft - 1)
1673 alpha = (step > 0) ? 0 : 128 - fade / 2;
1674 if (index == nleft - 2)
1675 alpha = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;
1676 if (index == nleft - 3)
1677 alpha = (step > 0) ? 256 - fade / 2 : 256;
1678 render_slide(&left_slides[index], alpha);
1680 for (index = nright - 1; index >= 0; index--) {
1681 int alpha = (index < nright - 2) ? 256 : 128;
1682 if (index == nright - 1)
1683 alpha = (step > 0) ? fade / 2 : 0;
1684 if (index == nright - 2)
1685 alpha = (step > 0) ? 128 + fade / 2 : fade / 2;
1686 if (index == nright - 3)
1687 alpha = (step > 0) ? 256 : 128 + fade / 2;
1688 render_slide(&right_slides[index], alpha);
1691 render_slide(&center_slide, 256);
1696 Updates the animation effect. Call this periodically from a timer.
1698 void update_scroll_animation(void)
1700 if (step == 0)
1701 return;
1703 int speed = 16384;
1704 int i;
1706 /* deaccelerate when approaching the target */
1707 if (true) {
1708 const int max = 2 * 65536;
1710 int fi = slide_frame;
1711 fi -= (target << 16);
1712 if (fi < 0)
1713 fi = -fi;
1714 fi = fmin(fi, max);
1716 int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);
1717 speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;
1720 slide_frame += speed * step;
1722 int index = slide_frame >> 16;
1723 int pos = slide_frame & 0xffff;
1724 int neg = 65536 - pos;
1725 int tick = (step < 0) ? neg : pos;
1726 PFreal ftick = (tick * PFREAL_ONE) >> 16;
1728 /* the leftmost and rightmost slide must fade away */
1729 fade = pos / 256;
1731 if (step < 0)
1732 index++;
1733 if (center_index != index) {
1734 center_index = index;
1735 rb->queue_post(&thread_q, EV_WAKEUP, 0);
1736 slide_frame = index << 16;
1737 center_slide.slide_index = center_index;
1738 for (i = 0; i < num_slides; i++)
1739 left_slides[i].slide_index = center_index - 1 - i;
1740 for (i = 0; i < num_slides; i++)
1741 right_slides[i].slide_index = center_index + 1 + i;
1744 center_slide.angle = (step * tick * itilt) >> 16;
1745 center_slide.cx = -step * fmul(offsetX, ftick);
1746 center_slide.cy = fmul(offsetY, ftick);
1748 if (center_index == target) {
1749 reset_slides();
1750 pf_state = pf_idle;
1751 step = 0;
1752 fade = 256;
1753 return;
1756 for (i = 0; i < num_slides; i++) {
1757 struct slide_data *si = &left_slides[i];
1758 si->angle = itilt;
1759 si->cx =
1760 -(offsetX + slide_spacing * i * PFREAL_ONE + step
1761 * slide_spacing * ftick);
1762 si->cy = offsetY;
1765 for (i = 0; i < num_slides; i++) {
1766 struct slide_data *si = &right_slides[i];
1767 si->angle = -itilt;
1768 si->cx =
1769 offsetX + slide_spacing * i * PFREAL_ONE - step
1770 * slide_spacing * ftick;
1771 si->cy = offsetY;
1774 if (step > 0) {
1775 PFreal ftick = (neg * PFREAL_ONE) >> 16;
1776 right_slides[0].angle = -(neg * itilt) >> 16;
1777 right_slides[0].cx = fmul(offsetX, ftick);
1778 right_slides[0].cy = fmul(offsetY, ftick);
1779 } else {
1780 PFreal ftick = (pos * PFREAL_ONE) >> 16;
1781 left_slides[0].angle = (pos * itilt) >> 16;
1782 left_slides[0].cx = -fmul(offsetX, ftick);
1783 left_slides[0].cy = fmul(offsetY, ftick);
1786 /* must change direction ? */
1787 if (target < index)
1788 if (step > 0)
1789 step = -1;
1790 if (target > index)
1791 if (step < 0)
1792 step = 1;
1797 Cleanup the plugin
1799 void cleanup(void *parameter)
1801 (void) parameter;
1802 /* Turn on backlight timeout (revert to settings) */
1803 backlight_use_settings(); /* backlight control in lib/helper.c */
1805 #ifdef USEGSLIB
1806 grey_release();
1807 #endif
1808 rb->lcd_set_drawmode(old_drawmode);
1812 Create the "?" slide, that is shown while loading
1813 or when no cover was found.
1815 int create_empty_slide(bool force)
1817 if ( force || ! rb->file_exists( EMPTY_SLIDE ) ) {
1818 struct bitmap input_bmp;
1819 int ret;
1820 input_bmp.width = DISPLAY_WIDTH;
1821 input_bmp.height = DISPLAY_HEIGHT;
1822 #if LCD_DEPTH > 1
1823 input_bmp.format = FORMAT_NATIVE;
1824 #endif
1825 input_bmp.data = (char*)buf;
1826 ret = scaled_read_bmp_file(EMPTY_SLIDE_BMP, &input_bmp,
1827 buf_size,
1828 FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_KEEP_ASPECT,
1829 &format_transposed);
1830 if (!save_pfraw(EMPTY_SLIDE, &input_bmp))
1831 return false;
1834 return true;
1838 Shows the album name setting menu
1840 int album_name_menu(void)
1842 int selection = show_album_name;
1844 MENUITEM_STRINGLIST(album_name_menu,"Show album title",NULL,
1845 "Hide album title", "Show at the bottom", "Show at the top");
1846 rb->do_menu(&album_name_menu, &selection, NULL, false);
1848 show_album_name = selection;
1849 return GO_TO_PREVIOUS;
1853 Shows the settings menu
1855 int settings_menu(void)
1857 int selection = 0;
1858 bool old_val;
1860 MENUITEM_STRINGLIST(settings_menu, "PictureFlow Settings", NULL, "Show FPS",
1861 "Spacing", "Centre margin", "Number of slides", "Zoom",
1862 "Show album title", "Resize Covers", "Rebuild cache");
1864 do {
1865 selection=rb->do_menu(&settings_menu,&selection, NULL, false);
1866 switch(selection) {
1867 case 0:
1868 rb->set_bool("Show FPS", &show_fps);
1869 reset_track_list();
1870 break;
1872 case 1:
1873 rb->set_int("Spacing between slides", "", 1,
1874 &slide_spacing,
1875 NULL, 1, 0, 100, NULL );
1876 recalc_offsets();
1877 reset_slides();
1878 break;
1880 case 2:
1881 rb->set_int("Centre margin", "", 1,
1882 &center_margin,
1883 NULL, 1, 0, 80, NULL );
1884 recalc_offsets();
1885 reset_slides();
1886 break;
1888 case 3:
1889 rb->set_int("Number of slides", "", 1, &num_slides,
1890 NULL, 1, 1, MAX_SLIDES_COUNT, NULL );
1891 recalc_offsets();
1892 reset_slides();
1893 break;
1895 case 4:
1896 rb->set_int("Zoom", "", 1, &zoom,
1897 NULL, 1, 10, 300, NULL );
1898 recalc_offsets();
1899 reset_slides();
1900 break;
1901 case 5:
1902 album_name_menu();
1903 reset_track_list();
1904 recalc_offsets();
1905 reset_slides();
1906 break;
1907 case 6:
1908 old_val = resize;
1909 rb->set_bool("Resize Covers", &resize);
1910 if (old_val == resize) /* changed? */
1911 break;
1912 /* fallthrough if changed, since cache needs to be rebuilt */
1913 case 7:
1914 cache_version = 0;
1915 rb->remove(EMPTY_SLIDE);
1916 rb->splash(HZ, "Cache will be rebuilt on next restart");
1917 break;
1919 case MENU_ATTACHED_USB:
1920 return PLUGIN_USB_CONNECTED;
1922 } while ( selection >= 0 );
1923 configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
1924 return 0;
1928 Show the main menu
1930 int main_menu(void)
1932 int selection = 0;
1933 int result;
1935 #if LCD_DEPTH > 1
1936 rb->lcd_set_foreground(N_BRIGHT(255));
1937 #endif
1939 MENUITEM_STRINGLIST(main_menu,"PictureFlow Main Menu",NULL,
1940 "Settings", "Return", "Quit");
1941 while (1) {
1942 switch (rb->do_menu(&main_menu,&selection, NULL, false)) {
1943 case 0:
1944 result = settings_menu();
1945 if ( result != 0 ) return result;
1946 break;
1948 case 1:
1949 return 0;
1951 case 2:
1952 return -1;
1954 case MENU_ATTACHED_USB:
1955 return PLUGIN_USB_CONNECTED;
1957 default:
1958 return 0;
1964 Animation step for zooming into the current cover
1966 void update_cover_in_animation(void)
1968 cover_animation_keyframe++;
1969 if( cover_animation_keyframe < 20 ) {
1970 center_slide.distance-=5;
1971 center_slide.angle+=1;
1972 extra_fade += 13;
1974 else if( cover_animation_keyframe < 35 ) {
1975 center_slide.angle+=16;
1977 else {
1978 cover_animation_keyframe = 0;
1979 pf_state = pf_show_tracks;
1984 Animation step for zooming out the current cover
1986 void update_cover_out_animation(void)
1988 cover_animation_keyframe++;
1989 if( cover_animation_keyframe <= 15 ) {
1990 center_slide.angle-=16;
1992 else if( cover_animation_keyframe < 35 ) {
1993 center_slide.distance+=5;
1994 center_slide.angle-=1;
1995 extra_fade -= 13;
1997 else {
1998 cover_animation_keyframe = 0;
1999 pf_state = pf_idle;
2004 Draw a blue gradient at y with height h
2006 static inline void draw_gradient(int y, int h)
2008 static int r, inc, c;
2009 inc = (100 << 8) / h;
2010 c = 0;
2011 selected_track_pulse = (selected_track_pulse+1) % 10;
2012 int c2 = selected_track_pulse - 5;
2013 for (r=0; r<h; r++) {
2014 #ifdef HAVE_LCD_COLOR
2015 MYLCD(set_foreground)(G_PIX(c2+80-(c >> 9), c2+100-(c >> 9),
2016 c2+250-(c >> 8)));
2017 #else
2018 MYLCD(set_foreground)(G_BRIGHT(c2+160-(c >> 8)));
2019 #endif
2020 MYLCD(hline)(0, LCD_WIDTH, r+y);
2021 if ( r > h/2 )
2022 c-=inc;
2023 else
2024 c+=inc;
2029 static void track_list_yh(int char_height)
2031 switch (show_album_name)
2033 case album_name_hide:
2034 track_list_y = (show_fps ? char_height : 0);
2035 track_list_h = LCD_HEIGHT - track_list_y;
2036 break;
2037 case album_name_bottom:
2038 track_list_y = (show_fps ? char_height : 0);
2039 track_list_h = LCD_HEIGHT - track_list_y - char_height * 2;
2040 break;
2041 default: /* case album_name_top */
2042 track_list_y = char_height * 2;
2043 track_list_h = LCD_HEIGHT - track_list_y -
2044 (show_fps ? char_height : 0);
2045 break;
2050 Reset the track list after a album change
2052 void reset_track_list(void)
2054 int albumtxt_h = rb->screens[SCREEN_MAIN]->getcharheight();
2055 track_list_yh(albumtxt_h);
2056 track_list_visible_entries = fmin( track_list_h/albumtxt_h , track_count );
2057 start_index_track_list = 0;
2058 track_scroll_index = 0;
2059 track_scroll_dir = 1;
2060 selected_track = 0;
2062 /* let the tracklist start more centered
2063 * if the screen isn't filled with tracks */
2064 if (track_count*albumtxt_h < track_list_h)
2066 track_list_h = track_count * albumtxt_h;
2067 track_list_y = LCD_HEIGHT / 2 - (track_list_h / 2);
2072 Display the list of tracks
2074 void show_track_list(void)
2076 MYLCD(clear_display)();
2077 if ( center_slide.slide_index != track_index ) {
2078 create_track_index(center_slide.slide_index);
2079 reset_track_list();
2081 static int titletxt_w, titletxt_x, color, titletxt_h;
2082 titletxt_h = rb->screens[SCREEN_MAIN]->getcharheight();
2084 int titletxt_y = track_list_y;
2085 int track_i;
2086 track_i = start_index_track_list;
2087 for (;track_i < track_list_visible_entries+start_index_track_list;
2088 track_i++)
2090 MYLCD(getstringsize)(get_track_name(track_i), &titletxt_w, NULL);
2091 titletxt_x = (LCD_WIDTH-titletxt_w)/2;
2092 if ( track_i == selected_track ) {
2093 draw_gradient(titletxt_y, titletxt_h);
2094 MYLCD(set_foreground)(G_BRIGHT(255));
2095 if (titletxt_w > LCD_WIDTH ) {
2096 if ( titletxt_w + track_scroll_index <= LCD_WIDTH )
2097 track_scroll_dir = 1;
2098 else if ( track_scroll_index >= 0 ) track_scroll_dir = -1;
2099 track_scroll_index += track_scroll_dir*2;
2100 titletxt_x = track_scroll_index;
2102 MYLCD(putsxy)(titletxt_x,titletxt_y,get_track_name(track_i));
2104 else {
2105 color = 250 - (abs(selected_track - track_i) * 200 / track_count);
2106 MYLCD(set_foreground)(G_BRIGHT(color));
2107 MYLCD(putsxy)(titletxt_x,titletxt_y,get_track_name(track_i));
2109 titletxt_y += titletxt_h;
2113 void select_next_track(void)
2115 if ( selected_track < track_count - 1 ) {
2116 selected_track++;
2117 track_scroll_index = 0;
2118 track_scroll_dir = 1;
2119 if (selected_track==(track_list_visible_entries+start_index_track_list))
2120 start_index_track_list++;
2124 void select_prev_track(void)
2126 if (selected_track > 0 ) {
2127 if (selected_track==start_index_track_list) start_index_track_list--;
2128 track_scroll_index = 0;
2129 track_scroll_dir = 1;
2130 selected_track--;
2135 Draw the current album name
2137 void draw_album_text(void)
2139 if (0 == show_album_name)
2140 return;
2142 int albumtxt_w, albumtxt_h;
2143 int albumtxt_y = 0;
2145 char *albumtxt;
2146 int c;
2147 /* Draw album text */
2148 if ( pf_state == pf_scrolling ) {
2149 c = ((slide_frame & 0xffff )/ 255);
2150 if (step < 0) c = 255-c;
2151 if (c > 128 ) { /* half way to next slide .. still not perfect! */
2152 albumtxt = get_album_name(center_index+step);
2153 c = (c-128)*2;
2155 else {
2156 albumtxt = get_album_name(center_index);
2157 c = (128-c)*2;
2160 else {
2161 c= 255;
2162 albumtxt = get_album_name(center_index);
2165 MYLCD(set_foreground)(G_BRIGHT(c));
2166 MYLCD(getstringsize)(albumtxt, &albumtxt_w, &albumtxt_h);
2167 if (center_index != prev_center_index) {
2168 albumtxt_x = 0;
2169 albumtxt_dir = -1;
2170 prev_center_index = center_index;
2173 if (show_album_name == album_name_top)
2174 albumtxt_y = albumtxt_h / 2;
2175 else
2176 albumtxt_y = LCD_HEIGHT - albumtxt_h - albumtxt_h/2;
2178 if (albumtxt_w > LCD_WIDTH ) {
2179 MYLCD(putsxy)(albumtxt_x, albumtxt_y , albumtxt);
2180 if ( pf_state == pf_idle || pf_state == pf_show_tracks ) {
2181 if ( albumtxt_w + albumtxt_x <= LCD_WIDTH ) albumtxt_dir = 1;
2182 else if ( albumtxt_x >= 0 ) albumtxt_dir = -1;
2183 albumtxt_x += albumtxt_dir;
2186 else {
2187 MYLCD(putsxy)((LCD_WIDTH - albumtxt_w) /2, albumtxt_y , albumtxt);
2195 Main function that also contain the main plasma
2196 algorithm.
2198 int main(void)
2200 int ret;
2202 rb->lcd_setfont(FONT_UI);
2203 draw_splashscreen();
2205 if ( ! rb->dir_exists( CACHE_PREFIX ) ) {
2206 if ( rb->mkdir( CACHE_PREFIX ) < 0 ) {
2207 rb->splash(HZ, "Could not create directory " CACHE_PREFIX );
2208 return PLUGIN_ERROR;
2212 configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
2214 init_reflect_table();
2216 ALIGN_BUFFER(buf, buf_size, 4);
2217 ret = create_album_index();
2218 if (ret == ERROR_BUFFER_FULL) {
2219 rb->splash(HZ, "Not enough memory for album names");
2220 return PLUGIN_ERROR;
2221 } else if (ret == ERROR_NO_ALBUMS) {
2222 rb->splash(HZ, "No albums found. Please enable database");
2223 return PLUGIN_ERROR;
2226 ALIGN_BUFFER(buf, buf_size, 4);
2227 number_of_slides = album_count;
2228 if ((cache_version != CACHE_VERSION) && !create_albumart_cache()) {
2229 rb->splash(HZ, "Could not create album art cache");
2230 return PLUGIN_ERROR;
2233 if (!create_empty_slide(cache_version != CACHE_VERSION)) {
2234 rb->splash(HZ, "Could not load the empty slide");
2235 return PLUGIN_ERROR;
2237 cache_version = CACHE_VERSION;
2238 configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
2241 #ifdef USEGSLIB
2242 long grey_buf_used;
2243 if (!grey_init(buf, buf_size, GREY_BUFFERED|GREY_ON_COP,
2244 LCD_WIDTH, LCD_HEIGHT, &grey_buf_used))
2246 rb->splash(HZ, "Greylib init failed!");
2247 return PLUGIN_ERROR;
2249 grey_setfont(FONT_UI);
2250 buf_size -= grey_buf_used;
2251 buf = (void*)(grey_buf_used + (char*)buf);
2252 #endif
2253 buflib_init(&buf_ctx, (void *)buf, buf_size);
2255 if (!(empty_slide_hid = read_pfraw(EMPTY_SLIDE, 0)))
2257 rb->splash(HZ, "Unable to load empty slide image");
2258 return PLUGIN_ERROR;
2261 if (!create_pf_thread()) {
2262 rb->splash(HZ, "Cannot create thread!");
2263 return PLUGIN_ERROR;
2266 int i;
2268 /* initialize */
2269 for (i = 0; i < SLIDE_CACHE_SIZE; i++) {
2270 cache[i].hid = 0;
2271 cache[i].index = 0;
2272 cache[i].next = i + 1;
2273 cache[i].prev = i - 1;
2275 cache[0].prev = i - 1;
2276 cache[i - 1].next = 0;
2277 cache_free = 0;
2278 buffer = LCD_BUF;
2280 pf_state = pf_idle;
2282 track_index = -1;
2283 extra_fade = 0;
2284 slide_frame = 0;
2285 step = 0;
2286 target = 0;
2287 fade = 256;
2289 recalc_offsets();
2290 reset_slides();
2292 char fpstxt[10];
2293 int button;
2295 int frames = 0;
2296 long last_update = *rb->current_tick;
2297 long current_update;
2298 long update_interval = 100;
2299 int fps = 0;
2300 int fpstxt_y;
2302 bool instant_update;
2303 old_drawmode = rb->lcd_get_drawmode();
2304 #ifdef USEGSLIB
2305 grey_show(true);
2306 grey_set_drawmode(DRMODE_FG);
2307 #endif
2308 rb->lcd_set_drawmode(DRMODE_FG);
2309 while (true) {
2310 current_update = *rb->current_tick;
2311 frames++;
2313 /* Initial rendering */
2314 instant_update = false;
2316 /* Handle states */
2317 switch ( pf_state ) {
2318 case pf_scrolling:
2319 update_scroll_animation();
2320 render_all_slides();
2321 instant_update = true;
2322 break;
2323 case pf_cover_in:
2324 update_cover_in_animation();
2325 render_all_slides();
2326 instant_update = true;
2327 break;
2328 case pf_cover_out:
2329 update_cover_out_animation();
2330 render_all_slides();
2331 instant_update = true;
2332 break;
2333 case pf_show_tracks:
2334 show_track_list();
2335 break;
2336 case pf_idle:
2337 render_all_slides();
2338 break;
2341 /* Calculate FPS */
2342 if (current_update - last_update > update_interval) {
2343 fps = frames * HZ / (current_update - last_update);
2344 last_update = current_update;
2345 frames = 0;
2347 /* Draw FPS */
2348 if (show_fps)
2350 #ifdef USEGSLIB
2351 MYLCD(set_foreground)(G_BRIGHT(255));
2352 #else
2353 MYLCD(set_foreground)(G_PIX(255,0,0));
2354 #endif
2355 rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps);
2356 if (show_album_name == album_name_top)
2357 fpstxt_y = LCD_HEIGHT -
2358 rb->screens[SCREEN_MAIN]->getcharheight();
2359 else
2360 fpstxt_y = 0;
2361 MYLCD(putsxy)(0, fpstxt_y, fpstxt);
2363 draw_album_text();
2366 /* Copy offscreen buffer to LCD and give time to other threads */
2367 MYLCD(update)();
2368 rb->yield();
2370 /*/ Handle buttons */
2371 button = pluginlib_getaction(instant_update ? 0 : HZ/16,
2372 plugin_contexts, NB_ACTION_CONTEXTS);
2374 switch (button) {
2375 case PICTUREFLOW_QUIT:
2376 return PLUGIN_OK;
2378 case PICTUREFLOW_MENU:
2379 if ( pf_state == pf_idle || pf_state == pf_scrolling ) {
2380 #ifdef USEGSLIB
2381 grey_show(false);
2382 #endif
2383 ret = main_menu();
2384 if ( ret == -1 ) return PLUGIN_OK;
2385 if ( ret != 0 ) return i;
2386 #ifdef USEGSLIB
2387 grey_show(true);
2388 #endif
2389 MYLCD(set_drawmode)(DRMODE_FG);
2391 else {
2392 pf_state = pf_cover_out;
2394 break;
2396 case PICTUREFLOW_NEXT_ALBUM:
2397 case PICTUREFLOW_NEXT_ALBUM_REPEAT:
2398 #ifdef HAVE_SCROLLWHEEL
2399 if ( pf_state == pf_show_tracks )
2400 select_next_track();
2401 #endif
2402 if ( pf_state == pf_idle || pf_state == pf_scrolling )
2403 show_next_slide();
2404 break;
2406 case PICTUREFLOW_PREV_ALBUM:
2407 case PICTUREFLOW_PREV_ALBUM_REPEAT:
2408 #ifdef HAVE_SCROLLWHEEL
2409 if ( pf_state == pf_show_tracks )
2410 select_prev_track();
2411 #endif
2412 if ( pf_state == pf_idle || pf_state == pf_scrolling )
2413 show_previous_slide();
2414 break;
2416 #ifndef HAVE_SCROLLWHEEL
2417 case PICTUREFLOW_NEXT_TRACK:
2418 case PICTUREFLOW_NEXT_TRACK_REPEAT:
2419 if ( pf_state == pf_show_tracks )
2420 select_next_track();
2421 break;
2423 case PICTUREFLOW_PREV_TRACK:
2424 case PICTUREFLOW_PREV_TRACK_REPEAT:
2425 if ( pf_state == pf_show_tracks )
2426 select_prev_track();
2427 break;
2428 #endif
2430 case PICTUREFLOW_SELECT_ALBUM:
2431 if ( pf_state == pf_idle ) {
2432 pf_state = pf_cover_in;
2434 if ( pf_state == pf_show_tracks )
2435 pf_state = pf_cover_out;
2436 break;
2438 default:
2439 if (rb->default_event_handler_ex(button, cleanup, NULL)
2440 == SYS_USB_CONNECTED)
2441 return PLUGIN_USB_CONNECTED;
2442 break;
2449 /*************************** Plugin entry point ****************************/
2451 enum plugin_status plugin_start(const void *parameter)
2453 int ret;
2454 (void) parameter;
2455 #if LCD_DEPTH > 1
2456 rb->lcd_set_backdrop(NULL);
2457 #endif
2458 /* Turn off backlight timeout */
2459 backlight_force_on(); /* backlight control in lib/helper.c */
2460 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
2461 rb->cpu_boost(true);
2462 #endif
2463 #if PLUGIN_BUFFER_SIZE > 0x10000
2464 buf = rb->plugin_get_buffer(&buf_size);
2465 #else
2466 buf = rb->plugin_get_audio_buffer(&buf_size);
2467 #endif
2468 ret = main();
2469 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
2470 rb->cpu_boost(false);
2471 #endif
2472 if ( ret == PLUGIN_OK ) {
2473 if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
2474 CONFIG_VERSION))
2476 rb->splash(HZ, "Error writing config.");
2477 ret = PLUGIN_ERROR;
2481 end_pf_thread();
2482 cleanup(NULL);
2483 return ret;