1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
28 #include "lib/read_image.h"
29 #include "lib/pluginlib_actions.h"
30 #include "lib/helper.h"
31 #include "lib/configfile.h"
33 #include "lib/feature_wrappers.h"
34 #include "lib/buflib.h"
38 /******************************* Globals ***********************************/
40 #define PF_PREV ACTION_STD_PREV
41 #define PF_PREV_REPEAT ACTION_STD_PREVREPEAT
42 #define PF_NEXT ACTION_STD_NEXT
43 #define PF_NEXT_REPEAT ACTION_STD_NEXTREPEAT
44 #define PF_SELECT ACTION_STD_OK
45 #define PF_CONTEXT ACTION_STD_CONTEXT
46 #define PF_BACK ACTION_STD_CANCEL
47 #define PF_MENU ACTION_STD_MENU
48 #define PF_QUIT (LAST_ACTION_PLACEHOLDER + 1)
50 const struct button_mapping pf_context_album_scroll
[] =
52 #ifdef HAVE_TOUCHSCREEN
53 {PF_PREV
, BUTTON_MIDLEFT
, BUTTON_NONE
},
54 {PF_PREV_REPEAT
, BUTTON_MIDLEFT
|BUTTON_REPEAT
, BUTTON_NONE
},
55 {PF_NEXT
, BUTTON_MIDRIGHT
, BUTTON_NONE
},
56 {PF_NEXT_REPEAT
, BUTTON_MIDRIGHT
|BUTTON_REPEAT
, BUTTON_NONE
},
58 #if CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD || \
59 CONFIG_KEYPAD == IAUDIO_X5M5_PAD || CONFIG_KEYPAD == GIGABEAT_PAD || \
60 CONFIG_KEYPAD == GIGABEAT_S_PAD || CONFIG_KEYPAD == RECORDER_PAD || \
61 CONFIG_KEYPAD == ARCHOS_AV300_PAD || CONFIG_KEYPAD == SANSA_C100_PAD || \
62 CONFIG_KEYPAD == SANSA_C200_PAD || CONFIG_KEYPAD == SANSA_CLIP_PAD || \
63 CONFIG_KEYPAD == SANSA_M200_PAD || CONFIG_KEYPAD == IRIVER_IFP7XX_PAD || \
64 CONFIG_KEYPAD == MROBE100_PAD || CONFIG_KEYPAD == PHILIPS_SA9200_PAD || \
65 CONFIG_KEYPAD == IAUDIO67_PAD || CONFIG_KEYPAD == CREATIVEZVM_PAD || \
66 CONFIG_KEYPAD == PHILIPS_HDD1630_PAD || CONFIG_KEYPAD == CREATIVEZV_PAD \
67 || CONFIG_KEYPAD == SANSA_CLIP_PAD || CONFIG_KEYPAD == LOGIK_DAX_PAD || \
68 CONFIG_KEYPAD == MEIZU_M6SL_PAD
69 {PF_PREV
, BUTTON_LEFT
, BUTTON_NONE
},
70 {PF_PREV_REPEAT
, BUTTON_LEFT
|BUTTON_REPEAT
, BUTTON_NONE
},
71 {PF_NEXT
, BUTTON_RIGHT
, BUTTON_NONE
},
72 {PF_NEXT_REPEAT
, BUTTON_RIGHT
|BUTTON_REPEAT
, BUTTON_NONE
},
73 #elif CONFIG_KEYPAD == ONDIO_PAD
74 {PF_PREV
, BUTTON_LEFT
, BUTTON_NONE
},
75 {PF_PREV_REPEAT
, BUTTON_LEFT
|BUTTON_REPEAT
, BUTTON_NONE
},
76 {PF_NEXT
, BUTTON_RIGHT
, BUTTON_NONE
},
77 {PF_NEXT_REPEAT
, BUTTON_RIGHT
|BUTTON_REPEAT
, BUTTON_NONE
},
78 {PF_SELECT
, BUTTON_UP
|BUTTON_REL
, BUTTON_UP
},
79 {PF_CONTEXT
, BUTTON_UP
|BUTTON_REPEAT
, BUTTON_UP
},
80 {ACTION_NONE
, BUTTON_UP
, BUTTON_NONE
},
81 {ACTION_NONE
, BUTTON_DOWN
, BUTTON_NONE
},
82 {ACTION_NONE
, BUTTON_DOWN
|BUTTON_REPEAT
, BUTTON_NONE
},
83 {ACTION_NONE
, BUTTON_RIGHT
|BUTTON_REL
, BUTTON_RIGHT
},
84 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD || CONFIG_KEYPAD == MROBE500_PAD
85 {PF_PREV
, BUTTON_RC_REW
, BUTTON_NONE
},
86 {PF_PREV_REPEAT
, BUTTON_RC_REW
|BUTTON_REPEAT
,BUTTON_NONE
},
87 {PF_NEXT
, BUTTON_RC_FF
, BUTTON_NONE
},
88 {PF_NEXT_REPEAT
, BUTTON_RC_FF
|BUTTON_REPEAT
, BUTTON_NONE
},
90 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM
|1)
93 const struct button_mapping pf_context_buttons
[] =
95 #ifdef HAVE_TOUCHSCREEN
96 {PF_SELECT
, BUTTON_CENTER
, BUTTON_NONE
},
97 {PF_MENU
, BUTTON_TOPLEFT
, BUTTON_NONE
},
98 {PF_BACK
, BUTTON_BOTTOMRIGHT
, BUTTON_NONE
},
100 #if CONFIG_KEYPAD == ARCHOS_AV300_PAD
101 {PF_QUIT
, BUTTON_OFF
, BUTTON_NONE
},
102 #elif CONFIG_KEYPAD == SANSA_C100_PAD
103 {PF_QUIT
, BUTTON_MENU
|BUTTON_REPEAT
, BUTTON_MENU
},
104 #elif CONFIG_KEYPAD == CREATIVEZV_PAD || CONFIG_KEYPAD == CREATIVEZVM_PAD || \
105 CONFIG_KEYPAD == PHILIPS_HDD1630_PAD || CONFIG_KEYPAD == IAUDIO67_PAD || \
106 CONFIG_KEYPAD == GIGABEAT_PAD || CONFIG_KEYPAD == GIGABEAT_S_PAD || \
107 CONFIG_KEYPAD == MROBE100_PAD || CONFIG_KEYPAD == MROBE500_PAD || \
108 CONFIG_KEYPAD == PHILIPS_SA9200_PAD || CONFIG_KEYPAD == SANSA_CLIP_PAD || \
109 CONFIG_KEYPAD == SANSA_FUZE_PAD
110 {PF_QUIT
, BUTTON_POWER
, BUTTON_NONE
},
111 /* These all use short press of BUTTON_POWER for menu, map long POWER to quit
113 #elif CONFIG_KEYPAD == SANSA_C200_PAD || CONFIG_KEYPAD == SANSA_M200_PAD || \
114 CONFIG_KEYPAD == IRIVER_H10_PAD || CONFIG_KEYPAD == COWOND2_PAD
115 {PF_QUIT
, BUTTON_POWER
|BUTTON_REPEAT
, BUTTON_POWER
},
116 #if CONFIG_KEYPAD == COWOND2_PAD
117 {PF_BACK
, BUTTON_POWER
|BUTTON_REL
, BUTTON_POWER
},
118 {ACTION_NONE
, BUTTON_POWER
, BUTTON_NONE
},
120 #elif CONFIG_KEYPAD == SANSA_E200_PAD
121 {PF_QUIT
, BUTTON_POWER
, BUTTON_NONE
},
122 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
123 {PF_QUIT
, BUTTON_EQ
, BUTTON_NONE
},
124 #elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
125 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
126 || (CONFIG_KEYPAD == IPOD_4G_PAD)
127 {PF_QUIT
, BUTTON_MENU
|BUTTON_REPEAT
, BUTTON_MENU
},
128 #elif CONFIG_KEYPAD == LOGIK_DAX_PAD
129 {PF_QUIT
, BUTTON_POWERPLAY
|BUTTON_REPEAT
, BUTTON_POWERPLAY
},
130 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
131 {PF_QUIT
, BUTTON_RC_REC
, BUTTON_NONE
},
132 #elif CONFIG_KEYPAD == MEIZU_M6SL_PAD
133 {PF_QUIT
, BUTTON_MENU
|BUTTON_REPEAT
, BUTTON_MENU
},
134 #elif CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD || \
135 CONFIG_KEYPAD == RECORDER_PAD || CONFIG_KEYPAD == ONDIO_PAD
136 {PF_QUIT
, BUTTON_OFF
, BUTTON_NONE
},
138 #if CONFIG_KEYPAD == IAUDIO_M3_PAD
139 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD
|CONTEXT_REMOTE
)
141 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD
)
144 const struct button_mapping
*pf_contexts
[] =
146 pf_context_album_scroll
,
152 #define N_BRIGHT(y) LCD_BRIGHTNESS(y)
153 #else /* LCD_DEPTH <= 1 */
154 #define N_BRIGHT(y) ((y > 127) ? 0 : 1)
155 #ifdef HAVE_NEGATIVE_LCD /* m:robe 100, Clip */
156 #define PICTUREFLOW_DRMODE DRMODE_SOLID
158 #define PICTUREFLOW_DRMODE (DRMODE_SOLID|DRMODE_INVERSEVID)
160 #endif /* LCD_DEPTH <= 1 */
163 #define LCD_BUF _grey_info.buffer
164 #define MYLCD(fn) grey_ ## fn
165 #define G_PIX(r,g,b) \
166 (77 * (unsigned)(r) + 150 * (unsigned)(g) + 29 * (unsigned)(b)) / 256
167 #define N_PIX(r,g,b) N_BRIGHT(G_PIX(r,g,b))
168 #define G_BRIGHT(y) (y)
169 #define BUFFER_WIDTH _grey_info.width
170 #define BUFFER_HEIGHT _grey_info.height
171 typedef unsigned char pix_t
;
172 #else /* LCD_DEPTH >= 8 */
173 #define LCD_BUF rb->lcd_framebuffer
174 #define MYLCD(fn) rb->lcd_ ## fn
175 #define G_PIX LCD_RGBPACK
176 #define N_PIX LCD_RGBPACK
177 #define G_BRIGHT(y) LCD_RGBPACK(y,y,y)
178 #define N_BRIGHT(y) LCD_RGBPACK(y,y,y)
179 #define BUFFER_WIDTH LCD_WIDTH
180 #define BUFFER_HEIGHT LCD_HEIGHT
181 typedef fb_data pix_t
;
182 #endif /* LCD_DEPTH >= 8 */
184 /* for fixed-point arithmetic, we need minimum 32-bit long
185 long long (64-bit) might be useful for multiplication and division */
187 #define PFREAL_SHIFT 10
188 #define PFREAL_FACTOR (1 << PFREAL_SHIFT)
189 #define PFREAL_ONE (1 << PFREAL_SHIFT)
190 #define PFREAL_HALF (PFREAL_ONE >> 1)
193 #define IANGLE_MAX 1024
194 #define IANGLE_MASK 1023
196 #define REFLECT_TOP (LCD_HEIGHT * 2 / 3)
197 #define REFLECT_HEIGHT (LCD_HEIGHT - REFLECT_TOP)
198 #define DISPLAY_HEIGHT REFLECT_TOP
199 #define DISPLAY_WIDTH MAX((LCD_HEIGHT * LCD_PIXEL_ASPECT_HEIGHT / \
200 LCD_PIXEL_ASPECT_WIDTH / 2), (LCD_WIDTH * 2 / 5))
201 #define REFLECT_SC ((0x10000U * 3 + (REFLECT_HEIGHT * 5 - 1)) / \
202 (REFLECT_HEIGHT * 5))
203 #define DISPLAY_OFFS ((LCD_HEIGHT / 2) - REFLECT_HEIGHT)
204 #define CAM_DIST MAX(MIN(LCD_HEIGHT,LCD_WIDTH),120)
205 #define CAM_DIST_R (CAM_DIST << PFREAL_SHIFT)
206 #define DISPLAY_LEFT_R (PFREAL_HALF - LCD_WIDTH * PFREAL_HALF)
207 #define MAXSLIDE_LEFT_R (PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF)
209 #define SLIDE_CACHE_SIZE 64 /* probably more than can be loaded */
211 #define MAX_SLIDES_COUNT 10
213 #define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
214 #define CACHE_PREFIX PLUGIN_DEMOS_DIR "/pictureflow"
217 #define EV_WAKEUP 1337
219 #define EMPTY_SLIDE CACHE_PREFIX "/emptyslide.pfraw"
220 #define EMPTY_SLIDE_BMP PLUGIN_DEMOS_DIR "/pictureflow_emptyslide.bmp"
221 #define SPLASH_BMP PLUGIN_DEMOS_DIR "/pictureflow_splash.bmp"
223 /* Error return values */
224 #define ERROR_NO_ALBUMS -1
225 #define ERROR_BUFFER_FULL -2
227 /* current version for cover cache */
228 #define CACHE_VERSION 3
229 #define CONFIG_VERSION 1
230 #define CONFIG_FILE "pictureflow.cfg"
232 /** structs we use */
243 int index
; /* index of the cached slide */
244 int hid
; /* handle ID of the cached slide */
245 short next
; /* "next" slide, with LRU last */
246 short prev
; /* "previous" slide */
267 struct load_slide_event_data
{
273 struct pfraw_header
{
274 int32_t width
; /* bmap width in pixels */
275 int32_t height
; /* bmap height in pixels */
278 enum show_album_name_values
{ album_name_hide
= 0, album_name_bottom
,
280 static char* show_album_name_conf
[] =
287 #define MAX_SPACING 40
288 #define MAX_MARGIN 80
290 /* config values and their defaults */
291 static int slide_spacing
= DISPLAY_WIDTH
/ 4;
292 static int center_margin
= (LCD_WIDTH
- DISPLAY_WIDTH
) / 12;
293 static int num_slides
= 4;
294 static int zoom
= 100;
295 static bool show_fps
= false;
296 static bool resize
= true;
297 static int cache_version
= 0;
298 static int show_album_name
= (LCD_HEIGHT
> 100)
299 ? album_name_top
: album_name_bottom
;
301 static struct configdata config
[] =
303 { TYPE_INT
, 0, MAX_SPACING
, { .int_p
= &slide_spacing
}, "slide spacing",
305 { TYPE_INT
, 0, MAX_MARGIN
, { .int_p
= ¢er_margin
}, "center margin",
307 { TYPE_INT
, 0, MAX_SLIDES_COUNT
, { .int_p
= &num_slides
}, "slides count",
309 { TYPE_INT
, 0, 300, { .int_p
= &zoom
}, "zoom", NULL
},
310 { TYPE_BOOL
, 0, 1, { .bool_p
= &show_fps
}, "show fps", NULL
},
311 { TYPE_BOOL
, 0, 1, { .bool_p
= &resize
}, "resize", NULL
},
312 { TYPE_INT
, 0, 100, { .int_p
= &cache_version
}, "cache version", NULL
},
313 { TYPE_ENUM
, 0, 2, { .int_p
= &show_album_name
}, "show album name",
314 show_album_name_conf
}
317 #define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata))
319 /** below we allocate the memory we want to use **/
321 static pix_t
*buffer
; /* for now it always points to the lcd framebuffer */
322 static uint8_t reflect_table
[REFLECT_HEIGHT
];
323 static struct slide_data center_slide
;
324 static struct slide_data left_slides
[MAX_SLIDES_COUNT
];
325 static struct slide_data right_slides
[MAX_SLIDES_COUNT
];
326 static int slide_frame
;
330 static int center_index
= 0; /* index of the slide that is in the center */
332 static PFreal offsetX
;
333 static PFreal offsetY
;
334 static int number_of_slides
;
336 static struct slide_cache cache
[SLIDE_CACHE_SIZE
];
337 static int cache_free
;
338 static int cache_used
= -1;
339 static int cache_left_index
= -1;
340 static int cache_right_index
= -1;
341 static int cache_center_index
= -1;
343 /* use long for aligning */
344 unsigned long thread_stack
[THREAD_STACK_SIZE
/ sizeof(long)];
345 /* queue (as array) for scheduling load_surface */
347 static int empty_slide_hid
;
349 unsigned int thread_id
;
350 struct event_queue thread_q
;
352 static struct tagcache_search tcs
;
354 static struct buflib_context buf_ctx
;
356 static struct album_data
*album
;
357 static char *album_names
;
358 static int album_count
;
360 static struct track_data
*tracks
;
361 static char *track_names
;
362 static size_t borrowed
= 0;
363 static int track_count
;
364 static int track_index
;
365 static int selected_track
;
366 static int selected_track_pulse
;
367 void reset_track_list(void);
372 static bool thread_is_running
;
374 static int cover_animation_keyframe
;
375 static int extra_fade
;
377 static int albumtxt_x
= 0;
378 static int albumtxt_dir
= -1;
379 static int prev_center_index
= -1;
381 static int start_index_track_list
= 0;
382 static int track_list_visible_entries
= 0;
383 static int track_list_y
;
384 static int track_list_h
;
385 static int track_scroll_index
= 0;
386 static int track_scroll_dir
= 1;
389 Proposals for transitions:
391 pf_idle -> pf_scrolling : NEXT_ALBUM/PREV_ALBUM pressed
392 -> pf_cover_in -> pf_show_tracks : SELECT_ALBUM clicked
394 pf_scrolling -> pf_idle : NEXT_ALBUM/PREV_ALBUM released
396 pf_show_tracks -> pf_cover_out -> pf_idle : SELECT_ALBUM pressed
399 pf_show_tracks -> pf_cover_out -> pf_idle : MENU_PRESSED pressed
400 pf_show_tracks -> play_track() -> exit() : SELECT_ALBUM pressed
402 pf_idle, pf_scrolling -> show_menu(): MENU_PRESSED
415 static bool free_slide_prio(int prio
);
416 static inline unsigned fade_color(pix_t c
, unsigned a
);
417 bool save_pfraw(char* filename
, struct bitmap
*bm
);
418 bool load_new_slide(void);
419 int load_surface(int);
421 static inline PFreal
fmul(PFreal a
, PFreal b
)
423 return (a
*b
) >> PFREAL_SHIFT
;
427 * This version preshifts each operand, which is useful when we know how many
428 * of the least significant bits will be empty, or are worried about overflow
429 * in a particular calculation
431 static inline PFreal
fmuln(PFreal a
, PFreal b
, int ps1
, int ps2
)
433 return ((a
>> ps1
) * (b
>> ps2
)) >> (PFREAL_SHIFT
- ps1
- ps2
);
436 /* ARMv5+ has a clz instruction equivalent to our function.
438 #if (defined(CPU_ARM) && (ARM_ARCH > 4))
439 static inline int clz(uint32_t v
)
441 return __builtin_clz(v
);
444 /* Otherwise, use our clz, which can be inlined */
445 #elif defined(CPU_COLDFIRE)
446 /* This clz is based on the log2(n) implementation at
447 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
448 * A clz benchmark plugin showed this to be about 14% faster on coldfire
449 * than the LUT-based version.
451 static inline int clz(uint32_t v
)
483 static const char clz_lut
[16] = { 4, 3, 2, 2, 1, 1, 1, 1,
484 0, 0, 0, 0, 0, 0, 0, 0 };
485 /* This clz is based on the log2(n) implementation at
486 * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
487 * It is not any faster than the one above, but trades 16B in the lookup table
488 * for a savings of 12B per each inlined call.
490 static inline int clz(uint32_t v
)
508 return r
+ clz_lut
[v
];
512 /* Return the maximum possible left shift for a signed int32, without
515 static inline int allowed_shift(int32_t val
)
517 uint32_t uval
= val
^ (val
>> 31);
518 return clz(uval
) - 1;
521 /* Calculate num/den, with the result shifted left by PFREAL_SHIFT, by shifting
522 * num and den before dividing.
524 static inline PFreal
fdiv(PFreal num
, PFreal den
)
526 int shift
= allowed_shift(num
);
527 shift
= MIN(PFREAL_SHIFT
, shift
);
529 den
>>= PFREAL_SHIFT
- shift
;
533 #define fmin(a,b) (((a) < (b)) ? (a) : (b))
534 #define fmax(a,b) (((a) > (b)) ? (a) : (b))
535 #define fabs(a) (a < 0 ? -a : a)
536 #define fbound(min,val,max) (fmax((min),fmin((max),(val))))
538 #if CONFIG_CPU == SH7034
539 /* 16*16->32 bit multiplication is a single instrcution on the SH1 */
540 #define MULUQ(a, b) ((uint32_t) (((uint16_t) (a)) * ((uint16_t) (b))))
542 #define MULUQ(a, b) ((a) * (b))
547 #define fmul(a,b) ( ((a)*(b)) >> PFREAL_SHIFT )
548 #define fdiv(n,m) ( ((n)<< PFREAL_SHIFT ) / m )
550 #define fconv(a, q1, q2) (((q2)>(q1)) ? (a)<<((q2)-(q1)) : (a)>>((q1)-(q2)))
551 #define tofloat(a, q) ( (float)(a) / (float)(1<<(q)) )
553 static inline PFreal
fmul(PFreal a
, PFreal b
)
555 return (a
*b
) >> PFREAL_SHIFT
;
558 static inline PFreal
fdiv(PFreal n
, PFreal m
)
560 return (n
<<(PFREAL_SHIFT
))/m
;
564 /* warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! */
565 static const short sin_tab
[] = {
566 0, 100, 200, 297, 392, 483, 569, 650,
567 724, 792, 851, 903, 946, 980, 1004, 1019,
568 1024, 1019, 1004, 980, 946, 903, 851, 792,
569 724, 650, 569, 483, 392, 297, 200, 100,
570 0, -100, -200, -297, -392, -483, -569, -650,
571 -724, -792, -851, -903, -946, -980, -1004, -1019,
572 -1024, -1019, -1004, -980, -946, -903, -851, -792,
573 -724, -650, -569, -483, -392, -297, -200, -100,
577 static inline PFreal
fsin(int iangle
)
579 iangle
&= IANGLE_MASK
;
581 int i
= (iangle
>> 4);
582 PFreal p
= sin_tab
[i
];
583 PFreal q
= sin_tab
[(i
+1)];
585 return p
+ g
* (iangle
-i
*16)/16;
588 static inline PFreal
fcos(int iangle
)
590 return fsin(iangle
+ (IANGLE_MAX
>> 2));
593 static inline unsigned scale_val(unsigned val
, unsigned bits
)
595 val
= val
* ((1 << bits
) - 1);
596 return ((val
>> 8) + val
+ 128) >> 8;
599 static void output_row_8_transposed(uint32_t row
, void * row_in
,
600 struct scaler_context
*ctx
)
602 pix_t
*dest
= (pix_t
*)ctx
->bm
->data
+ row
;
603 pix_t
*end
= dest
+ ctx
->bm
->height
* ctx
->bm
->width
;
605 uint8_t *qp
= (uint8_t*)row_in
;
606 for (; dest
< end
; dest
+= ctx
->bm
->height
)
609 struct uint8_rgb
*qp
= (struct uint8_rgb
*)row_in
;
611 for (; dest
< end
; dest
+= ctx
->bm
->height
)
613 r
= scale_val(qp
->red
, 5);
614 g
= scale_val(qp
->green
, 6);
615 b
= scale_val((qp
++)->blue
, 5);
616 *dest
= LCD_RGBPACK_LCD(r
,g
,b
);
621 static void output_row_32_transposed(uint32_t row
, void * row_in
,
622 struct scaler_context
*ctx
)
624 pix_t
*dest
= (pix_t
*)ctx
->bm
->data
+ row
;
625 pix_t
*end
= dest
+ ctx
->bm
->height
* ctx
->bm
->width
;
627 uint32_t *qp
= (uint32_t*)row_in
;
628 for (; dest
< end
; dest
+= ctx
->bm
->height
)
629 *dest
= SC_OUT(*qp
++, ctx
);
631 struct uint32_rgb
*qp
= (struct uint32_rgb
*)row_in
;
633 for (; dest
< end
; dest
+= ctx
->bm
->height
)
635 r
= scale_val(SC_OUT(qp
->r
, ctx
), 5);
636 g
= scale_val(SC_OUT(qp
->g
, ctx
), 6);
637 b
= scale_val(SC_OUT(qp
->b
, ctx
), 5);
639 *dest
= LCD_RGBPACK_LCD(r
,g
,b
);
644 #ifdef HAVE_LCD_COLOR
645 static void output_row_32_transposed_fromyuv(uint32_t row
, void * row_in
,
646 struct scaler_context
*ctx
)
648 pix_t
*dest
= (pix_t
*)ctx
->bm
->data
+ row
;
649 pix_t
*end
= dest
+ ctx
->bm
->height
* ctx
->bm
->width
;
650 struct uint32_rgb
*qp
= (struct uint32_rgb
*)row_in
;
651 for (; dest
< end
; dest
+= ctx
->bm
->height
)
653 unsigned r
, g
, b
, y
, u
, v
;
654 y
= SC_OUT(qp
->b
, ctx
);
655 u
= SC_OUT(qp
->g
, ctx
);
656 v
= SC_OUT(qp
->r
, ctx
);
658 yuv_to_rgb(y
, u
, v
, &r
, &g
, &b
);
662 *dest
= LCD_RGBPACK_LCD(r
, g
, b
);
667 static unsigned int get_size(struct bitmap
*bm
)
669 return bm
->width
* bm
->height
* sizeof(pix_t
);
672 const struct custom_format format_transposed
= {
673 .output_row_8
= output_row_8_transposed
,
674 #ifdef HAVE_LCD_COLOR
676 output_row_32_transposed
,
677 output_row_32_transposed_fromyuv
680 .output_row_32
= output_row_32_transposed
,
685 static const struct button_mapping
* get_context_map(int context
)
687 return pf_contexts
[context
& ~CONTEXT_CUSTOM
];
690 /* Create the lookup table with the scaling values for the reflections */
691 void init_reflect_table(void)
694 for (i
= 0; i
< REFLECT_HEIGHT
; i
++)
696 (768 * (REFLECT_HEIGHT
- i
) + (5 * REFLECT_HEIGHT
/ 2)) /
697 (5 * REFLECT_HEIGHT
);
701 Create an index of all albums from the database.
702 Also store the album names so we can access them later.
704 int create_album_index(void)
706 album
= ((struct album_data
*)(buf_size
+ (char *) buf
)) - 1;
707 rb
->memset(&tcs
, 0, sizeof(struct tagcache_search
) );
709 rb
->tagcache_search(&tcs
, tag_album
);
710 unsigned int l
, old_l
= 0;
712 album
[0].name_idx
= 0;
713 while (rb
->tagcache_get_next(&tcs
))
715 buf_size
-= sizeof(struct album_data
);
717 if ( album_count
> 0 )
718 album
[-album_count
].name_idx
= album
[1-album_count
].name_idx
+ old_l
;
721 /* not enough memory */
722 return ERROR_BUFFER_FULL
;
724 rb
->strcpy(buf
, tcs
.result
);
726 buf
= l
+ (char *)buf
;
727 DEBUGF("%lX: %s\n", tcs
.idxfd
[tag_album
] ? rb
->lseek(tcs
.idxfd
[tag_album
], 0, SEEK_CUR
) : -1, tcs
.result
);
728 album
[-album_count
].seek
= tcs
.result_seek
;
732 rb
->tagcache_search_finish(&tcs
);
733 ALIGN_BUFFER(buf
, buf_size
, 4);
735 struct album_data
* tmp_album
= (struct album_data
*)buf
;
736 for (i
= album_count
- 1; i
>= 0; i
--)
737 tmp_album
[i
] = album
[-i
];
739 buf
= album
+ album_count
;
740 return (album_count
> 0) ? 0 : ERROR_NO_ALBUMS
;
744 Return a pointer to the album name of the given slide_index
746 char* get_album_name(const int slide_index
)
748 return album_names
+ album
[slide_index
].name_idx
;
752 Return a pointer to the track name of the active album
753 create_track_index has to be called first.
755 char* get_track_name(const int track_index
)
757 if ( track_index
< track_count
)
758 return track_names
+ tracks
[track_index
].name_idx
;
763 Compare two unsigned ints passed via pointers.
765 int compare_tracks (const void *a_v
, const void *b_v
)
767 uint32_t a
= ((struct track_data
*)a_v
)->sort
;
768 uint32_t b
= ((struct track_data
*)b_v
)->sort
;
773 Create the track index of the given slide_index.
775 void create_track_index(const int slide_index
)
777 if ( slide_index
== track_index
)
779 track_index
= slide_index
;
781 if (!rb
->tagcache_search(&tcs
, tag_title
))
784 rb
->tagcache_search_add_filter(&tcs
, tag_album
, album
[slide_index
].seek
);
786 int string_index
= 0, track_num
;
789 track_names
= (char *)buflib_buffer_out(&buf_ctx
, &out
);
791 int avail
= borrowed
;
792 tracks
= (struct track_data
*)(track_names
+ borrowed
);
793 while (rb
->tagcache_get_next(&tcs
))
795 avail
-= sizeof(struct track_data
);
796 track_num
= rb
->tagcache_get_numeric(&tcs
, tag_tracknumber
) - 1;
797 disc_num
= rb
->tagcache_get_numeric(&tcs
, tag_discnumber
);
805 len
= 1 + rb
->snprintf(track_names
+ string_index
, avail
,
806 "%d.%02d: %s", disc_num
, track_num
+ 1, tcs
.result
);
808 len
= 1 + rb
->snprintf(track_names
+ string_index
, avail
,
809 "%d: %s", track_num
+ 1, tcs
.result
);
814 len
= tcs
.result_len
;
815 rb
->strncpy(track_names
+ string_index
, tcs
.result
, avail
);
821 if (!free_slide_prio(0))
824 buflib_buffer_out(&buf_ctx
, &out
);
829 struct track_data
*new_tracks
= (struct track_data
*)(out
+ (uintptr_t)tracks
);
830 unsigned int bytes
= track_count
* sizeof(struct track_data
);
831 rb
->memmove(new_tracks
, tracks
, bytes
);
840 tracks
->sort
= ((disc_num
- 1) << 24) + (track_num
<< 14) + track_count
;
841 tracks
->name_idx
= string_index
;
842 tracks
->seek
= tcs
.result_seek
;
847 rb
->tagcache_search_finish(&tcs
);
849 /* now fix the track list order */
850 rb
->qsort(tracks
, track_count
, sizeof(struct track_data
), compare_tracks
);
858 Determine filename of the album art for the given slide_index and
859 store the result in buf.
860 The algorithm looks for the first track of the given album uses
861 find_albumart to find the filename.
863 bool get_albumart_for_index_from_db(const int slide_index
, char *buf
,
866 if ( slide_index
== -1 )
868 rb
->strncpy( buf
, EMPTY_SLIDE
, buflen
);
871 if (!rb
->tagcache_search(&tcs
, tag_filename
))
875 /* find the first track of the album */
876 rb
->tagcache_search_add_filter(&tcs
, tag_album
, album
[slide_index
].seek
);
878 if ( rb
->tagcache_get_next(&tcs
) ) {
882 #ifdef HAVE_TC_RAMCACHE
883 if (rb
->tagcache_fill_tags(&id3
, tcs
.result
))
885 rb
->strncpy(id3
.path
, tcs
.result
, sizeof(id3
.path
));
886 id3
.path
[sizeof(id3
.path
) - 1] = 0;
891 fd
= rb
->open(tcs
.result
, O_RDONLY
);
892 rb
->get_metadata(&id3
, fd
, tcs
.result
);
895 if ( search_albumart_files(&id3
, "", buf
, buflen
) )
901 /* did not find a matching track */
904 rb
->tagcache_search_finish(&tcs
);
909 Draw the PictureFlow logo
911 void draw_splashscreen(void)
913 unsigned char * buf_tmp
= buf
;
914 size_t buf_tmp_size
= buf_size
;
915 struct screen
* display
= rb
->screens
[0];
917 ALIGN_BUFFER(buf_tmp
, buf_tmp_size
, sizeof(fb_data
));
919 struct bitmap logo
= {
929 int ret
= rb
->read_bmp_file(SPLASH_BMP
, &logo
, buf_tmp_size
, FORMAT_NATIVE
,
932 rb
->lcd_set_background(N_BRIGHT(0));
933 rb
->lcd_set_foreground(N_BRIGHT(255));
935 rb
->lcd_set_drawmode(PICTUREFLOW_DRMODE
);
937 rb
->lcd_clear_display();
941 #if LCD_DEPTH == 1 /* Mono LCDs need the logo inverted */
942 rb
->lcd_set_drawmode(PICTUREFLOW_DRMODE
^ DRMODE_INVERSEVID
);
944 display
->bitmap(logo
.data
, (LCD_WIDTH
- logo
.width
) / 2, 10,
945 logo
.width
, logo
.height
);
946 #if LCD_DEPTH == 1 /* Mono LCDs need the logo inverted */
947 rb
->lcd_set_drawmode(PICTUREFLOW_DRMODE
);
956 Draw a simple progress bar
958 void draw_progressbar(int step
)
961 const int bar_height
= 22;
962 const int w
= LCD_WIDTH
- 20;
965 rb
->lcd_getstringsize("Preparing album artwork", &txt_w
, &txt_h
);
967 int y
= (LCD_HEIGHT
- txt_h
)/2;
969 rb
->lcd_putsxy((LCD_WIDTH
- txt_w
)/2, y
, "Preparing album artwork");
973 rb
->lcd_set_foreground(N_BRIGHT(100));
975 rb
->lcd_drawrect(x
, y
, w
+2, bar_height
);
977 rb
->lcd_set_foreground(N_PIX(165, 231, 82));
980 rb
->lcd_fillrect(x
+1, y
+1, step
* w
/ album_count
, bar_height
-2);
982 rb
->lcd_set_foreground(N_BRIGHT(255));
989 Precomupte the album art images and store them in CACHE_PREFIX.
991 bool create_albumart_cache(void)
996 struct bitmap input_bmp
;
998 char pfraw_file
[MAX_PATH
];
999 char albumart_file
[MAX_PATH
];
1000 unsigned int format
= FORMAT_NATIVE
;
1002 configfile_save(CONFIG_FILE
, config
, CONFIG_NUM_ITEMS
, CONFIG_VERSION
);
1004 format
|= FORMAT_RESIZE
|FORMAT_KEEP_ASPECT
;
1005 for (i
=0; i
< album_count
; i
++)
1007 rb
->snprintf(pfraw_file
, sizeof(pfraw_file
), CACHE_PREFIX
"/%d.pfraw",
1009 /* delete existing cache, so it's a true rebuild */
1010 if(rb
->file_exists(pfraw_file
))
1011 rb
->remove(pfraw_file
);
1012 draw_progressbar(i
);
1013 if (!get_albumart_for_index_from_db(i
, albumart_file
, MAX_PATH
))
1016 input_bmp
.data
= buf
;
1017 input_bmp
.width
= DISPLAY_WIDTH
;
1018 input_bmp
.height
= DISPLAY_HEIGHT
;
1019 ret
= read_image_file(albumart_file
, &input_bmp
,
1020 buf_size
, format
, &format_transposed
);
1022 rb
->splash(HZ
, "Could not read bmp");
1023 continue; /* skip missing/broken files */
1025 if (!save_pfraw(pfraw_file
, &input_bmp
))
1027 rb
->splash(HZ
, "Could not write bmp");
1030 if ( rb
->button_get(false) == PF_MENU
) return false;
1032 if ( slides
== 0 ) {
1033 /* Warn the user that we couldn't find any albumart */
1034 rb
->splash(2*HZ
, "No album art found");
1041 Thread used for loading and preparing bitmaps in the background
1045 long sleep_time
= 5 * HZ
;
1046 struct queue_event ev
;
1048 rb
->queue_wait_w_tmo(&thread_q
, &ev
, sleep_time
);
1053 /* we just woke up */
1056 while ( load_new_slide() ) {
1068 End the thread by posting the EV_EXIT event
1070 void end_pf_thread(void)
1072 if ( thread_is_running
) {
1073 rb
->queue_post(&thread_q
, EV_EXIT
, 0);
1074 rb
->thread_wait(thread_id
);
1075 /* remove the thread's queue from the broadcast list */
1076 rb
->queue_delete(&thread_q
);
1077 thread_is_running
= false;
1084 Create the thread an setup the event queue
1086 bool create_pf_thread(void)
1088 /* put the thread's queue in the bcast list */
1089 rb
->queue_init(&thread_q
, true);
1090 if ((thread_id
= rb
->create_thread(
1093 sizeof(thread_stack
),
1095 "Picture load thread"
1096 IF_PRIO(, MAX(PRIORITY_USER_INTERFACE
/ 2,
1097 PRIORITY_REALTIME
+ 1))
1103 thread_is_running
= true;
1104 rb
->queue_post(&thread_q
, EV_WAKEUP
, 0);
1109 Safe the given bitmap as filename in the pfraw format
1111 bool save_pfraw(char* filename
, struct bitmap
*bm
)
1113 struct pfraw_header bmph
;
1114 bmph
.width
= bm
->width
;
1115 bmph
.height
= bm
->height
;
1116 int fh
= rb
->creat( filename
);
1117 if( fh
< 0 ) return false;
1118 rb
->write( fh
, &bmph
, sizeof( struct pfraw_header
) );
1120 for( y
= 0; y
< bm
->height
; y
++ )
1122 pix_t
*d
= (pix_t
*)( bm
->data
) + (y
*bm
->width
);
1123 rb
->write( fh
, d
, sizeof( pix_t
) * bm
->width
);
1131 * The following functions implement the linked-list-in-array used to manage
1132 * the LRU cache of slides, and the list of free cache slots.
1135 #define seek_right_while(start, cond) \
1137 int ind_, next_ = (start); \
1140 next_ = cache[ind_].next; \
1141 } while (next_ != cache_used && (cond)); \
1145 #define seek_left_while(start, cond) \
1147 int ind_, next_ = (start); \
1150 next_ = cache[ind_].prev; \
1151 } while (ind_ != cache_used && (cond)); \
1156 Pop the given item from the linked list starting at *head, returning the next
1157 item, or -1 if the list is now empty.
1159 static inline int lla_pop_item (int *head
, int i
)
1161 int prev
= cache
[i
].prev
;
1162 int next
= cache
[i
].next
;
1168 else if (i
== *head
)
1170 cache
[next
].prev
= prev
;
1171 cache
[prev
].next
= next
;
1177 Pop the head item from the list starting at *head, returning the index of the
1178 item, or -1 if the list is already empty.
1180 static inline int lla_pop_head (int *head
)
1184 lla_pop_item(head
, i
);
1189 Insert the item at index i before the one at index p.
1191 static inline void lla_insert (int i
, int p
)
1194 int prev
= cache
[next
].prev
;
1195 cache
[next
].prev
= i
;
1196 cache
[prev
].next
= i
;
1197 cache
[i
].next
= next
;
1198 cache
[i
].prev
= prev
;
1203 Insert the item at index i at the end of the list starting at *head.
1205 static inline void lla_insert_tail (int *head
, int i
)
1213 lla_insert(i
, *head
);
1217 Insert the item at index i before the one at index p.
1219 static inline void lla_insert_after(int i
, int p
)
1227 Insert the item at index i before the one at index p in the list starting at
1230 static inline void lla_insert_before(int *head
, int i
, int p
)
1239 Free the used slide at index i, and its buffer, and move it to the free
1242 static inline void free_slide(int i
)
1244 if (cache
[i
].hid
!= empty_slide_hid
)
1245 buflib_free(&buf_ctx
, cache
[i
].hid
);
1246 cache
[i
].index
= -1;
1247 lla_pop_item(&cache_used
, i
);
1248 lla_insert_tail(&cache_free
, i
);
1249 if (cache_used
== -1)
1251 cache_right_index
= -1;
1252 cache_left_index
= -1;
1253 cache_center_index
= -1;
1259 Free one slide ranked above the given priority. If no such slide can be found,
1262 static bool free_slide_prio(int prio
)
1264 if (cache_used
== -1)
1266 int i
, l
= cache_used
, r
= cache
[cache_used
].prev
, prio_max
;
1267 int prio_l
= cache
[l
].index
< center_index
?
1268 center_index
- cache
[l
].index
: 0;
1269 int prio_r
= cache
[r
].index
> center_index
?
1270 cache
[r
].index
- center_index
: 0;
1271 if (prio_l
> prio_r
)
1279 if (prio_max
> prio
)
1281 if (i
== cache_left_index
)
1282 cache_left_index
= cache
[i
].next
;
1283 if (i
== cache_right_index
)
1284 cache_right_index
= cache
[i
].prev
;
1292 Read the pfraw image given as filename and return the hid of the buffer
1294 int read_pfraw(char* filename
, int prio
)
1296 struct pfraw_header bmph
;
1297 int fh
= rb
->open(filename
, O_RDONLY
);
1299 return empty_slide_hid
;
1301 rb
->read(fh
, &bmph
, sizeof(struct pfraw_header
));
1303 int size
= sizeof(struct bitmap
) + sizeof( pix_t
) *
1304 bmph
.width
* bmph
.height
;
1307 while (!(hid
= buflib_alloc(&buf_ctx
, size
)) && free_slide_prio(prio
));
1314 struct dim
*bm
= buflib_get_data(&buf_ctx
, hid
);
1316 bm
->width
= bmph
.width
;
1317 bm
->height
= bmph
.height
;
1318 pix_t
*data
= (pix_t
*)(sizeof(struct dim
) + (char *)bm
);
1321 for( y
= 0; y
< bm
->height
; y
++ )
1323 rb
->read( fh
, data
, sizeof( pix_t
) * bm
->width
);
1332 Load the surface for the given slide_index into the cache at cache_index.
1334 static inline bool load_and_prepare_surface(const int slide_index
,
1335 const int cache_index
,
1338 char tmp_path_name
[MAX_PATH
+1];
1339 rb
->snprintf(tmp_path_name
, sizeof(tmp_path_name
), CACHE_PREFIX
"/%d.pfraw",
1342 int hid
= read_pfraw(tmp_path_name
, prio
);
1346 cache
[cache_index
].hid
= hid
;
1348 if ( cache_index
< SLIDE_CACHE_SIZE
) {
1349 cache
[cache_index
].index
= slide_index
;
1357 Load the "next" slide that we can load, freeing old slides if needed, provided
1358 that they are further from center_index than the current slide
1360 bool load_new_slide(void)
1363 if (cache_center_index
!= -1)
1366 if (cache
[cache_center_index
].index
!= center_index
)
1368 if (cache
[cache_center_index
].index
< center_index
)
1370 cache_center_index
= seek_right_while(cache_center_index
,
1371 cache
[next_
].index
<= center_index
);
1372 prev
= cache_center_index
;
1373 next
= cache
[cache_center_index
].next
;
1377 cache_center_index
= seek_left_while(cache_center_index
,
1378 cache
[next_
].index
>= center_index
);
1379 next
= cache_center_index
;
1380 prev
= cache
[cache_center_index
].prev
;
1382 if (cache
[cache_center_index
].index
!= center_index
)
1384 if (cache_free
== -1)
1386 i
= lla_pop_head(&cache_free
);
1387 if (!load_and_prepare_surface(center_index
, i
, 0))
1388 goto fail_and_refree
;
1389 if (cache
[next
].index
== -1)
1391 if (cache
[prev
].index
== -1)
1392 goto insert_first_slide
;
1394 next
= cache
[prev
].next
;
1396 lla_insert(i
, next
);
1397 if (cache
[i
].index
< cache
[cache_used
].index
)
1399 cache_center_index
= i
;
1400 cache_left_index
= i
;
1401 cache_right_index
= i
;
1405 if (cache
[cache_left_index
].index
>
1406 cache
[cache_center_index
].index
)
1407 cache_left_index
= cache_center_index
;
1408 if (cache
[cache_right_index
].index
<
1409 cache
[cache_center_index
].index
)
1410 cache_right_index
= cache_center_index
;
1411 cache_left_index
= seek_left_while(cache_left_index
,
1412 cache
[ind_
].index
- 1 == cache
[next_
].index
);
1413 cache_right_index
= seek_right_while(cache_right_index
,
1414 cache
[ind_
].index
- 1 == cache
[next_
].index
);
1415 int prio_l
= cache
[cache_center_index
].index
-
1416 cache
[cache_left_index
].index
+ 1;
1417 int prio_r
= cache
[cache_right_index
].index
-
1418 cache
[cache_center_index
].index
+ 1;
1419 if ((prio_l
< prio_r
||
1420 cache
[cache_right_index
].index
>= number_of_slides
) &&
1421 cache
[cache_left_index
].index
> 0)
1423 if (cache_free
== -1 && !free_slide_prio(prio_l
))
1425 i
= lla_pop_head(&cache_free
);
1426 if (load_and_prepare_surface(cache
[cache_left_index
].index
1429 lla_insert_before(&cache_used
, i
, cache_left_index
);
1430 cache_left_index
= i
;
1433 } else if(cache
[cache_right_index
].index
< number_of_slides
- 1)
1435 if (cache_free
== -1 && !free_slide_prio(prio_r
))
1437 i
= lla_pop_head(&cache_free
);
1438 if (load_and_prepare_surface(cache
[cache_right_index
].index
1441 lla_insert_after(i
, cache_right_index
);
1442 cache_right_index
= i
;
1447 i
= lla_pop_head(&cache_free
);
1448 if (load_and_prepare_surface(center_index
, i
, 0))
1453 cache_center_index
= i
;
1454 cache_left_index
= i
;
1455 cache_right_index
= i
;
1463 lla_insert_tail(&cache_free
, i
);
1470 Get a slide from the buffer
1472 static inline struct dim
*get_slide(const int hid
)
1479 bmp
= buflib_get_data(&buf_ctx
, hid
);
1486 Return the requested surface
1488 static inline struct dim
*surface(const int slide_index
)
1490 if (slide_index
< 0)
1492 if (slide_index
>= number_of_slides
)
1495 if ((i
= cache_used
) != -1)
1498 if (cache
[i
].index
== slide_index
)
1499 return get_slide(cache
[i
].hid
);
1501 } while (i
!= cache_used
);
1503 return get_slide(empty_slide_hid
);
1507 adjust slides so that they are in "steady state" position
1509 void reset_slides(void)
1511 center_slide
.angle
= 0;
1512 center_slide
.cx
= 0;
1513 center_slide
.cy
= 0;
1514 center_slide
.distance
= 0;
1515 center_slide
.slide_index
= center_index
;
1518 for (i
= 0; i
< num_slides
; i
++) {
1519 struct slide_data
*si
= &left_slides
[i
];
1521 si
->cx
= -(offsetX
+ slide_spacing
* i
* PFREAL_ONE
);
1523 si
->slide_index
= center_index
- 1 - i
;
1527 for (i
= 0; i
< num_slides
; i
++) {
1528 struct slide_data
*si
= &right_slides
[i
];
1530 si
->cx
= offsetX
+ slide_spacing
* i
* PFREAL_ONE
;
1532 si
->slide_index
= center_index
+ 1 + i
;
1539 Updates look-up table and other stuff necessary for the rendering.
1540 Call this when the viewport size or slide dimension is changed.
1542 * To calculate the offset that will provide the proper margin, we use the same
1543 * projection used to render the slides. The solution for xc, the slide center,
1545 * xp * (zo + xs * sin(r))
1546 * xc = xp - xs * cos(r) + ───────────────────────
1548 * TODO: support moving the side slides toward or away from the camera
1550 void recalc_offsets(void)
1552 PFreal xs
= PFREAL_HALF
- DISPLAY_WIDTH
* PFREAL_HALF
;
1554 PFreal xp
= (DISPLAY_WIDTH
* PFREAL_HALF
- PFREAL_HALF
+ center_margin
*
1555 PFREAL_ONE
) * zoom
/ 100;
1558 itilt
= 70 * IANGLE_MAX
/ 360; /* approx. 70 degrees tilted */
1559 cosr
= fcos(-itilt
);
1560 sinr
= fsin(-itilt
);
1561 zo
= CAM_DIST_R
* 100 / zoom
- CAM_DIST_R
+
1562 fmuln(MAXSLIDE_LEFT_R
, sinr
, PFREAL_SHIFT
- 2, 0);
1563 offsetX
= xp
- fmul(xs
, cosr
) + fmuln(xp
,
1564 zo
+ fmuln(xs
, sinr
, PFREAL_SHIFT
- 2, 0), PFREAL_SHIFT
- 2, 0)
1566 offsetY
= DISPLAY_WIDTH
/ 2 * (fsin(itilt
) + PFREAL_ONE
/ 2);
1571 Fade the given color by spreading the fb_data (ushort)
1572 to an uint, multiply and compress the result back to a ushort.
1574 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
1575 static inline unsigned fade_color(pix_t c
, unsigned a
)
1577 unsigned int result
;
1579 a
= (a
+ 2) & 0x1fc;
1580 result
= ((c
& 0xf81f) * a
) & 0xf81f00;
1581 result
|= ((c
& 0x7e0) * a
) & 0x7e000;
1583 return swap16(result
);
1585 #elif LCD_PIXELFORMAT == RGB565
1586 static inline unsigned fade_color(pix_t c
, unsigned a
)
1588 unsigned int result
;
1589 a
= (a
+ 2) & 0x1fc;
1590 result
= ((c
& 0xf81f) * a
) & 0xf81f00;
1591 result
|= ((c
& 0x7e0) * a
) & 0x7e000;
1596 static inline unsigned fade_color(pix_t c
, unsigned a
)
1599 return MULUQ(val
, a
) >> 8;
1604 * Render a single slide
1605 * Where xc is the slide's horizontal offset from center, xs is the horizontal
1606 * on the slide from its center, zo is the slide's depth offset from the plane
1607 * of the display, r is the angle at which the slide is tilted, and xp is the
1608 * point on the display corresponding to xs on the slide, the projection
1611 * z * (xc + xs * cos(r))
1612 * xp = ──────────────────────
1613 * z + zo + xs * sin(r)
1615 * z * (xc - xp) - xp * zo
1616 * xs = ────────────────────────
1617 * xp * sin(r) - z * cos(r)
1619 * We use the xp projection once, to find the left edge of the slide on the
1620 * display. From there, we use the xs reverse projection to find the horizontal
1621 * offset from the slide center of each column on the screen, until we reach
1622 * the right edge of the slide, or the screen. The reverse projection can be
1623 * optimized by saving the numerator and denominator of the fraction, which can
1624 * then be incremented by (z + zo) and sin(r) respectively.
1626 void render_slide(struct slide_data
*slide
, const int alpha
)
1628 struct dim
*bmp
= surface(slide
->slide_index
);
1632 if (slide
->angle
> 255 || slide
->angle
< -255)
1634 pix_t
*src
= (pix_t
*)(sizeof(struct dim
) + (char *)bmp
);
1636 const int sw
= bmp
->width
;
1637 const int sh
= bmp
->height
;
1638 const PFreal slide_left
= -sw
* PFREAL_HALF
+ PFREAL_HALF
;
1639 const int w
= LCD_WIDTH
;
1641 uint8_t reftab
[REFLECT_HEIGHT
]; /* on stack, which is in IRAM on several targets */
1643 if (alpha
== 256) { /* opaque -> copy table */
1644 rb
->memcpy(reftab
, reflect_table
, sizeof(reftab
));
1645 } else { /* precalculate faded table */
1647 for (i
= 0; i
< REFLECT_HEIGHT
; i
++) {
1648 lalpha
= reflect_table
[i
];
1649 reftab
[i
] = (MULUQ(lalpha
, alpha
) + 129) >> 8;
1653 PFreal cosr
= fcos(slide
->angle
);
1654 PFreal sinr
= fsin(slide
->angle
);
1655 PFreal zo
= PFREAL_ONE
* slide
->distance
+ CAM_DIST_R
* 100 / zoom
1656 - CAM_DIST_R
- fmuln(MAXSLIDE_LEFT_R
, fabs(sinr
), PFREAL_SHIFT
- 2, 0);
1657 PFreal xs
= slide_left
, xsnum
, xsnumi
, xsden
, xsdeni
;
1658 PFreal xp
= fdiv(CAM_DIST
* (slide
->cx
+ fmul(xs
, cosr
)),
1659 (CAM_DIST_R
+ zo
+ fmul(xs
,sinr
)));
1661 /* Since we're finding the screen position of the left edge of the slide,
1664 int xi
= (fmax(DISPLAY_LEFT_R
, xp
) - DISPLAY_LEFT_R
+ PFREAL_ONE
- 1)
1666 xp
= DISPLAY_LEFT_R
+ xi
* PFREAL_ONE
;
1670 xsnum
= CAM_DIST
* (slide
->cx
- xp
) - fmuln(xp
, zo
, PFREAL_SHIFT
- 2, 0);
1671 xsden
= fmuln(xp
, sinr
, PFREAL_SHIFT
- 2, 0) - CAM_DIST
* cosr
;
1672 xs
= fdiv(xsnum
, xsden
);
1674 xsnumi
= -CAM_DIST_R
- zo
;
1677 int dy
= PFREAL_ONE
;
1678 for (x
= xi
; x
< w
; x
++) {
1679 int column
= (xs
- slide_left
) / PFREAL_ONE
;
1682 if (zo
|| slide
->angle
)
1683 dy
= (CAM_DIST_R
+ zo
+ fmul(xs
, sinr
)) / CAM_DIST
;
1685 const pix_t
*ptr
= &src
[column
* bmp
->height
];
1686 const int pixelstep
= BUFFER_WIDTH
;
1688 int p
= (bmp
->height
-1-DISPLAY_OFFS
) * PFREAL_ONE
;
1689 int plim
= MAX(0, p
- (LCD_HEIGHT
/2-1) * dy
);
1690 pix_t
*pixel
= &buffer
[((LCD_HEIGHT
/2)-1)*BUFFER_WIDTH
+ x
];
1694 *pixel
= ptr
[((unsigned)p
) >> PFREAL_SHIFT
];
1700 *pixel
= fade_color(ptr
[((unsigned)p
) >> PFREAL_SHIFT
], alpha
);
1705 p
= (bmp
->height
-DISPLAY_OFFS
) * PFREAL_ONE
;
1706 plim
= MIN(sh
* PFREAL_ONE
, p
+ (LCD_HEIGHT
/2) * dy
);
1707 int plim2
= MIN(MIN(sh
+ REFLECT_HEIGHT
, sh
* 2) * PFREAL_ONE
,
1708 p
+ (LCD_HEIGHT
/2) * dy
);
1709 pixel
= &buffer
[(LCD_HEIGHT
/2)*BUFFER_WIDTH
+ x
];
1713 *pixel
= ptr
[((unsigned)p
) >> PFREAL_SHIFT
];
1719 *pixel
= fade_color(ptr
[((unsigned)p
) >> PFREAL_SHIFT
], alpha
);
1725 int ty
= (((unsigned)p
) >> PFREAL_SHIFT
) - sh
;
1726 int lalpha
= reftab
[ty
];
1727 *pixel
= fade_color(ptr
[sh
- 1 - ty
], lalpha
);
1732 if (zo
|| slide
->angle
)
1736 xs
= fdiv(xsnum
, xsden
);
1741 /* let the music play... */
1748 Jump the the given slide_index
1750 static inline void set_current_slide(const int slide_index
)
1752 int old_center_index
= center_index
;
1754 center_index
= fbound(slide_index
, 0, number_of_slides
- 1);
1755 if (old_center_index
!= center_index
)
1756 rb
->queue_post(&thread_q
, EV_WAKEUP
, 0);
1757 target
= center_index
;
1758 slide_frame
= slide_index
<< 16;
1763 Start the animation for changing slides
1765 void start_animation(void)
1767 step
= (target
< center_slide
.slide_index
) ? -1 : 1;
1768 pf_state
= pf_scrolling
;
1772 Go to the previous slide
1774 void show_previous_slide(void)
1777 if (center_index
> 0) {
1778 target
= center_index
- 1;
1781 } else if ( step
> 0 ) {
1782 target
= center_index
;
1785 target
= fmax(0, center_index
- 2);
1791 Go to the next slide
1793 void show_next_slide(void)
1796 if (center_index
< number_of_slides
- 1) {
1797 target
= center_index
+ 1;
1800 } else if ( step
< 0 ) {
1801 target
= center_index
;
1804 target
= fmin(center_index
+ 2, number_of_slides
- 1);
1810 Render the slides. Updates only the offscreen buffer.
1812 void render_all_slides(void)
1814 MYLCD(set_background
)(G_BRIGHT(0));
1815 /* TODO: Optimizes this by e.g. invalidating rects */
1816 MYLCD(clear_display
)();
1818 int nleft
= num_slides
;
1819 int nright
= num_slides
;
1823 /* no animation, boring plain rendering */
1824 for (index
= nleft
- 2; index
>= 0; index
--) {
1825 int alpha
= (index
< nleft
- 2) ? 256 : 128;
1826 alpha
-= extra_fade
;
1828 render_slide(&left_slides
[index
], alpha
);
1830 for (index
= nright
- 2; index
>= 0; index
--) {
1831 int alpha
= (index
< nright
- 2) ? 256 : 128;
1832 alpha
-= extra_fade
;
1834 render_slide(&right_slides
[index
], alpha
);
1837 /* the first and last slide must fade in/fade out */
1838 for (index
= nleft
- 1; index
>= 0; index
--) {
1840 if (index
== nleft
- 1)
1841 alpha
= (step
> 0) ? 0 : 128 - fade
/ 2;
1842 if (index
== nleft
- 2)
1843 alpha
= (step
> 0) ? 128 - fade
/ 2 : 256 - fade
/ 2;
1844 if (index
== nleft
- 3)
1845 alpha
= (step
> 0) ? 256 - fade
/ 2 : 256;
1846 render_slide(&left_slides
[index
], alpha
);
1848 for (index
= nright
- 1; index
>= 0; index
--) {
1849 int alpha
= (index
< nright
- 2) ? 256 : 128;
1850 if (index
== nright
- 1)
1851 alpha
= (step
> 0) ? fade
/ 2 : 0;
1852 if (index
== nright
- 2)
1853 alpha
= (step
> 0) ? 128 + fade
/ 2 : fade
/ 2;
1854 if (index
== nright
- 3)
1855 alpha
= (step
> 0) ? 256 : 128 + fade
/ 2;
1856 render_slide(&right_slides
[index
], alpha
);
1859 render_slide(¢er_slide
, 256);
1864 Updates the animation effect. Call this periodically from a timer.
1866 void update_scroll_animation(void)
1874 /* deaccelerate when approaching the target */
1876 const int max
= 2 * 65536;
1878 int fi
= slide_frame
;
1879 fi
-= (target
<< 16);
1884 int ia
= IANGLE_MAX
* (fi
- max
/ 2) / (max
* 2);
1885 speed
= 512 + 16384 * (PFREAL_ONE
+ fsin(ia
)) / PFREAL_ONE
;
1888 slide_frame
+= speed
* step
;
1890 int index
= slide_frame
>> 16;
1891 int pos
= slide_frame
& 0xffff;
1892 int neg
= 65536 - pos
;
1893 int tick
= (step
< 0) ? neg
: pos
;
1894 PFreal ftick
= (tick
* PFREAL_ONE
) >> 16;
1896 /* the leftmost and rightmost slide must fade away */
1901 if (center_index
!= index
) {
1902 center_index
= index
;
1903 rb
->queue_post(&thread_q
, EV_WAKEUP
, 0);
1904 slide_frame
= index
<< 16;
1905 center_slide
.slide_index
= center_index
;
1906 for (i
= 0; i
< num_slides
; i
++)
1907 left_slides
[i
].slide_index
= center_index
- 1 - i
;
1908 for (i
= 0; i
< num_slides
; i
++)
1909 right_slides
[i
].slide_index
= center_index
+ 1 + i
;
1912 center_slide
.angle
= (step
* tick
* itilt
) >> 16;
1913 center_slide
.cx
= -step
* fmul(offsetX
, ftick
);
1914 center_slide
.cy
= fmul(offsetY
, ftick
);
1916 if (center_index
== target
) {
1924 for (i
= 0; i
< num_slides
; i
++) {
1925 struct slide_data
*si
= &left_slides
[i
];
1928 -(offsetX
+ slide_spacing
* i
* PFREAL_ONE
+ step
1929 * slide_spacing
* ftick
);
1933 for (i
= 0; i
< num_slides
; i
++) {
1934 struct slide_data
*si
= &right_slides
[i
];
1937 offsetX
+ slide_spacing
* i
* PFREAL_ONE
- step
1938 * slide_spacing
* ftick
;
1943 PFreal ftick
= (neg
* PFREAL_ONE
) >> 16;
1944 right_slides
[0].angle
= -(neg
* itilt
) >> 16;
1945 right_slides
[0].cx
= fmul(offsetX
, ftick
);
1946 right_slides
[0].cy
= fmul(offsetY
, ftick
);
1948 PFreal ftick
= (pos
* PFREAL_ONE
) >> 16;
1949 left_slides
[0].angle
= (pos
* itilt
) >> 16;
1950 left_slides
[0].cx
= -fmul(offsetX
, ftick
);
1951 left_slides
[0].cy
= fmul(offsetY
, ftick
);
1954 /* must change direction ? */
1967 void cleanup(void *parameter
)
1970 /* Turn on backlight timeout (revert to settings) */
1971 backlight_use_settings(); /* backlight control in lib/helper.c */
1979 Create the "?" slide, that is shown while loading
1980 or when no cover was found.
1982 int create_empty_slide(bool force
)
1984 if ( force
|| ! rb
->file_exists( EMPTY_SLIDE
) ) {
1985 struct bitmap input_bmp
;
1987 input_bmp
.width
= DISPLAY_WIDTH
;
1988 input_bmp
.height
= DISPLAY_HEIGHT
;
1990 input_bmp
.format
= FORMAT_NATIVE
;
1992 input_bmp
.data
= (char*)buf
;
1993 ret
= scaled_read_bmp_file(EMPTY_SLIDE_BMP
, &input_bmp
,
1995 FORMAT_NATIVE
|FORMAT_RESIZE
|FORMAT_KEEP_ASPECT
,
1996 &format_transposed
);
1997 if (!save_pfraw(EMPTY_SLIDE
, &input_bmp
))
2005 Shows the album name setting menu
2007 int album_name_menu(void)
2009 int selection
= show_album_name
;
2011 MENUITEM_STRINGLIST(album_name_menu
,"Show album title",NULL
,
2012 "Hide album title", "Show at the bottom", "Show at the top");
2013 rb
->do_menu(&album_name_menu
, &selection
, NULL
, false);
2015 show_album_name
= selection
;
2016 return GO_TO_PREVIOUS
;
2020 Shows the settings menu
2022 int settings_menu(void)
2027 MENUITEM_STRINGLIST(settings_menu
, "PictureFlow Settings", NULL
, "Show FPS",
2028 "Spacing", "Centre margin", "Number of slides", "Zoom",
2029 "Show album title", "Resize Covers", "Rebuild cache");
2032 selection
=rb
->do_menu(&settings_menu
,&selection
, NULL
, false);
2035 rb
->set_bool("Show FPS", &show_fps
);
2040 rb
->set_int("Spacing between slides", "", 1,
2042 NULL
, 1, 0, 100, NULL
);
2048 rb
->set_int("Centre margin", "", 1,
2050 NULL
, 1, 0, 80, NULL
);
2056 rb
->set_int("Number of slides", "", 1, &num_slides
,
2057 NULL
, 1, 1, MAX_SLIDES_COUNT
, NULL
);
2063 rb
->set_int("Zoom", "", 1, &zoom
,
2064 NULL
, 1, 10, 300, NULL
);
2076 rb
->set_bool("Resize Covers", &resize
);
2077 if (old_val
== resize
) /* changed? */
2079 /* fallthrough if changed, since cache needs to be rebuilt */
2082 rb
->remove(EMPTY_SLIDE
);
2083 rb
->splash(HZ
, "Cache will be rebuilt on next restart");
2086 case MENU_ATTACHED_USB
:
2087 return PLUGIN_USB_CONNECTED
;
2089 } while ( selection
>= 0 );
2090 configfile_save(CONFIG_FILE
, config
, CONFIG_NUM_ITEMS
, CONFIG_VERSION
);
2103 rb
->lcd_set_foreground(N_BRIGHT(255));
2106 MENUITEM_STRINGLIST(main_menu
,"PictureFlow Main Menu",NULL
,
2107 "Settings", "Return", "Quit");
2109 switch (rb
->do_menu(&main_menu
,&selection
, NULL
, false)) {
2111 result
= settings_menu();
2112 if ( result
!= 0 ) return result
;
2121 case MENU_ATTACHED_USB
:
2122 return PLUGIN_USB_CONNECTED
;
2131 Animation step for zooming into the current cover
2133 void update_cover_in_animation(void)
2135 cover_animation_keyframe
++;
2136 if( cover_animation_keyframe
< 20 ) {
2137 center_slide
.distance
-=5;
2138 center_slide
.angle
+=1;
2141 else if( cover_animation_keyframe
< 35 ) {
2142 center_slide
.angle
+=16;
2145 cover_animation_keyframe
= 0;
2146 pf_state
= pf_show_tracks
;
2151 Animation step for zooming out the current cover
2153 void update_cover_out_animation(void)
2155 cover_animation_keyframe
++;
2156 if( cover_animation_keyframe
<= 15 ) {
2157 center_slide
.angle
-=16;
2159 else if( cover_animation_keyframe
< 35 ) {
2160 center_slide
.distance
+=5;
2161 center_slide
.angle
-=1;
2165 cover_animation_keyframe
= 0;
2171 Draw a blue gradient at y with height h
2173 static inline void draw_gradient(int y
, int h
)
2175 static int r
, inc
, c
;
2176 inc
= (100 << 8) / h
;
2178 selected_track_pulse
= (selected_track_pulse
+1) % 10;
2179 int c2
= selected_track_pulse
- 5;
2180 for (r
=0; r
<h
; r
++) {
2181 #ifdef HAVE_LCD_COLOR
2182 MYLCD(set_foreground
)(G_PIX(c2
+80-(c
>> 9), c2
+100-(c
>> 9),
2185 MYLCD(set_foreground
)(G_BRIGHT(c2
+160-(c
>> 8)));
2187 MYLCD(hline
)(0, LCD_WIDTH
, r
+y
);
2196 static void track_list_yh(int char_height
)
2198 switch (show_album_name
)
2200 case album_name_hide
:
2201 track_list_y
= (show_fps
? char_height
: 0);
2202 track_list_h
= LCD_HEIGHT
- track_list_y
;
2204 case album_name_bottom
:
2205 track_list_y
= (show_fps
? char_height
: 0);
2206 track_list_h
= LCD_HEIGHT
- track_list_y
- char_height
* 2;
2208 default: /* case album_name_top */
2209 track_list_y
= char_height
* 2;
2210 track_list_h
= LCD_HEIGHT
- track_list_y
-
2211 (show_fps
? char_height
: 0);
2217 Reset the track list after a album change
2219 void reset_track_list(void)
2221 int albumtxt_h
= rb
->screens
[SCREEN_MAIN
]->getcharheight();
2222 track_list_yh(albumtxt_h
);
2223 track_list_visible_entries
= fmin( track_list_h
/albumtxt_h
, track_count
);
2224 start_index_track_list
= 0;
2225 track_scroll_index
= 0;
2226 track_scroll_dir
= 1;
2229 /* let the tracklist start more centered
2230 * if the screen isn't filled with tracks */
2231 if (track_count
*albumtxt_h
< track_list_h
)
2233 track_list_h
= track_count
* albumtxt_h
;
2234 track_list_y
= LCD_HEIGHT
/ 2 - (track_list_h
/ 2);
2239 Display the list of tracks
2241 void show_track_list(void)
2243 MYLCD(clear_display
)();
2244 if ( center_slide
.slide_index
!= track_index
) {
2245 create_track_index(center_slide
.slide_index
);
2248 static int titletxt_w
, titletxt_x
, color
, titletxt_h
;
2249 titletxt_h
= rb
->screens
[SCREEN_MAIN
]->getcharheight();
2251 int titletxt_y
= track_list_y
;
2253 track_i
= start_index_track_list
;
2254 for (;track_i
< track_list_visible_entries
+start_index_track_list
;
2257 MYLCD(getstringsize
)(get_track_name(track_i
), &titletxt_w
, NULL
);
2258 titletxt_x
= (LCD_WIDTH
-titletxt_w
)/2;
2259 if ( track_i
== selected_track
) {
2260 draw_gradient(titletxt_y
, titletxt_h
);
2261 MYLCD(set_foreground
)(G_BRIGHT(255));
2262 if (titletxt_w
> LCD_WIDTH
) {
2263 if ( titletxt_w
+ track_scroll_index
<= LCD_WIDTH
)
2264 track_scroll_dir
= 1;
2265 else if ( track_scroll_index
>= 0 ) track_scroll_dir
= -1;
2266 track_scroll_index
+= track_scroll_dir
*2;
2267 titletxt_x
= track_scroll_index
;
2269 MYLCD(putsxy
)(titletxt_x
,titletxt_y
,get_track_name(track_i
));
2272 color
= 250 - (abs(selected_track
- track_i
) * 200 / track_count
);
2273 MYLCD(set_foreground
)(G_BRIGHT(color
));
2274 MYLCD(putsxy
)(titletxt_x
,titletxt_y
,get_track_name(track_i
));
2276 titletxt_y
+= titletxt_h
;
2280 void select_next_track(void)
2282 if ( selected_track
< track_count
- 1 ) {
2284 track_scroll_index
= 0;
2285 track_scroll_dir
= 1;
2286 if (selected_track
==(track_list_visible_entries
+start_index_track_list
))
2287 start_index_track_list
++;
2291 void select_prev_track(void)
2293 if (selected_track
> 0 ) {
2294 if (selected_track
==start_index_track_list
) start_index_track_list
--;
2295 track_scroll_index
= 0;
2296 track_scroll_dir
= 1;
2302 Draw the current album name
2304 void draw_album_text(void)
2306 if (0 == show_album_name
)
2309 int albumtxt_w
, albumtxt_h
;
2314 /* Draw album text */
2315 if ( pf_state
== pf_scrolling
) {
2316 c
= ((slide_frame
& 0xffff )/ 255);
2317 if (step
< 0) c
= 255-c
;
2318 if (c
> 128 ) { /* half way to next slide .. still not perfect! */
2319 albumtxt
= get_album_name(center_index
+step
);
2323 albumtxt
= get_album_name(center_index
);
2329 albumtxt
= get_album_name(center_index
);
2332 MYLCD(set_foreground
)(G_BRIGHT(c
));
2333 MYLCD(getstringsize
)(albumtxt
, &albumtxt_w
, &albumtxt_h
);
2334 if (center_index
!= prev_center_index
) {
2337 prev_center_index
= center_index
;
2340 if (show_album_name
== album_name_top
)
2341 albumtxt_y
= albumtxt_h
/ 2;
2343 albumtxt_y
= LCD_HEIGHT
- albumtxt_h
- albumtxt_h
/2;
2345 if (albumtxt_w
> LCD_WIDTH
) {
2346 MYLCD(putsxy
)(albumtxt_x
, albumtxt_y
, albumtxt
);
2347 if ( pf_state
== pf_idle
|| pf_state
== pf_show_tracks
) {
2348 if ( albumtxt_w
+ albumtxt_x
<= LCD_WIDTH
) albumtxt_dir
= 1;
2349 else if ( albumtxt_x
>= 0 ) albumtxt_dir
= -1;
2350 albumtxt_x
+= albumtxt_dir
;
2354 MYLCD(putsxy
)((LCD_WIDTH
- albumtxt_w
) /2, albumtxt_y
, albumtxt
);
2362 Main function that also contain the main plasma
2369 rb
->lcd_setfont(FONT_UI
);
2370 draw_splashscreen();
2372 if ( ! rb
->dir_exists( CACHE_PREFIX
) ) {
2373 if ( rb
->mkdir( CACHE_PREFIX
) < 0 ) {
2374 rb
->splash(HZ
, "Could not create directory " CACHE_PREFIX
);
2375 return PLUGIN_ERROR
;
2379 configfile_load(CONFIG_FILE
, config
, CONFIG_NUM_ITEMS
, CONFIG_VERSION
);
2381 init_reflect_table();
2383 ALIGN_BUFFER(buf
, buf_size
, 4);
2384 ret
= create_album_index();
2385 if (ret
== ERROR_BUFFER_FULL
) {
2386 rb
->splash(HZ
, "Not enough memory for album names");
2387 return PLUGIN_ERROR
;
2388 } else if (ret
== ERROR_NO_ALBUMS
) {
2389 rb
->splash(HZ
, "No albums found. Please enable database");
2390 return PLUGIN_ERROR
;
2393 ALIGN_BUFFER(buf
, buf_size
, 4);
2394 number_of_slides
= album_count
;
2395 if ((cache_version
!= CACHE_VERSION
) && !create_albumart_cache()) {
2396 rb
->splash(HZ
, "Could not create album art cache");
2397 return PLUGIN_ERROR
;
2400 if (!create_empty_slide(cache_version
!= CACHE_VERSION
)) {
2401 rb
->splash(HZ
, "Could not load the empty slide");
2402 return PLUGIN_ERROR
;
2404 cache_version
= CACHE_VERSION
;
2405 configfile_save(CONFIG_FILE
, config
, CONFIG_NUM_ITEMS
, CONFIG_VERSION
);
2410 if (!grey_init(buf
, buf_size
, GREY_BUFFERED
|GREY_ON_COP
,
2411 LCD_WIDTH
, LCD_HEIGHT
, &grey_buf_used
))
2413 rb
->splash(HZ
, "Greylib init failed!");
2414 return PLUGIN_ERROR
;
2416 grey_setfont(FONT_UI
);
2417 buf_size
-= grey_buf_used
;
2418 buf
= (void*)(grey_buf_used
+ (char*)buf
);
2420 buflib_init(&buf_ctx
, (void *)buf
, buf_size
);
2422 if (!(empty_slide_hid
= read_pfraw(EMPTY_SLIDE
, 0)))
2424 rb
->splash(HZ
, "Unable to load empty slide image");
2425 return PLUGIN_ERROR
;
2428 if (!create_pf_thread()) {
2429 rb
->splash(HZ
, "Cannot create thread!");
2430 return PLUGIN_ERROR
;
2436 for (i
= 0; i
< SLIDE_CACHE_SIZE
; i
++) {
2439 cache
[i
].next
= i
+ 1;
2440 cache
[i
].prev
= i
- 1;
2442 cache
[0].prev
= i
- 1;
2443 cache
[i
- 1].next
= 0;
2463 long last_update
= *rb
->current_tick
;
2464 long current_update
;
2465 long update_interval
= 100;
2469 bool instant_update
;
2472 grey_set_drawmode(DRMODE_FG
);
2474 rb
->lcd_set_drawmode(DRMODE_FG
);
2476 current_update
= *rb
->current_tick
;
2479 /* Initial rendering */
2480 instant_update
= false;
2483 switch ( pf_state
) {
2485 update_scroll_animation();
2486 render_all_slides();
2487 instant_update
= true;
2490 update_cover_in_animation();
2491 render_all_slides();
2492 instant_update
= true;
2495 update_cover_out_animation();
2496 render_all_slides();
2497 instant_update
= true;
2499 case pf_show_tracks
:
2503 render_all_slides();
2508 if (current_update
- last_update
> update_interval
) {
2509 fps
= frames
* HZ
/ (current_update
- last_update
);
2510 last_update
= current_update
;
2517 MYLCD(set_foreground
)(G_BRIGHT(255));
2519 MYLCD(set_foreground
)(G_PIX(255,0,0));
2521 rb
->snprintf(fpstxt
, sizeof(fpstxt
), "FPS: %d", fps
);
2522 if (show_album_name
== album_name_top
)
2523 fpstxt_y
= LCD_HEIGHT
-
2524 rb
->screens
[SCREEN_MAIN
]->getcharheight();
2527 MYLCD(putsxy
)(0, fpstxt_y
, fpstxt
);
2532 /* Copy offscreen buffer to LCD and give time to other threads */
2536 /*/ Handle buttons */
2537 button
= rb
->get_custom_action(CONTEXT_CUSTOM
|
2538 (pf_state
== pf_show_tracks
? 1 : 0),
2539 instant_update
? 0 : HZ
/16,
2547 if ( pf_state
== pf_show_tracks
)
2549 buflib_buffer_in(&buf_ctx
, borrowed
);
2552 pf_state
= pf_cover_out
;
2554 if (pf_state
== pf_idle
|| pf_state
== pf_scrolling
)
2563 if ( ret
== -1 ) return PLUGIN_OK
;
2564 if ( ret
!= 0 ) return ret
;
2568 MYLCD(set_drawmode
)(DRMODE_FG
);
2572 case PF_NEXT_REPEAT
:
2573 if ( pf_state
== pf_show_tracks
)
2574 select_next_track();
2575 if ( pf_state
== pf_idle
|| pf_state
== pf_scrolling
)
2580 case PF_PREV_REPEAT
:
2581 if ( pf_state
== pf_show_tracks
)
2582 select_prev_track();
2583 if ( pf_state
== pf_idle
|| pf_state
== pf_scrolling
)
2584 show_previous_slide();
2588 if ( pf_state
== pf_idle
) {
2589 pf_state
= pf_cover_in
;
2594 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
2595 == SYS_USB_CONNECTED
)
2596 return PLUGIN_USB_CONNECTED
;
2604 /*************************** Plugin entry point ****************************/
2606 enum plugin_status
plugin_start(const void *parameter
)
2611 rb
->lcd_set_backdrop(NULL
);
2613 /* Turn off backlight timeout */
2614 backlight_force_on(); /* backlight control in lib/helper.c */
2615 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
2616 rb
->cpu_boost(true);
2618 #if PLUGIN_BUFFER_SIZE > 0x10000
2619 buf
= rb
->plugin_get_buffer(&buf_size
);
2621 buf
= rb
->plugin_get_audio_buffer(&buf_size
);
2624 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
2625 rb
->cpu_boost(false);
2627 if ( ret
== PLUGIN_OK
) {
2628 if (configfile_save(CONFIG_FILE
, config
, CONFIG_NUM_ITEMS
,
2631 rb
->splash(HZ
, "Error writing config.");