diff options
Diffstat (limited to 'apps/plugins/pictureflow')
-rw-r--r-- | apps/plugins/pictureflow/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/pictureflow/pictureflow.c | 2575 | ||||
-rw-r--r-- | apps/plugins/pictureflow/pictureflow.make | 58 |
3 files changed, 2634 insertions, 0 deletions
diff --git a/apps/plugins/pictureflow/SOURCES b/apps/plugins/pictureflow/SOURCES new file mode 100644 index 0000000000..7d21cec526 --- /dev/null +++ b/apps/plugins/pictureflow/SOURCES | |||
@@ -0,0 +1 @@ | |||
pictureflow.c | |||
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c new file mode 100644 index 0000000000..82dc9748ec --- /dev/null +++ b/apps/plugins/pictureflow/pictureflow.c | |||
@@ -0,0 +1,2575 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Jonas Hurrelmann (j@outpo.st) | ||
11 | * Copyright (C) 2007 Nicolas Pennequin | ||
12 | * Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) (original Qt Version) | ||
13 | * | ||
14 | * Original code: http://code.google.com/p/pictureflow/ | ||
15 | * | ||
16 | * 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. | ||
20 | * | ||
21 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
22 | * KIND, either express or implied. | ||
23 | * | ||
24 | ****************************************************************************/ | ||
25 | |||
26 | #include "plugin.h" | ||
27 | #include <albumart.h> | ||
28 | #include "lib/read_image.h" | ||
29 | #include "lib/pluginlib_actions.h" | ||
30 | #include "lib/helper.h" | ||
31 | #include "lib/configfile.h" | ||
32 | #include "lib/picture.h" | ||
33 | #include "pluginbitmaps/pictureflow_logo.h" | ||
34 | #include "lib/grey.h" | ||
35 | #include "lib/feature_wrappers.h" | ||
36 | #include "lib/buflib.h" | ||
37 | |||
38 | PLUGIN_HEADER | ||
39 | |||
40 | /******************************* Globals ***********************************/ | ||
41 | |||
42 | #define PF_PREV ACTION_STD_PREV | ||
43 | #define PF_PREV_REPEAT ACTION_STD_PREVREPEAT | ||
44 | #define PF_NEXT ACTION_STD_NEXT | ||
45 | #define PF_NEXT_REPEAT ACTION_STD_NEXTREPEAT | ||
46 | #define PF_SELECT ACTION_STD_OK | ||
47 | #define PF_CONTEXT ACTION_STD_CONTEXT | ||
48 | #define PF_BACK ACTION_STD_CANCEL | ||
49 | #define PF_MENU ACTION_STD_MENU | ||
50 | #define PF_QUIT (LAST_ACTION_PLACEHOLDER + 1) | ||
51 | |||
52 | const struct button_mapping pf_context_album_scroll[] = | ||
53 | { | ||
54 | #ifdef HAVE_TOUCHSCREEN | ||
55 | {PF_PREV, BUTTON_MIDLEFT, BUTTON_NONE}, | ||
56 | {PF_PREV_REPEAT, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE}, | ||
57 | {PF_NEXT, BUTTON_MIDRIGHT, BUTTON_NONE}, | ||
58 | {PF_NEXT_REPEAT, BUTTON_MIDRIGHT|BUTTON_REPEAT, BUTTON_NONE}, | ||
59 | #endif | ||
60 | #if CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD || \ | ||
61 | CONFIG_KEYPAD == IAUDIO_X5M5_PAD || CONFIG_KEYPAD == GIGABEAT_PAD || \ | ||
62 | CONFIG_KEYPAD == GIGABEAT_S_PAD || CONFIG_KEYPAD == RECORDER_PAD || \ | ||
63 | CONFIG_KEYPAD == ARCHOS_AV300_PAD || CONFIG_KEYPAD == SANSA_C100_PAD || \ | ||
64 | CONFIG_KEYPAD == SANSA_C200_PAD || CONFIG_KEYPAD == SANSA_CLIP_PAD || \ | ||
65 | CONFIG_KEYPAD == SANSA_M200_PAD || CONFIG_KEYPAD == IRIVER_IFP7XX_PAD || \ | ||
66 | CONFIG_KEYPAD == MROBE100_PAD || CONFIG_KEYPAD == PHILIPS_SA9200_PAD || \ | ||
67 | CONFIG_KEYPAD == IAUDIO67_PAD || CONFIG_KEYPAD == CREATIVEZVM_PAD || \ | ||
68 | CONFIG_KEYPAD == PHILIPS_HDD1630_PAD || CONFIG_KEYPAD == CREATIVEZV_PAD \ | ||
69 | || CONFIG_KEYPAD == SANSA_CLIP_PAD || CONFIG_KEYPAD == LOGIK_DAX_PAD || \ | ||
70 | CONFIG_KEYPAD == MEIZU_M6SL_PAD | ||
71 | {PF_PREV, BUTTON_LEFT, BUTTON_NONE}, | ||
72 | {PF_PREV_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, | ||
73 | {PF_NEXT, BUTTON_RIGHT, BUTTON_NONE}, | ||
74 | {PF_NEXT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, | ||
75 | #elif CONFIG_KEYPAD == ONDIO_PAD | ||
76 | {PF_PREV, BUTTON_LEFT, BUTTON_NONE}, | ||
77 | {PF_PREV_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, | ||
78 | {PF_NEXT, BUTTON_RIGHT, BUTTON_NONE}, | ||
79 | {PF_NEXT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, | ||
80 | {PF_SELECT, BUTTON_UP|BUTTON_REL, BUTTON_UP}, | ||
81 | {PF_CONTEXT, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP}, | ||
82 | {ACTION_NONE, BUTTON_UP, BUTTON_NONE}, | ||
83 | {ACTION_NONE, BUTTON_DOWN, BUTTON_NONE}, | ||
84 | {ACTION_NONE, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, | ||
85 | {ACTION_NONE, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT}, | ||
86 | #elif CONFIG_KEYPAD == IAUDIO_M3_PAD || CONFIG_KEYPAD == MROBE500_PAD | ||
87 | {PF_PREV, BUTTON_RC_REW, BUTTON_NONE}, | ||
88 | {PF_PREV_REPEAT, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE}, | ||
89 | {PF_NEXT, BUTTON_RC_FF, BUTTON_NONE}, | ||
90 | {PF_NEXT_REPEAT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE}, | ||
91 | #endif | ||
92 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|1) | ||
93 | }; | ||
94 | |||
95 | const struct button_mapping pf_context_buttons[] = | ||
96 | { | ||
97 | #ifdef HAVE_TOUCHSCREEN | ||
98 | {PF_SELECT, BUTTON_CENTER, BUTTON_NONE}, | ||
99 | {PF_MENU, BUTTON_TOPLEFT, BUTTON_NONE}, | ||
100 | {PF_BACK, BUTTON_BOTTOMRIGHT, BUTTON_NONE}, | ||
101 | #endif | ||
102 | #if CONFIG_KEYPAD == ARCHOS_AV300_PAD | ||
103 | {PF_QUIT, BUTTON_OFF, BUTTON_NONE}, | ||
104 | #elif CONFIG_KEYPAD == SANSA_C100_PAD | ||
105 | {PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, | ||
106 | #elif CONFIG_KEYPAD == CREATIVEZV_PAD || CONFIG_KEYPAD == CREATIVEZVM_PAD || \ | ||
107 | CONFIG_KEYPAD == PHILIPS_HDD1630_PAD || CONFIG_KEYPAD == IAUDIO67_PAD || \ | ||
108 | CONFIG_KEYPAD == GIGABEAT_PAD || CONFIG_KEYPAD == GIGABEAT_S_PAD || \ | ||
109 | CONFIG_KEYPAD == MROBE100_PAD || CONFIG_KEYPAD == MROBE500_PAD || \ | ||
110 | CONFIG_KEYPAD == PHILIPS_SA9200_PAD || CONFIG_KEYPAD == SANSA_CLIP_PAD || \ | ||
111 | CONFIG_KEYPAD == SANSA_FUZE_PAD | ||
112 | {PF_QUIT, BUTTON_POWER, BUTTON_NONE}, | ||
113 | /* These all use short press of BUTTON_POWER for menu, map long POWER to quit | ||
114 | */ | ||
115 | #elif CONFIG_KEYPAD == SANSA_C200_PAD || CONFIG_KEYPAD == SANSA_M200_PAD || \ | ||
116 | CONFIG_KEYPAD == IRIVER_H10_PAD || CONFIG_KEYPAD == COWOND2_PAD | ||
117 | {PF_QUIT, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER}, | ||
118 | #if CONFIG_KEYPAD == COWOND2_PAD | ||
119 | {PF_BACK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER}, | ||
120 | {ACTION_NONE, BUTTON_POWER, BUTTON_NONE}, | ||
121 | #endif | ||
122 | #elif CONFIG_KEYPAD == SANSA_E200_PAD | ||
123 | {PF_QUIT, BUTTON_POWER, BUTTON_NONE}, | ||
124 | #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD | ||
125 | {PF_QUIT, BUTTON_EQ, BUTTON_NONE}, | ||
126 | #elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \ | ||
127 | || (CONFIG_KEYPAD == IPOD_3G_PAD) \ | ||
128 | || (CONFIG_KEYPAD == IPOD_4G_PAD) | ||
129 | {PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, | ||
130 | #elif CONFIG_KEYPAD == LOGIK_DAX_PAD | ||
131 | {PF_QUIT, BUTTON_POWERPLAY|BUTTON_REPEAT, BUTTON_POWERPLAY}, | ||
132 | #elif CONFIG_KEYPAD == IAUDIO_M3_PAD | ||
133 | {PF_QUIT, BUTTON_RC_REC, BUTTON_NONE}, | ||
134 | #elif CONFIG_KEYPAD == MEIZU_M6SL_PAD | ||
135 | {PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, | ||
136 | #elif CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD || \ | ||
137 | CONFIG_KEYPAD == RECORDER_PAD || CONFIG_KEYPAD == ONDIO_PAD | ||
138 | {PF_QUIT, BUTTON_OFF, BUTTON_NONE}, | ||
139 | #endif | ||
140 | #if CONFIG_KEYPAD == IAUDIO_M3_PAD | ||
141 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD|CONTEXT_REMOTE) | ||
142 | #else | ||
143 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) | ||
144 | #endif | ||
145 | }; | ||
146 | const struct button_mapping *pf_contexts[] = | ||
147 | { | ||
148 | pf_context_album_scroll, | ||
149 | pf_context_buttons | ||
150 | }; | ||
151 | |||
152 | #if LCD_DEPTH < 8 | ||
153 | #if LCD_DEPTH > 1 | ||
154 | #define N_BRIGHT(y) LCD_BRIGHTNESS(y) | ||
155 | #else /* LCD_DEPTH <= 1 */ | ||
156 | #define N_BRIGHT(y) ((y > 127) ? 0 : 1) | ||
157 | #ifdef HAVE_NEGATIVE_LCD /* m:robe 100, Clip */ | ||
158 | #define PICTUREFLOW_DRMODE DRMODE_SOLID | ||
159 | #else | ||
160 | #define PICTUREFLOW_DRMODE (DRMODE_SOLID|DRMODE_INVERSEVID) | ||
161 | #endif | ||
162 | #endif /* LCD_DEPTH <= 1 */ | ||
163 | #define USEGSLIB | ||
164 | GREY_INFO_STRUCT | ||
165 | #define LCD_BUF _grey_info.buffer | ||
166 | #define MYLCD(fn) grey_ ## fn | ||
167 | #define G_PIX(r,g,b) \ | ||
168 | (77 * (unsigned)(r) + 150 * (unsigned)(g) + 29 * (unsigned)(b)) / 256 | ||
169 | #define N_PIX(r,g,b) N_BRIGHT(G_PIX(r,g,b)) | ||
170 | #define G_BRIGHT(y) (y) | ||
171 | #define BUFFER_WIDTH _grey_info.width | ||
172 | #define BUFFER_HEIGHT _grey_info.height | ||
173 | typedef unsigned char pix_t; | ||
174 | #else /* LCD_DEPTH >= 8 */ | ||
175 | #define LCD_BUF rb->lcd_framebuffer | ||
176 | #define MYLCD(fn) rb->lcd_ ## fn | ||
177 | #define G_PIX LCD_RGBPACK | ||
178 | #define N_PIX LCD_RGBPACK | ||
179 | #define G_BRIGHT(y) LCD_RGBPACK(y,y,y) | ||
180 | #define N_BRIGHT(y) LCD_RGBPACK(y,y,y) | ||
181 | #define BUFFER_WIDTH LCD_WIDTH | ||
182 | #define BUFFER_HEIGHT LCD_HEIGHT | ||
183 | typedef fb_data pix_t; | ||
184 | #endif /* LCD_DEPTH >= 8 */ | ||
185 | |||
186 | /* for fixed-point arithmetic, we need minimum 32-bit long | ||
187 | long long (64-bit) might be useful for multiplication and division */ | ||
188 | #define PFreal long | ||
189 | #define PFREAL_SHIFT 10 | ||
190 | #define PFREAL_FACTOR (1 << PFREAL_SHIFT) | ||
191 | #define PFREAL_ONE (1 << PFREAL_SHIFT) | ||
192 | #define PFREAL_HALF (PFREAL_ONE >> 1) | ||
193 | |||
194 | |||
195 | #define IANGLE_MAX 1024 | ||
196 | #define IANGLE_MASK 1023 | ||
197 | |||
198 | #define REFLECT_TOP (LCD_HEIGHT * 2 / 3) | ||
199 | #define REFLECT_HEIGHT (LCD_HEIGHT - REFLECT_TOP) | ||
200 | #define DISPLAY_HEIGHT REFLECT_TOP | ||
201 | #define DISPLAY_WIDTH MAX((LCD_HEIGHT * LCD_PIXEL_ASPECT_HEIGHT / \ | ||
202 | LCD_PIXEL_ASPECT_WIDTH / 2), (LCD_WIDTH * 2 / 5)) | ||
203 | #define REFLECT_SC ((0x10000U * 3 + (REFLECT_HEIGHT * 5 - 1)) / \ | ||
204 | (REFLECT_HEIGHT * 5)) | ||
205 | #define DISPLAY_OFFS ((LCD_HEIGHT / 2) - REFLECT_HEIGHT) | ||
206 | #define CAM_DIST MAX(MIN(LCD_HEIGHT,LCD_WIDTH),120) | ||
207 | #define CAM_DIST_R (CAM_DIST << PFREAL_SHIFT) | ||
208 | #define DISPLAY_LEFT_R (PFREAL_HALF - LCD_WIDTH * PFREAL_HALF) | ||
209 | #define MAXSLIDE_LEFT_R (PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF) | ||
210 | |||
211 | #define SLIDE_CACHE_SIZE 64 /* probably more than can be loaded */ | ||
212 | |||
213 | #define MAX_SLIDES_COUNT 10 | ||
214 | |||
215 | #define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200 | ||
216 | #define CACHE_PREFIX PLUGIN_DEMOS_DIR "/pictureflow" | ||
217 | |||
218 | #define EV_EXIT 9999 | ||
219 | #define EV_WAKEUP 1337 | ||
220 | |||
221 | /* maximum number of albums */ | ||
222 | |||
223 | #define MAX_TRACKS 50 | ||
224 | #define AVG_TRACK_NAME_LENGTH 20 | ||
225 | |||
226 | |||
227 | #define UNIQBUF_SIZE (64*1024) | ||
228 | |||
229 | #define EMPTY_SLIDE CACHE_PREFIX "/emptyslide.pfraw" | ||
230 | #define EMPTY_SLIDE_BMP PLUGIN_DEMOS_DIR "/pictureflow_emptyslide.bmp" | ||
231 | |||
232 | /* Error return values */ | ||
233 | #define ERROR_NO_ALBUMS -1 | ||
234 | #define ERROR_BUFFER_FULL -2 | ||
235 | |||
236 | /* current version for cover cache */ | ||
237 | #define CACHE_VERSION 2 | ||
238 | #define CONFIG_VERSION 1 | ||
239 | #define CONFIG_FILE "pictureflow.cfg" | ||
240 | |||
241 | /** structs we use */ | ||
242 | |||
243 | struct slide_data { | ||
244 | int slide_index; | ||
245 | int angle; | ||
246 | PFreal cx; | ||
247 | PFreal cy; | ||
248 | PFreal distance; | ||
249 | }; | ||
250 | |||
251 | struct slide_cache { | ||
252 | int index; /* index of the cached slide */ | ||
253 | int hid; /* handle ID of the cached slide */ | ||
254 | short next; /* "next" slide, with LRU last */ | ||
255 | short prev; /* "previous" slide */ | ||
256 | }; | ||
257 | |||
258 | struct album_data { | ||
259 | int name_idx; | ||
260 | long seek; | ||
261 | }; | ||
262 | |||
263 | struct track_data { | ||
264 | int name_idx; | ||
265 | long seek; | ||
266 | }; | ||
267 | |||
268 | struct rect { | ||
269 | int left; | ||
270 | int right; | ||
271 | int top; | ||
272 | int bottom; | ||
273 | }; | ||
274 | |||
275 | struct load_slide_event_data { | ||
276 | int slide_index; | ||
277 | int cache_index; | ||
278 | }; | ||
279 | |||
280 | |||
281 | struct pfraw_header { | ||
282 | int32_t width; /* bmap width in pixels */ | ||
283 | int32_t height; /* bmap height in pixels */ | ||
284 | }; | ||
285 | |||
286 | const struct picture logos[]={ | ||
287 | {pictureflow_logo, BMPWIDTH_pictureflow_logo, BMPHEIGHT_pictureflow_logo}, | ||
288 | }; | ||
289 | |||
290 | enum show_album_name_values { album_name_hide = 0, album_name_bottom, | ||
291 | album_name_top }; | ||
292 | static char* show_album_name_conf[] = | ||
293 | { | ||
294 | "hide", | ||
295 | "bottom", | ||
296 | "top" | ||
297 | }; | ||
298 | |||
299 | #define MAX_SPACING 40 | ||
300 | #define MAX_MARGIN 80 | ||
301 | |||
302 | /* config values and their defaults */ | ||
303 | static int slide_spacing = DISPLAY_WIDTH / 4; | ||
304 | static int center_margin = (LCD_WIDTH - DISPLAY_WIDTH) / 12; | ||
305 | static int num_slides = 4; | ||
306 | static int zoom = 100; | ||
307 | static bool show_fps = false; | ||
308 | static bool resize = true; | ||
309 | static int cache_version = 0; | ||
310 | static int show_album_name = (LCD_HEIGHT > 100) | ||
311 | ? album_name_top : album_name_bottom; | ||
312 | |||
313 | static struct configdata config[] = | ||
314 | { | ||
315 | { TYPE_INT, 0, MAX_SPACING, { .int_p = &slide_spacing }, "slide spacing", | ||
316 | NULL }, | ||
317 | { TYPE_INT, 0, MAX_MARGIN, { .int_p = ¢er_margin }, "center margin", | ||
318 | NULL }, | ||
319 | { TYPE_INT, 0, MAX_SLIDES_COUNT, { .int_p = &num_slides }, "slides count", | ||
320 | NULL }, | ||
321 | { TYPE_INT, 0, 300, { .int_p = &zoom }, "zoom", NULL }, | ||
322 | { TYPE_BOOL, 0, 1, { .bool_p = &show_fps }, "show fps", NULL }, | ||
323 | { TYPE_BOOL, 0, 1, { .bool_p = &resize }, "resize", NULL }, | ||
324 | { TYPE_INT, 0, 100, { .int_p = &cache_version }, "cache version", NULL }, | ||
325 | { TYPE_ENUM, 0, 2, { .int_p = &show_album_name }, "show album name", | ||
326 | show_album_name_conf } | ||
327 | }; | ||
328 | |||
329 | #define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata)) | ||
330 | |||
331 | /** below we allocate the memory we want to use **/ | ||
332 | |||
333 | static pix_t *buffer; /* for now it always points to the lcd framebuffer */ | ||
334 | static uint8_t reflect_table[REFLECT_HEIGHT]; | ||
335 | static struct slide_data center_slide; | ||
336 | static struct slide_data left_slides[MAX_SLIDES_COUNT]; | ||
337 | static struct slide_data right_slides[MAX_SLIDES_COUNT]; | ||
338 | static int slide_frame; | ||
339 | static int step; | ||
340 | static int target; | ||
341 | static int fade; | ||
342 | static int center_index = 0; /* index of the slide that is in the center */ | ||
343 | static int itilt; | ||
344 | static PFreal offsetX; | ||
345 | static PFreal offsetY; | ||
346 | static int number_of_slides; | ||
347 | |||
348 | static struct slide_cache cache[SLIDE_CACHE_SIZE]; | ||
349 | static int cache_free; | ||
350 | static int cache_used = -1; | ||
351 | static int cache_left_index = -1; | ||
352 | static int cache_right_index = -1; | ||
353 | static int cache_center_index = -1; | ||
354 | |||
355 | /* use long for aligning */ | ||
356 | unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)]; | ||
357 | /* queue (as array) for scheduling load_surface */ | ||
358 | |||
359 | static int empty_slide_hid; | ||
360 | |||
361 | unsigned int thread_id; | ||
362 | struct event_queue thread_q; | ||
363 | |||
364 | static struct tagcache_search tcs; | ||
365 | |||
366 | static struct buflib_context buf_ctx; | ||
367 | |||
368 | static struct album_data *album; | ||
369 | static char *album_names; | ||
370 | static int album_count; | ||
371 | |||
372 | static char track_names[MAX_TRACKS * AVG_TRACK_NAME_LENGTH]; | ||
373 | static struct track_data tracks[MAX_TRACKS]; | ||
374 | static int track_count; | ||
375 | static int track_index; | ||
376 | static int selected_track; | ||
377 | static int selected_track_pulse; | ||
378 | void reset_track_list(void); | ||
379 | |||
380 | void * buf; | ||
381 | size_t buf_size; | ||
382 | |||
383 | static bool thread_is_running; | ||
384 | |||
385 | static int cover_animation_keyframe; | ||
386 | static int extra_fade; | ||
387 | |||
388 | static int albumtxt_x = 0; | ||
389 | static int albumtxt_dir = -1; | ||
390 | static int prev_center_index = -1; | ||
391 | |||
392 | static int start_index_track_list = 0; | ||
393 | static int track_list_visible_entries = 0; | ||
394 | static int track_list_y; | ||
395 | static int track_list_h; | ||
396 | static int track_scroll_index = 0; | ||
397 | static int track_scroll_dir = 1; | ||
398 | |||
399 | /* | ||
400 | Proposals for transitions: | ||
401 | |||
402 | pf_idle -> pf_scrolling : NEXT_ALBUM/PREV_ALBUM pressed | ||
403 | -> pf_cover_in -> pf_show_tracks : SELECT_ALBUM clicked | ||
404 | |||
405 | pf_scrolling -> pf_idle : NEXT_ALBUM/PREV_ALBUM released | ||
406 | |||
407 | pf_show_tracks -> pf_cover_out -> pf_idle : SELECT_ALBUM pressed | ||
408 | |||
409 | TODO: | ||
410 | pf_show_tracks -> pf_cover_out -> pf_idle : MENU_PRESSED pressed | ||
411 | pf_show_tracks -> play_track() -> exit() : SELECT_ALBUM pressed | ||
412 | |||
413 | pf_idle, pf_scrolling -> show_menu(): MENU_PRESSED | ||
414 | */ | ||
415 | enum pf_states { | ||
416 | pf_idle = 0, | ||
417 | pf_scrolling, | ||
418 | pf_cover_in, | ||
419 | pf_show_tracks, | ||
420 | pf_cover_out | ||
421 | }; | ||
422 | |||
423 | static int pf_state; | ||
424 | |||
425 | /** code */ | ||
426 | static inline unsigned fade_color(pix_t c, unsigned a); | ||
427 | bool save_pfraw(char* filename, struct bitmap *bm); | ||
428 | bool load_new_slide(void); | ||
429 | int load_surface(int); | ||
430 | |||
431 | static inline PFreal fmul(PFreal a, PFreal b) | ||
432 | { | ||
433 | return (a*b) >> PFREAL_SHIFT; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * This version preshifts each operand, which is useful when we know how many | ||
438 | * of the least significant bits will be empty, or are worried about overflow | ||
439 | * in a particular calculation | ||
440 | */ | ||
441 | static inline PFreal fmuln(PFreal a, PFreal b, int ps1, int ps2) | ||
442 | { | ||
443 | return ((a >> ps1) * (b >> ps2)) >> (PFREAL_SHIFT - ps1 - ps2); | ||
444 | } | ||
445 | |||
446 | /* ARMv5+ has a clz instruction equivalent to our function. | ||
447 | */ | ||
448 | #if (defined(CPU_ARM) && (ARM_ARCH > 4)) | ||
449 | static inline int clz(uint32_t v) | ||
450 | { | ||
451 | return __builtin_clz(v); | ||
452 | } | ||
453 | |||
454 | /* Otherwise, use our clz, which can be inlined */ | ||
455 | #elif defined(CPU_COLDFIRE) | ||
456 | /* This clz is based on the log2(n) implementation at | ||
457 | * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog | ||
458 | * A clz benchmark plugin showed this to be about 14% faster on coldfire | ||
459 | * than the LUT-based version. | ||
460 | */ | ||
461 | static inline int clz(uint32_t v) | ||
462 | { | ||
463 | int r = 32; | ||
464 | if (v >= 0x10000) | ||
465 | { | ||
466 | v >>= 16; | ||
467 | r -= 16; | ||
468 | } | ||
469 | if (v & 0xff00) | ||
470 | { | ||
471 | v >>= 8; | ||
472 | r -= 8; | ||
473 | } | ||
474 | if (v & 0xf0) | ||
475 | { | ||
476 | v >>= 4; | ||
477 | r -= 4; | ||
478 | } | ||
479 | if (v & 0xc) | ||
480 | { | ||
481 | v >>= 2; | ||
482 | r -= 2; | ||
483 | } | ||
484 | if (v & 2) | ||
485 | { | ||
486 | v >>= 1; | ||
487 | r -= 1; | ||
488 | } | ||
489 | r -= v; | ||
490 | return r; | ||
491 | } | ||
492 | #else | ||
493 | static const char clz_lut[16] = { 4, 3, 2, 2, 1, 1, 1, 1, | ||
494 | 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
495 | /* This clz is based on the log2(n) implementation at | ||
496 | * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup | ||
497 | * It is not any faster than the one above, but trades 16B in the lookup table | ||
498 | * for a savings of 12B per each inlined call. | ||
499 | */ | ||
500 | static inline int clz(uint32_t v) | ||
501 | { | ||
502 | int r = 28; | ||
503 | if (v >= 0x10000) | ||
504 | { | ||
505 | v >>= 16; | ||
506 | r -= 16; | ||
507 | } | ||
508 | if (v & 0xff00) | ||
509 | { | ||
510 | v >>= 8; | ||
511 | r -= 8; | ||
512 | } | ||
513 | if (v & 0xf0) | ||
514 | { | ||
515 | v >>= 4; | ||
516 | r -= 4; | ||
517 | } | ||
518 | return r + clz_lut[v]; | ||
519 | } | ||
520 | #endif | ||
521 | |||
522 | /* Return the maximum possible left shift for a signed int32, without | ||
523 | * overflow | ||
524 | */ | ||
525 | static inline int allowed_shift(int32_t val) | ||
526 | { | ||
527 | uint32_t uval = val ^ (val >> 31); | ||
528 | return clz(uval) - 1; | ||
529 | } | ||
530 | |||
531 | /* Calculate num/den, with the result shifted left by PFREAL_SHIFT, by shifting | ||
532 | * num and den before dividing. | ||
533 | */ | ||
534 | static inline PFreal fdiv(PFreal num, PFreal den) | ||
535 | { | ||
536 | int shift = allowed_shift(num); | ||
537 | shift = MIN(PFREAL_SHIFT, shift); | ||
538 | num <<= shift; | ||
539 | den >>= PFREAL_SHIFT - shift; | ||
540 | return num / den; | ||
541 | } | ||
542 | |||
543 | #define fmin(a,b) (((a) < (b)) ? (a) : (b)) | ||
544 | #define fmax(a,b) (((a) > (b)) ? (a) : (b)) | ||
545 | #define fabs(a) (a < 0 ? -a : a) | ||
546 | #define fbound(min,val,max) (fmax((min),fmin((max),(val)))) | ||
547 | |||
548 | #if CONFIG_CPU == SH7034 | ||
549 | /* 16*16->32 bit multiplication is a single instrcution on the SH1 */ | ||
550 | #define MULUQ(a, b) ((uint32_t) (((uint16_t) (a)) * ((uint16_t) (b)))) | ||
551 | #else | ||
552 | #define MULUQ(a, b) ((a) * (b)) | ||
553 | #endif | ||
554 | |||
555 | |||
556 | #if 0 | ||
557 | #define fmul(a,b) ( ((a)*(b)) >> PFREAL_SHIFT ) | ||
558 | #define fdiv(n,m) ( ((n)<< PFREAL_SHIFT ) / m ) | ||
559 | |||
560 | #define fconv(a, q1, q2) (((q2)>(q1)) ? (a)<<((q2)-(q1)) : (a)>>((q1)-(q2))) | ||
561 | #define tofloat(a, q) ( (float)(a) / (float)(1<<(q)) ) | ||
562 | |||
563 | static inline PFreal fmul(PFreal a, PFreal b) | ||
564 | { | ||
565 | return (a*b) >> PFREAL_SHIFT; | ||
566 | } | ||
567 | |||
568 | static inline PFreal fdiv(PFreal n, PFreal m) | ||
569 | { | ||
570 | return (n<<(PFREAL_SHIFT))/m; | ||
571 | } | ||
572 | #endif | ||
573 | |||
574 | /* warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! */ | ||
575 | static const short sin_tab[] = { | ||
576 | 0, 100, 200, 297, 392, 483, 569, 650, | ||
577 | 724, 792, 851, 903, 946, 980, 1004, 1019, | ||
578 | 1024, 1019, 1004, 980, 946, 903, 851, 792, | ||
579 | 724, 650, 569, 483, 392, 297, 200, 100, | ||
580 | 0, -100, -200, -297, -392, -483, -569, -650, | ||
581 | -724, -792, -851, -903, -946, -980, -1004, -1019, | ||
582 | -1024, -1019, -1004, -980, -946, -903, -851, -792, | ||
583 | -724, -650, -569, -483, -392, -297, -200, -100, | ||
584 | 0 | ||
585 | }; | ||
586 | |||
587 | static inline PFreal fsin(int iangle) | ||
588 | { | ||
589 | iangle &= IANGLE_MASK; | ||
590 | |||
591 | int i = (iangle >> 4); | ||
592 | PFreal p = sin_tab[i]; | ||
593 | PFreal q = sin_tab[(i+1)]; | ||
594 | PFreal g = (q - p); | ||
595 | return p + g * (iangle-i*16)/16; | ||
596 | } | ||
597 | |||
598 | static inline PFreal fcos(int iangle) | ||
599 | { | ||
600 | return fsin(iangle + (IANGLE_MAX >> 2)); | ||
601 | } | ||
602 | |||
603 | static inline uint32_t div255(uint32_t val) | ||
604 | { | ||
605 | return ((((val >> 8) + val) >> 8) + val) >> 8; | ||
606 | } | ||
607 | |||
608 | #define SCALE_VAL(val,out) div255((val) * (out) + 127) | ||
609 | |||
610 | static void output_row_transposed(uint32_t row, void * row_in, | ||
611 | struct scaler_context *ctx) | ||
612 | { | ||
613 | pix_t *dest = (pix_t*)ctx->bm->data + row; | ||
614 | pix_t *end = dest + ctx->bm->height * ctx->bm->width; | ||
615 | #ifdef USEGSLIB | ||
616 | uint32_t *qp = (uint32_t*)row_in; | ||
617 | for (; dest < end; dest += ctx->bm->height) | ||
618 | *dest = SC_MUL((*qp++) + ctx->round, ctx->divisor); | ||
619 | #else | ||
620 | struct uint32_rgb *qp = (struct uint32_rgb*)row_in; | ||
621 | uint32_t rb_mul = SCALE_VAL(ctx->divisor, 31), | ||
622 | rb_rnd = SCALE_VAL(ctx->round, 31), | ||
623 | g_mul = SCALE_VAL(ctx->divisor, 63), | ||
624 | g_rnd = SCALE_VAL(ctx->round, 63); | ||
625 | int r, g, b; | ||
626 | for (; dest < end; dest += ctx->bm->height) | ||
627 | { | ||
628 | r = SC_MUL(qp->r + rb_rnd, rb_mul); | ||
629 | g = SC_MUL(qp->g + g_rnd, g_mul); | ||
630 | b = SC_MUL(qp->b + rb_rnd, rb_mul); | ||
631 | qp++; | ||
632 | *dest = LCD_RGBPACK_LCD(r,g,b); | ||
633 | } | ||
634 | #endif | ||
635 | } | ||
636 | |||
637 | #ifdef HAVE_LCD_COLOR | ||
638 | static void output_row_transposed_fromyuv(uint32_t row, void * row_in, | ||
639 | struct scaler_context *ctx) | ||
640 | { | ||
641 | pix_t *dest = (pix_t*)ctx->bm->data + row; | ||
642 | pix_t *end = dest + ctx->bm->height * ctx->bm->width; | ||
643 | struct uint32_rgb *qp = (struct uint32_rgb*)row_in; | ||
644 | for (; dest < end; dest += ctx->bm->height) | ||
645 | { | ||
646 | unsigned r, g, b, y, u, v; | ||
647 | y = SC_MUL(qp->b + ctx->round, ctx->divisor); | ||
648 | u = SC_MUL(qp->g + ctx->round, ctx->divisor); | ||
649 | v = SC_MUL(qp->r + ctx->round, ctx->divisor); | ||
650 | qp++; | ||
651 | yuv_to_rgb(y, u, v, &r, &g, &b); | ||
652 | r = (31 * r + (r >> 3) + 127) >> 8; | ||
653 | g = (63 * g + (g >> 2) + 127) >> 8; | ||
654 | b = (31 * b + (b >> 3) + 127) >> 8; | ||
655 | *dest = LCD_RGBPACK_LCD(r, g, b); | ||
656 | } | ||
657 | } | ||
658 | #endif | ||
659 | |||
660 | static unsigned int get_size(struct bitmap *bm) | ||
661 | { | ||
662 | return bm->width * bm->height * sizeof(pix_t); | ||
663 | } | ||
664 | |||
665 | const struct custom_format format_transposed = { | ||
666 | #ifdef HAVE_LCD_COLOR | ||
667 | .output_row = { | ||
668 | output_row_transposed, | ||
669 | output_row_transposed_fromyuv | ||
670 | }, | ||
671 | #else | ||
672 | .output_row = output_row_transposed, | ||
673 | #endif | ||
674 | .get_size = get_size | ||
675 | }; | ||
676 | |||
677 | static const struct button_mapping* get_context_map(int context) | ||
678 | { | ||
679 | return pf_contexts[context & ~CONTEXT_CUSTOM]; | ||
680 | } | ||
681 | |||
682 | /* Create the lookup table with the scaling values for the reflections */ | ||
683 | void init_reflect_table(void) | ||
684 | { | ||
685 | int i; | ||
686 | for (i = 0; i < REFLECT_HEIGHT; i++) | ||
687 | reflect_table[i] = | ||
688 | (768 * (REFLECT_HEIGHT - i) + (5 * REFLECT_HEIGHT / 2)) / | ||
689 | (5 * REFLECT_HEIGHT); | ||
690 | } | ||
691 | |||
692 | /** | ||
693 | Create an index of all albums from the database. | ||
694 | Also store the album names so we can access them later. | ||
695 | */ | ||
696 | int create_album_index(void) | ||
697 | { | ||
698 | buf_size -= UNIQBUF_SIZE * sizeof(long); | ||
699 | long *uniqbuf = (long *)(buf_size + (char *)buf); | ||
700 | album = ((struct album_data *)uniqbuf) - 1; | ||
701 | rb->memset(&tcs, 0, sizeof(struct tagcache_search) ); | ||
702 | album_count = 0; | ||
703 | rb->tagcache_search(&tcs, tag_album); | ||
704 | rb->tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE); | ||
705 | unsigned int l, old_l = 0; | ||
706 | album_names = buf; | ||
707 | album[0].name_idx = 0; | ||
708 | while (rb->tagcache_get_next(&tcs)) | ||
709 | { | ||
710 | buf_size -= sizeof(struct album_data); | ||
711 | l = rb->strlen(tcs.result) + 1; | ||
712 | if ( album_count > 0 ) | ||
713 | album[-album_count].name_idx = album[1-album_count].name_idx + old_l; | ||
714 | |||
715 | if ( l > buf_size ) | ||
716 | /* not enough memory */ | ||
717 | return ERROR_BUFFER_FULL; | ||
718 | |||
719 | rb->strcpy(buf, tcs.result); | ||
720 | buf_size -= l; | ||
721 | buf = l + (char *)buf; | ||
722 | album[-album_count].seek = tcs.result_seek; | ||
723 | old_l = l; | ||
724 | album_count++; | ||
725 | } | ||
726 | rb->tagcache_search_finish(&tcs); | ||
727 | ALIGN_BUFFER(buf, buf_size, 4); | ||
728 | int i; | ||
729 | struct album_data* tmp_album = (struct album_data*)buf; | ||
730 | for (i = album_count - 1; i >= 0; i--) | ||
731 | tmp_album[i] = album[-i]; | ||
732 | album = tmp_album; | ||
733 | buf = album + album_count; | ||
734 | buf_size += UNIQBUF_SIZE * sizeof(long); | ||
735 | return (album_count > 0) ? 0 : ERROR_NO_ALBUMS; | ||
736 | } | ||
737 | |||
738 | /** | ||
739 | Return a pointer to the album name of the given slide_index | ||
740 | */ | ||
741 | char* get_album_name(const int slide_index) | ||
742 | { | ||
743 | return album_names + album[slide_index].name_idx; | ||
744 | } | ||
745 | |||
746 | /** | ||
747 | Return a pointer to the track name of the active album | ||
748 | create_track_index has to be called first. | ||
749 | */ | ||
750 | char* get_track_name(const int track_index) | ||
751 | { | ||
752 | if ( track_index < track_count ) | ||
753 | return track_names + tracks[track_index].name_idx; | ||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | /** | ||
758 | Create the track index of the given slide_index. | ||
759 | */ | ||
760 | int create_track_index(const int slide_index) | ||
761 | { | ||
762 | if ( slide_index == track_index ) { | ||
763 | return -1; | ||
764 | } | ||
765 | |||
766 | if (!rb->tagcache_search(&tcs, tag_title)) | ||
767 | return -1; | ||
768 | |||
769 | int ret = 0; | ||
770 | char temp_titles[MAX_TRACKS][AVG_TRACK_NAME_LENGTH*4]; | ||
771 | int temp_seeks[MAX_TRACKS]; | ||
772 | |||
773 | rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek); | ||
774 | track_count=0; | ||
775 | int string_index = 0; | ||
776 | int l, track_num, heighest_index = 0; | ||
777 | |||
778 | for(l=0;l<MAX_TRACKS;l++) | ||
779 | temp_titles[l][0] = '\0'; | ||
780 | while (rb->tagcache_get_next(&tcs) && track_count < MAX_TRACKS) | ||
781 | { | ||
782 | track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber) - 1; | ||
783 | if (track_num >= 0) | ||
784 | { | ||
785 | rb->snprintf(temp_titles[track_num],sizeof(temp_titles[track_num]), | ||
786 | "%d: %s", track_num+1, tcs.result); | ||
787 | temp_seeks[track_num] = tcs.result_seek; | ||
788 | } | ||
789 | else | ||
790 | { | ||
791 | track_num = 0; | ||
792 | while (temp_titles[track_num][0] != '\0') | ||
793 | track_num++; | ||
794 | rb->strcpy(temp_titles[track_num], tcs.result); | ||
795 | temp_seeks[track_num] = tcs.result_seek; | ||
796 | } | ||
797 | if (track_num > heighest_index) | ||
798 | heighest_index = track_num; | ||
799 | track_count++; | ||
800 | } | ||
801 | |||
802 | rb->tagcache_search_finish(&tcs); | ||
803 | track_index = slide_index; | ||
804 | |||
805 | /* now fix the track list order */ | ||
806 | l = 0; | ||
807 | track_count = 0; | ||
808 | while (l <= heighest_index && | ||
809 | string_index < MAX_TRACKS*AVG_TRACK_NAME_LENGTH) | ||
810 | { | ||
811 | if (temp_titles[l][0] != '\0') | ||
812 | { | ||
813 | rb->strcpy(track_names + string_index, temp_titles[l]); | ||
814 | tracks[track_count].name_idx = string_index; | ||
815 | tracks[track_count].seek = temp_seeks[l]; | ||
816 | string_index += rb->strlen(temp_titles[l]) + 1; | ||
817 | track_count++; | ||
818 | } | ||
819 | l++; | ||
820 | } | ||
821 | if (ret != 0) | ||
822 | return ret; | ||
823 | else | ||
824 | return (track_count > 0) ? 0 : -1; | ||
825 | } | ||
826 | |||
827 | /** | ||
828 | Determine filename of the album art for the given slide_index and | ||
829 | store the result in buf. | ||
830 | The algorithm looks for the first track of the given album uses | ||
831 | find_albumart to find the filename. | ||
832 | */ | ||
833 | bool get_albumart_for_index_from_db(const int slide_index, char *buf, | ||
834 | int buflen) | ||
835 | { | ||
836 | if ( slide_index == -1 ) | ||
837 | { | ||
838 | rb->strncpy( buf, EMPTY_SLIDE, buflen ); | ||
839 | } | ||
840 | |||
841 | if (!rb->tagcache_search(&tcs, tag_filename)) | ||
842 | return false; | ||
843 | |||
844 | bool result; | ||
845 | /* find the first track of the album */ | ||
846 | rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek); | ||
847 | |||
848 | if ( rb->tagcache_get_next(&tcs) ) { | ||
849 | struct mp3entry id3; | ||
850 | int fd; | ||
851 | |||
852 | fd = rb->open(tcs.result, O_RDONLY); | ||
853 | rb->get_metadata(&id3, fd, tcs.result); | ||
854 | rb->close(fd); | ||
855 | if ( search_albumart_files(&id3, "", buf, buflen) ) | ||
856 | result = true; | ||
857 | else | ||
858 | result = false; | ||
859 | } | ||
860 | else { | ||
861 | /* did not find a matching track */ | ||
862 | result = false; | ||
863 | } | ||
864 | rb->tagcache_search_finish(&tcs); | ||
865 | return result; | ||
866 | } | ||
867 | |||
868 | /** | ||
869 | Draw the PictureFlow logo | ||
870 | */ | ||
871 | void draw_splashscreen(void) | ||
872 | { | ||
873 | struct screen* display = rb->screens[0]; | ||
874 | const struct picture* logo = &(logos[display->screen_type]); | ||
875 | |||
876 | #if LCD_DEPTH > 1 | ||
877 | rb->lcd_set_background(N_BRIGHT(0)); | ||
878 | rb->lcd_set_foreground(N_BRIGHT(255)); | ||
879 | #else | ||
880 | rb->lcd_set_drawmode(PICTUREFLOW_DRMODE); | ||
881 | #endif | ||
882 | rb->lcd_clear_display(); | ||
883 | |||
884 | #if LCD_DEPTH == 1 /* Mono LCDs need the logo inverted */ | ||
885 | rb->lcd_set_drawmode(PICTUREFLOW_DRMODE ^ DRMODE_INVERSEVID); | ||
886 | picture_draw(display, logo, (LCD_WIDTH - logo->width) / 2, 10); | ||
887 | rb->lcd_set_drawmode(PICTUREFLOW_DRMODE); | ||
888 | #else | ||
889 | picture_draw(display, logo, (LCD_WIDTH - logo->width) / 2, 10); | ||
890 | #endif | ||
891 | |||
892 | rb->lcd_update(); | ||
893 | } | ||
894 | |||
895 | |||
896 | /** | ||
897 | Draw a simple progress bar | ||
898 | */ | ||
899 | void draw_progressbar(int step) | ||
900 | { | ||
901 | int txt_w, txt_h; | ||
902 | const int bar_height = 22; | ||
903 | const int w = LCD_WIDTH - 20; | ||
904 | const int x = 10; | ||
905 | |||
906 | rb->lcd_getstringsize("Preparing album artwork", &txt_w, &txt_h); | ||
907 | |||
908 | int y = (LCD_HEIGHT - txt_h)/2; | ||
909 | |||
910 | rb->lcd_putsxy((LCD_WIDTH - txt_w)/2, y, "Preparing album artwork"); | ||
911 | y += (txt_h + 5); | ||
912 | |||
913 | #if LCD_DEPTH > 1 | ||
914 | rb->lcd_set_foreground(N_BRIGHT(100)); | ||
915 | #endif | ||
916 | rb->lcd_drawrect(x, y, w+2, bar_height); | ||
917 | #if LCD_DEPTH > 1 | ||
918 | rb->lcd_set_foreground(N_PIX(165, 231, 82)); | ||
919 | #endif | ||
920 | |||
921 | rb->lcd_fillrect(x+1, y+1, step * w / album_count, bar_height-2); | ||
922 | #if LCD_DEPTH > 1 | ||
923 | rb->lcd_set_foreground(N_BRIGHT(255)); | ||
924 | #endif | ||
925 | rb->lcd_update(); | ||
926 | rb->yield(); | ||
927 | } | ||
928 | |||
929 | /** | ||
930 | Precomupte the album art images and store them in CACHE_PREFIX. | ||
931 | */ | ||
932 | bool create_albumart_cache(void) | ||
933 | { | ||
934 | int ret; | ||
935 | |||
936 | int i, slides = 0; | ||
937 | struct bitmap input_bmp; | ||
938 | |||
939 | char pfraw_file[MAX_PATH]; | ||
940 | char albumart_file[MAX_PATH]; | ||
941 | unsigned int format = FORMAT_NATIVE; | ||
942 | cache_version = 0; | ||
943 | configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); | ||
944 | if (resize) | ||
945 | format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT; | ||
946 | for (i=0; i < album_count; i++) | ||
947 | { | ||
948 | rb->snprintf(pfraw_file, sizeof(pfraw_file), CACHE_PREFIX "/%d.pfraw", | ||
949 | i); | ||
950 | /* delete existing cache, so it's a true rebuild */ | ||
951 | if(rb->file_exists(pfraw_file)) | ||
952 | rb->remove(pfraw_file); | ||
953 | draw_progressbar(i); | ||
954 | if (!get_albumart_for_index_from_db(i, albumart_file, MAX_PATH)) | ||
955 | continue; | ||
956 | |||
957 | input_bmp.data = buf; | ||
958 | input_bmp.width = DISPLAY_WIDTH; | ||
959 | input_bmp.height = DISPLAY_HEIGHT; | ||
960 | ret = read_image_file(albumart_file, &input_bmp, | ||
961 | buf_size, format, &format_transposed); | ||
962 | if (ret <= 0) { | ||
963 | rb->splash(HZ, "Could not read bmp"); | ||
964 | continue; /* skip missing/broken files */ | ||
965 | } | ||
966 | if (!save_pfraw(pfraw_file, &input_bmp)) | ||
967 | { | ||
968 | rb->splash(HZ, "Could not write bmp"); | ||
969 | } | ||
970 | slides++; | ||
971 | if ( rb->button_get(false) == PF_MENU ) return false; | ||
972 | } | ||
973 | if ( slides == 0 ) { | ||
974 | /* Warn the user that we couldn't find any albumart */ | ||
975 | rb->splash(2*HZ, "No album art found"); | ||
976 | return false; | ||
977 | } | ||
978 | return true; | ||
979 | } | ||
980 | |||
981 | /** | ||
982 | Thread used for loading and preparing bitmaps in the background | ||
983 | */ | ||
984 | void thread(void) | ||
985 | { | ||
986 | long sleep_time = 5 * HZ; | ||
987 | struct queue_event ev; | ||
988 | while (1) { | ||
989 | rb->queue_wait_w_tmo(&thread_q, &ev, sleep_time); | ||
990 | switch (ev.id) { | ||
991 | case EV_EXIT: | ||
992 | return; | ||
993 | case EV_WAKEUP: | ||
994 | /* we just woke up */ | ||
995 | break; | ||
996 | } | ||
997 | while ( load_new_slide() ) { | ||
998 | rb->yield(); | ||
999 | switch (ev.id) { | ||
1000 | case EV_EXIT: | ||
1001 | return; | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | |||
1008 | /** | ||
1009 | End the thread by posting the EV_EXIT event | ||
1010 | */ | ||
1011 | void end_pf_thread(void) | ||
1012 | { | ||
1013 | if ( thread_is_running ) { | ||
1014 | rb->queue_post(&thread_q, EV_EXIT, 0); | ||
1015 | rb->thread_wait(thread_id); | ||
1016 | /* remove the thread's queue from the broadcast list */ | ||
1017 | rb->queue_delete(&thread_q); | ||
1018 | thread_is_running = false; | ||
1019 | } | ||
1020 | |||
1021 | } | ||
1022 | |||
1023 | |||
1024 | /** | ||
1025 | Create the thread an setup the event queue | ||
1026 | */ | ||
1027 | bool create_pf_thread(void) | ||
1028 | { | ||
1029 | /* put the thread's queue in the bcast list */ | ||
1030 | rb->queue_init(&thread_q, true); | ||
1031 | if ((thread_id = rb->create_thread( | ||
1032 | thread, | ||
1033 | thread_stack, | ||
1034 | sizeof(thread_stack), | ||
1035 | 0, | ||
1036 | "Picture load thread" | ||
1037 | IF_PRIO(, MAX(PRIORITY_USER_INTERFACE / 2, | ||
1038 | PRIORITY_REALTIME + 1)) | ||
1039 | IF_COP(, CPU) | ||
1040 | ) | ||
1041 | ) == 0) { | ||
1042 | return false; | ||
1043 | } | ||
1044 | thread_is_running = true; | ||
1045 | rb->queue_post(&thread_q, EV_WAKEUP, 0); | ||
1046 | return true; | ||
1047 | } | ||
1048 | |||
1049 | /** | ||
1050 | Safe the given bitmap as filename in the pfraw format | ||
1051 | */ | ||
1052 | bool save_pfraw(char* filename, struct bitmap *bm) | ||
1053 | { | ||
1054 | struct pfraw_header bmph; | ||
1055 | bmph.width = bm->width; | ||
1056 | bmph.height = bm->height; | ||
1057 | int fh = rb->creat( filename ); | ||
1058 | if( fh < 0 ) return false; | ||
1059 | rb->write( fh, &bmph, sizeof( struct pfraw_header ) ); | ||
1060 | int y; | ||
1061 | for( y = 0; y < bm->height; y++ ) | ||
1062 | { | ||
1063 | pix_t *d = (pix_t*)( bm->data ) + (y*bm->width); | ||
1064 | rb->write( fh, d, sizeof( pix_t ) * bm->width ); | ||
1065 | } | ||
1066 | rb->close( fh ); | ||
1067 | return true; | ||
1068 | } | ||
1069 | |||
1070 | |||
1071 | /* | ||
1072 | * The following functions implement the linked-list-in-array used to manage | ||
1073 | * the LRU cache of slides, and the list of free cache slots. | ||
1074 | */ | ||
1075 | |||
1076 | #define seek_right_while(start, cond) \ | ||
1077 | ({ \ | ||
1078 | int ind_, next_ = (start); \ | ||
1079 | do { \ | ||
1080 | ind_ = next_; \ | ||
1081 | next_ = cache[ind_].next; \ | ||
1082 | } while (next_ != cache_used && (cond)); \ | ||
1083 | ind_; \ | ||
1084 | }) | ||
1085 | |||
1086 | #define seek_left_while(start, cond) \ | ||
1087 | ({ \ | ||
1088 | int ind_, next_ = (start); \ | ||
1089 | do { \ | ||
1090 | ind_ = next_; \ | ||
1091 | next_ = cache[ind_].prev; \ | ||
1092 | } while (ind_ != cache_used && (cond)); \ | ||
1093 | ind_; \ | ||
1094 | }) | ||
1095 | |||
1096 | /** | ||
1097 | Pop the given item from the linked list starting at *head, returning the next | ||
1098 | item, or -1 if the list is now empty. | ||
1099 | */ | ||
1100 | static inline int lla_pop_item (int *head, int i) | ||
1101 | { | ||
1102 | int prev = cache[i].prev; | ||
1103 | int next = cache[i].next; | ||
1104 | if (i == next) | ||
1105 | { | ||
1106 | *head = -1; | ||
1107 | return -1; | ||
1108 | } | ||
1109 | else if (i == *head) | ||
1110 | *head = next; | ||
1111 | cache[next].prev = prev; | ||
1112 | cache[prev].next = next; | ||
1113 | return next; | ||
1114 | } | ||
1115 | |||
1116 | |||
1117 | /** | ||
1118 | Pop the head item from the list starting at *head, returning the index of the | ||
1119 | item, or -1 if the list is already empty. | ||
1120 | */ | ||
1121 | static inline int lla_pop_head (int *head) | ||
1122 | { | ||
1123 | int i = *head; | ||
1124 | if (i != -1) | ||
1125 | lla_pop_item(head, i); | ||
1126 | return i; | ||
1127 | } | ||
1128 | |||
1129 | /** | ||
1130 | Insert the item at index i before the one at index p. | ||
1131 | */ | ||
1132 | static inline void lla_insert (int i, int p) | ||
1133 | { | ||
1134 | int next = p; | ||
1135 | int prev = cache[next].prev; | ||
1136 | cache[next].prev = i; | ||
1137 | cache[prev].next = i; | ||
1138 | cache[i].next = next; | ||
1139 | cache[i].prev = prev; | ||
1140 | } | ||
1141 | |||
1142 | |||
1143 | /** | ||
1144 | Insert the item at index i at the end of the list starting at *head. | ||
1145 | */ | ||
1146 | static inline void lla_insert_tail (int *head, int i) | ||
1147 | { | ||
1148 | if (*head == -1) | ||
1149 | { | ||
1150 | *head = i; | ||
1151 | cache[i].next = i; | ||
1152 | cache[i].prev = i; | ||
1153 | } else | ||
1154 | lla_insert(i, *head); | ||
1155 | } | ||
1156 | |||
1157 | /** | ||
1158 | Insert the item at index i before the one at index p. | ||
1159 | */ | ||
1160 | static inline void lla_insert_after(int i, int p) | ||
1161 | { | ||
1162 | p = cache[p].next; | ||
1163 | lla_insert(i, p); | ||
1164 | } | ||
1165 | |||
1166 | |||
1167 | /** | ||
1168 | Insert the item at index i before the one at index p in the list starting at | ||
1169 | *head | ||
1170 | */ | ||
1171 | static inline void lla_insert_before(int *head, int i, int p) | ||
1172 | { | ||
1173 | lla_insert(i, p); | ||
1174 | if (*head == p) | ||
1175 | *head = i; | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | /** | ||
1180 | Free the used slide at index i, and its buffer, and move it to the free | ||
1181 | slides list. | ||
1182 | */ | ||
1183 | static inline void free_slide(int i) | ||
1184 | { | ||
1185 | if (cache[i].hid != empty_slide_hid) | ||
1186 | buflib_free(&buf_ctx, cache[i].hid); | ||
1187 | cache[i].index = -1; | ||
1188 | lla_pop_item(&cache_used, i); | ||
1189 | lla_insert_tail(&cache_free, i); | ||
1190 | if (cache_used == -1) | ||
1191 | { | ||
1192 | cache_right_index = -1; | ||
1193 | cache_left_index = -1; | ||
1194 | cache_center_index = -1; | ||
1195 | } | ||
1196 | } | ||
1197 | |||
1198 | |||
1199 | /** | ||
1200 | Free one slide ranked above the given priority. If no such slide can be found, | ||
1201 | return false. | ||
1202 | */ | ||
1203 | static inline int free_slide_prio(int prio) | ||
1204 | { | ||
1205 | if (cache_used == -1) | ||
1206 | return false; | ||
1207 | int i, l = cache_used, r = cache[cache_used].prev, prio_max; | ||
1208 | int prio_l = cache[l].index < center_index ? | ||
1209 | center_index - cache[l].index : 0; | ||
1210 | int prio_r = cache[r].index > center_index ? | ||
1211 | cache[r].index - center_index : 0; | ||
1212 | if (prio_l > prio_r) | ||
1213 | { | ||
1214 | i = l; | ||
1215 | prio_max = prio_l; | ||
1216 | } else { | ||
1217 | i = r; | ||
1218 | prio_max = prio_r; | ||
1219 | } | ||
1220 | if (prio_max > prio) | ||
1221 | { | ||
1222 | if (i == cache_left_index) | ||
1223 | cache_left_index = cache[i].next; | ||
1224 | if (i == cache_right_index) | ||
1225 | cache_right_index = cache[i].prev; | ||
1226 | free_slide(i); | ||
1227 | return true; | ||
1228 | } else | ||
1229 | return false; | ||
1230 | } | ||
1231 | |||
1232 | /** | ||
1233 | Read the pfraw image given as filename and return the hid of the buffer | ||
1234 | */ | ||
1235 | int read_pfraw(char* filename, int prio) | ||
1236 | { | ||
1237 | struct pfraw_header bmph; | ||
1238 | int fh = rb->open(filename, O_RDONLY); | ||
1239 | if( fh < 0 ) | ||
1240 | return empty_slide_hid; | ||
1241 | else | ||
1242 | rb->read(fh, &bmph, sizeof(struct pfraw_header)); | ||
1243 | |||
1244 | int size = sizeof(struct bitmap) + sizeof( pix_t ) * | ||
1245 | bmph.width * bmph.height; | ||
1246 | |||
1247 | int hid; | ||
1248 | while (!(hid = buflib_alloc(&buf_ctx, size)) && free_slide_prio(prio)); | ||
1249 | |||
1250 | if (!hid) { | ||
1251 | rb->close( fh ); | ||
1252 | return 0; | ||
1253 | } | ||
1254 | |||
1255 | struct dim *bm = buflib_get_data(&buf_ctx, hid); | ||
1256 | |||
1257 | bm->width = bmph.width; | ||
1258 | bm->height = bmph.height; | ||
1259 | pix_t *data = (pix_t*)(sizeof(struct dim) + (char *)bm); | ||
1260 | |||
1261 | int y; | ||
1262 | for( y = 0; y < bm->height; y++ ) | ||
1263 | { | ||
1264 | rb->read( fh, data , sizeof( pix_t ) * bm->width ); | ||
1265 | data += bm->width; | ||
1266 | } | ||
1267 | rb->close( fh ); | ||
1268 | return hid; | ||
1269 | } | ||
1270 | |||
1271 | |||
1272 | /** | ||
1273 | Load the surface for the given slide_index into the cache at cache_index. | ||
1274 | */ | ||
1275 | static inline bool load_and_prepare_surface(const int slide_index, | ||
1276 | const int cache_index, | ||
1277 | const int prio) | ||
1278 | { | ||
1279 | char tmp_path_name[MAX_PATH+1]; | ||
1280 | rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", | ||
1281 | slide_index); | ||
1282 | |||
1283 | int hid = read_pfraw(tmp_path_name, prio); | ||
1284 | if (!hid) | ||
1285 | return false; | ||
1286 | |||
1287 | cache[cache_index].hid = hid; | ||
1288 | |||
1289 | if ( cache_index < SLIDE_CACHE_SIZE ) { | ||
1290 | cache[cache_index].index = slide_index; | ||
1291 | } | ||
1292 | |||
1293 | return true; | ||
1294 | } | ||
1295 | |||
1296 | |||
1297 | /** | ||
1298 | Load the "next" slide that we can load, freeing old slides if needed, provided | ||
1299 | that they are further from center_index than the current slide | ||
1300 | */ | ||
1301 | bool load_new_slide(void) | ||
1302 | { | ||
1303 | int i = -1; | ||
1304 | if (cache_center_index != -1) | ||
1305 | { | ||
1306 | int next, prev; | ||
1307 | if (cache[cache_center_index].index != center_index) | ||
1308 | { | ||
1309 | if (cache[cache_center_index].index < center_index) | ||
1310 | { | ||
1311 | cache_center_index = seek_right_while(cache_center_index, | ||
1312 | cache[next_].index <= center_index); | ||
1313 | prev = cache_center_index; | ||
1314 | next = cache[cache_center_index].next; | ||
1315 | } | ||
1316 | else | ||
1317 | { | ||
1318 | cache_center_index = seek_left_while(cache_center_index, | ||
1319 | cache[next_].index >= center_index); | ||
1320 | next = cache_center_index; | ||
1321 | prev = cache[cache_center_index].prev; | ||
1322 | } | ||
1323 | if (cache[cache_center_index].index != center_index) | ||
1324 | { | ||
1325 | if (cache_free == -1) | ||
1326 | free_slide_prio(0); | ||
1327 | i = lla_pop_head(&cache_free); | ||
1328 | if (!load_and_prepare_surface(center_index, i, 0)) | ||
1329 | goto fail_and_refree; | ||
1330 | if (cache[next].index == -1) | ||
1331 | { | ||
1332 | if (cache[prev].index == -1) | ||
1333 | goto insert_first_slide; | ||
1334 | else | ||
1335 | next = cache[prev].next; | ||
1336 | } | ||
1337 | lla_insert(i, next); | ||
1338 | if (cache[i].index < cache[cache_used].index) | ||
1339 | cache_used = i; | ||
1340 | cache_center_index = i; | ||
1341 | cache_left_index = i; | ||
1342 | cache_right_index = i; | ||
1343 | return true; | ||
1344 | } | ||
1345 | } | ||
1346 | if (cache[cache_left_index].index > | ||
1347 | cache[cache_center_index].index) | ||
1348 | cache_left_index = cache_center_index; | ||
1349 | if (cache[cache_right_index].index < | ||
1350 | cache[cache_center_index].index) | ||
1351 | cache_right_index = cache_center_index; | ||
1352 | cache_left_index = seek_left_while(cache_left_index, | ||
1353 | cache[ind_].index - 1 == cache[next_].index); | ||
1354 | cache_right_index = seek_right_while(cache_right_index, | ||
1355 | cache[ind_].index - 1 == cache[next_].index); | ||
1356 | int prio_l = cache[cache_center_index].index - | ||
1357 | cache[cache_left_index].index + 1; | ||
1358 | int prio_r = cache[cache_right_index].index - | ||
1359 | cache[cache_center_index].index + 1; | ||
1360 | if ((prio_l < prio_r || | ||
1361 | cache[cache_right_index].index >= number_of_slides) && | ||
1362 | cache[cache_left_index].index > 0) | ||
1363 | { | ||
1364 | if (cache_free == -1 && !free_slide_prio(prio_l)) | ||
1365 | return false; | ||
1366 | i = lla_pop_head(&cache_free); | ||
1367 | if (load_and_prepare_surface(cache[cache_left_index].index | ||
1368 | - 1, i, prio_l)) | ||
1369 | { | ||
1370 | lla_insert_before(&cache_used, i, cache_left_index); | ||
1371 | cache_left_index = i; | ||
1372 | return true; | ||
1373 | } | ||
1374 | } else if(cache[cache_right_index].index < number_of_slides - 1) | ||
1375 | { | ||
1376 | if (cache_free == -1 && !free_slide_prio(prio_r)) | ||
1377 | return false; | ||
1378 | i = lla_pop_head(&cache_free); | ||
1379 | if (load_and_prepare_surface(cache[cache_right_index].index | ||
1380 | + 1, i, prio_r)) | ||
1381 | { | ||
1382 | lla_insert_after(i, cache_right_index); | ||
1383 | cache_right_index = i; | ||
1384 | return true; | ||
1385 | } | ||
1386 | } | ||
1387 | } else { | ||
1388 | i = lla_pop_head(&cache_free); | ||
1389 | if (load_and_prepare_surface(center_index, i, 0)) | ||
1390 | { | ||
1391 | insert_first_slide: | ||
1392 | cache[i].next = i; | ||
1393 | cache[i].prev = i; | ||
1394 | cache_center_index = i; | ||
1395 | cache_left_index = i; | ||
1396 | cache_right_index = i; | ||
1397 | cache_used = i; | ||
1398 | return true; | ||
1399 | } | ||
1400 | } | ||
1401 | fail_and_refree: | ||
1402 | if (i != -1) | ||
1403 | { | ||
1404 | lla_insert_tail(&cache_free, i); | ||
1405 | } | ||
1406 | return false; | ||
1407 | } | ||
1408 | |||
1409 | |||
1410 | /** | ||
1411 | Get a slide from the buffer | ||
1412 | */ | ||
1413 | static inline struct dim *get_slide(const int hid) | ||
1414 | { | ||
1415 | if (!hid) | ||
1416 | return NULL; | ||
1417 | |||
1418 | struct dim *bmp; | ||
1419 | |||
1420 | bmp = buflib_get_data(&buf_ctx, hid); | ||
1421 | |||
1422 | return bmp; | ||
1423 | } | ||
1424 | |||
1425 | |||
1426 | /** | ||
1427 | Return the requested surface | ||
1428 | */ | ||
1429 | static inline struct dim *surface(const int slide_index) | ||
1430 | { | ||
1431 | if (slide_index < 0) | ||
1432 | return 0; | ||
1433 | if (slide_index >= number_of_slides) | ||
1434 | return 0; | ||
1435 | int i; | ||
1436 | if ((i = cache_used ) != -1) | ||
1437 | { | ||
1438 | do { | ||
1439 | if (cache[i].index == slide_index) | ||
1440 | return get_slide(cache[i].hid); | ||
1441 | i = cache[i].next; | ||
1442 | } while (i != cache_used); | ||
1443 | } | ||
1444 | return get_slide(empty_slide_hid); | ||
1445 | } | ||
1446 | |||
1447 | /** | ||
1448 | adjust slides so that they are in "steady state" position | ||
1449 | */ | ||
1450 | void reset_slides(void) | ||
1451 | { | ||
1452 | center_slide.angle = 0; | ||
1453 | center_slide.cx = 0; | ||
1454 | center_slide.cy = 0; | ||
1455 | center_slide.distance = 0; | ||
1456 | center_slide.slide_index = center_index; | ||
1457 | |||
1458 | int i; | ||
1459 | for (i = 0; i < num_slides; i++) { | ||
1460 | struct slide_data *si = &left_slides[i]; | ||
1461 | si->angle = itilt; | ||
1462 | si->cx = -(offsetX + slide_spacing * i * PFREAL_ONE); | ||
1463 | si->cy = offsetY; | ||
1464 | si->slide_index = center_index - 1 - i; | ||
1465 | si->distance = 0; | ||
1466 | } | ||
1467 | |||
1468 | for (i = 0; i < num_slides; i++) { | ||
1469 | struct slide_data *si = &right_slides[i]; | ||
1470 | si->angle = -itilt; | ||
1471 | si->cx = offsetX + slide_spacing * i * PFREAL_ONE; | ||
1472 | si->cy = offsetY; | ||
1473 | si->slide_index = center_index + 1 + i; | ||
1474 | si->distance = 0; | ||
1475 | } | ||
1476 | } | ||
1477 | |||
1478 | |||
1479 | /** | ||
1480 | Updates look-up table and other stuff necessary for the rendering. | ||
1481 | Call this when the viewport size or slide dimension is changed. | ||
1482 | * | ||
1483 | * To calculate the offset that will provide the proper margin, we use the same | ||
1484 | * projection used to render the slides. The solution for xc, the slide center, | ||
1485 | * is: | ||
1486 | * xp * (zo + xs * sin(r)) | ||
1487 | * xc = xp - xs * cos(r) + ─────────────────────── | ||
1488 | * z | ||
1489 | * TODO: support moving the side slides toward or away from the camera | ||
1490 | */ | ||
1491 | void recalc_offsets(void) | ||
1492 | { | ||
1493 | PFreal xs = PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF; | ||
1494 | PFreal zo; | ||
1495 | PFreal xp = (DISPLAY_WIDTH * PFREAL_HALF - PFREAL_HALF + center_margin * | ||
1496 | PFREAL_ONE) * zoom / 100; | ||
1497 | PFreal cosr, sinr; | ||
1498 | |||
1499 | itilt = 70 * IANGLE_MAX / 360; /* approx. 70 degrees tilted */ | ||
1500 | cosr = fcos(-itilt); | ||
1501 | sinr = fsin(-itilt); | ||
1502 | zo = CAM_DIST_R * 100 / zoom - CAM_DIST_R + | ||
1503 | fmuln(MAXSLIDE_LEFT_R, sinr, PFREAL_SHIFT - 2, 0); | ||
1504 | offsetX = xp - fmul(xs, cosr) + fmuln(xp, | ||
1505 | zo + fmuln(xs, sinr, PFREAL_SHIFT - 2, 0), PFREAL_SHIFT - 2, 0) | ||
1506 | / CAM_DIST; | ||
1507 | offsetY = DISPLAY_WIDTH / 2 * (fsin(itilt) + PFREAL_ONE / 2); | ||
1508 | } | ||
1509 | |||
1510 | |||
1511 | /** | ||
1512 | Fade the given color by spreading the fb_data (ushort) | ||
1513 | to an uint, multiply and compress the result back to a ushort. | ||
1514 | */ | ||
1515 | #if (LCD_PIXELFORMAT == RGB565SWAPPED) | ||
1516 | static inline unsigned fade_color(pix_t c, unsigned a) | ||
1517 | { | ||
1518 | unsigned int result; | ||
1519 | c = swap16(c); | ||
1520 | a = (a + 2) & 0x1fc; | ||
1521 | result = ((c & 0xf81f) * a) & 0xf81f00; | ||
1522 | result |= ((c & 0x7e0) * a) & 0x7e000; | ||
1523 | result >>= 8; | ||
1524 | return swap16(result); | ||
1525 | } | ||
1526 | #elif LCD_PIXELFORMAT == RGB565 | ||
1527 | static inline unsigned fade_color(pix_t c, unsigned a) | ||
1528 | { | ||
1529 | unsigned int result; | ||
1530 | a = (a + 2) & 0x1fc; | ||
1531 | result = ((c & 0xf81f) * a) & 0xf81f00; | ||
1532 | result |= ((c & 0x7e0) * a) & 0x7e000; | ||
1533 | result >>= 8; | ||
1534 | return result; | ||
1535 | } | ||
1536 | #else | ||
1537 | static inline unsigned fade_color(pix_t c, unsigned a) | ||
1538 | { | ||
1539 | unsigned val = c; | ||
1540 | return MULUQ(val, a) >> 8; | ||
1541 | } | ||
1542 | #endif | ||
1543 | |||
1544 | /** | ||
1545 | * Render a single slide | ||
1546 | * Where xc is the slide's horizontal offset from center, xs is the horizontal | ||
1547 | * on the slide from its center, zo is the slide's depth offset from the plane | ||
1548 | * of the display, r is the angle at which the slide is tilted, and xp is the | ||
1549 | * point on the display corresponding to xs on the slide, the projection | ||
1550 | * formulas are: | ||
1551 | * | ||
1552 | * z * (xc + xs * cos(r)) | ||
1553 | * xp = ────────────────────── | ||
1554 | * z + zo + xs * sin(r) | ||
1555 | * | ||
1556 | * z * (xc - xp) - xp * zo | ||
1557 | * xs = ──────────────────────── | ||
1558 | * xp * sin(r) - z * cos(r) | ||
1559 | * | ||
1560 | * We use the xp projection once, to find the left edge of the slide on the | ||
1561 | * display. From there, we use the xs reverse projection to find the horizontal | ||
1562 | * offset from the slide center of each column on the screen, until we reach | ||
1563 | * the right edge of the slide, or the screen. The reverse projection can be | ||
1564 | * optimized by saving the numerator and denominator of the fraction, which can | ||
1565 | * then be incremented by (z + zo) and sin(r) respectively. | ||
1566 | */ | ||
1567 | void render_slide(struct slide_data *slide, const int alpha) | ||
1568 | { | ||
1569 | struct dim *bmp = surface(slide->slide_index); | ||
1570 | if (!bmp) { | ||
1571 | return; | ||
1572 | } | ||
1573 | if (slide->angle > 255 || slide->angle < -255) | ||
1574 | return; | ||
1575 | pix_t *src = (pix_t*)(sizeof(struct dim) + (char *)bmp); | ||
1576 | |||
1577 | const int sw = bmp->width; | ||
1578 | const int sh = bmp->height; | ||
1579 | const PFreal slide_left = -sw * PFREAL_HALF + PFREAL_HALF; | ||
1580 | const int w = LCD_WIDTH; | ||
1581 | |||
1582 | uint8_t reftab[REFLECT_HEIGHT]; /* on stack, which is in IRAM on several targets */ | ||
1583 | |||
1584 | if (alpha == 256) { /* opaque -> copy table */ | ||
1585 | rb->memcpy(reftab, reflect_table, sizeof(reftab)); | ||
1586 | } else { /* precalculate faded table */ | ||
1587 | int i, lalpha; | ||
1588 | for (i = 0; i < REFLECT_HEIGHT; i++) { | ||
1589 | lalpha = reflect_table[i]; | ||
1590 | reftab[i] = (MULUQ(lalpha, alpha) + 129) >> 8; | ||
1591 | } | ||
1592 | } | ||
1593 | |||
1594 | PFreal cosr = fcos(slide->angle); | ||
1595 | PFreal sinr = fsin(slide->angle); | ||
1596 | PFreal zo = PFREAL_ONE * slide->distance + CAM_DIST_R * 100 / zoom | ||
1597 | - CAM_DIST_R - fmuln(MAXSLIDE_LEFT_R, fabs(sinr), PFREAL_SHIFT - 2, 0); | ||
1598 | PFreal xs = slide_left, xsnum, xsnumi, xsden, xsdeni; | ||
1599 | PFreal xp = fdiv(CAM_DIST * (slide->cx + fmul(xs, cosr)), | ||
1600 | (CAM_DIST_R + zo + fmul(xs,sinr))); | ||
1601 | |||
1602 | /* Since we're finding the screen position of the left edge of the slide, | ||
1603 | * we round up. | ||
1604 | */ | ||
1605 | int xi = (fmax(DISPLAY_LEFT_R, xp) - DISPLAY_LEFT_R + PFREAL_ONE - 1) | ||
1606 | >> PFREAL_SHIFT; | ||
1607 | xp = DISPLAY_LEFT_R + xi * PFREAL_ONE; | ||
1608 | if (xi >= w) { | ||
1609 | return; | ||
1610 | } | ||
1611 | xsnum = CAM_DIST * (slide->cx - xp) - fmuln(xp, zo, PFREAL_SHIFT - 2, 0); | ||
1612 | xsden = fmuln(xp, sinr, PFREAL_SHIFT - 2, 0) - CAM_DIST * cosr; | ||
1613 | xs = fdiv(xsnum, xsden); | ||
1614 | |||
1615 | xsnumi = -CAM_DIST_R - zo; | ||
1616 | xsdeni = sinr; | ||
1617 | int x; | ||
1618 | int dy = PFREAL_ONE; | ||
1619 | for (x = xi; x < w; x++) { | ||
1620 | int column = (xs - slide_left) / PFREAL_ONE; | ||
1621 | if (column >= sw) | ||
1622 | break; | ||
1623 | if (zo || slide->angle) | ||
1624 | dy = (CAM_DIST_R + zo + fmul(xs, sinr)) / CAM_DIST; | ||
1625 | |||
1626 | const pix_t *ptr = &src[column * bmp->height]; | ||
1627 | const int pixelstep = BUFFER_WIDTH; | ||
1628 | |||
1629 | int p = (bmp->height-1-DISPLAY_OFFS) * PFREAL_ONE; | ||
1630 | int plim = MAX(0, p - (LCD_HEIGHT/2-1) * dy); | ||
1631 | pix_t *pixel = &buffer[((LCD_HEIGHT/2)-1)*BUFFER_WIDTH + x]; | ||
1632 | |||
1633 | if (alpha == 256) { | ||
1634 | while (p >= plim) { | ||
1635 | *pixel = ptr[((unsigned)p) >> PFREAL_SHIFT]; | ||
1636 | p -= dy; | ||
1637 | pixel -= pixelstep; | ||
1638 | } | ||
1639 | } else { | ||
1640 | while (p >= plim) { | ||
1641 | *pixel = fade_color(ptr[((unsigned)p) >> PFREAL_SHIFT], alpha); | ||
1642 | p -= dy; | ||
1643 | pixel -= pixelstep; | ||
1644 | } | ||
1645 | } | ||
1646 | p = (bmp->height-DISPLAY_OFFS) * PFREAL_ONE; | ||
1647 | plim = MIN(sh * PFREAL_ONE, p + (LCD_HEIGHT/2) * dy); | ||
1648 | int plim2 = MIN(MIN(sh + REFLECT_HEIGHT, sh * 2) * PFREAL_ONE, | ||
1649 | p + (LCD_HEIGHT/2) * dy); | ||
1650 | pixel = &buffer[(LCD_HEIGHT/2)*BUFFER_WIDTH + x]; | ||
1651 | |||
1652 | if (alpha == 256) { | ||
1653 | while (p < plim) { | ||
1654 | *pixel = ptr[((unsigned)p) >> PFREAL_SHIFT]; | ||
1655 | p += dy; | ||
1656 | pixel += pixelstep; | ||
1657 | } | ||
1658 | } else { | ||
1659 | while (p < plim) { | ||
1660 | *pixel = fade_color(ptr[((unsigned)p) >> PFREAL_SHIFT], alpha); | ||
1661 | p += dy; | ||
1662 | pixel += pixelstep; | ||
1663 | } | ||
1664 | } | ||
1665 | while (p < plim2) { | ||
1666 | int ty = (((unsigned)p) >> PFREAL_SHIFT) - sh; | ||
1667 | int lalpha = reftab[ty]; | ||
1668 | *pixel = fade_color(ptr[sh - 1 - ty], lalpha); | ||
1669 | p += dy; | ||
1670 | pixel += pixelstep; | ||
1671 | } | ||
1672 | |||
1673 | if (zo || slide->angle) | ||
1674 | { | ||
1675 | xsnum += xsnumi; | ||
1676 | xsden += xsdeni; | ||
1677 | xs = fdiv(xsnum, xsden); | ||
1678 | } else | ||
1679 | xs += PFREAL_ONE; | ||
1680 | |||
1681 | } | ||
1682 | /* let the music play... */ | ||
1683 | rb->yield(); | ||
1684 | return; | ||
1685 | } | ||
1686 | |||
1687 | |||
1688 | /** | ||
1689 | Jump the the given slide_index | ||
1690 | */ | ||
1691 | static inline void set_current_slide(const int slide_index) | ||
1692 | { | ||
1693 | int old_center_index = center_index; | ||
1694 | step = 0; | ||
1695 | center_index = fbound(slide_index, 0, number_of_slides - 1); | ||
1696 | if (old_center_index != center_index) | ||
1697 | rb->queue_post(&thread_q, EV_WAKEUP, 0); | ||
1698 | target = center_index; | ||
1699 | slide_frame = slide_index << 16; | ||
1700 | reset_slides(); | ||
1701 | } | ||
1702 | |||
1703 | /** | ||
1704 | Start the animation for changing slides | ||
1705 | */ | ||
1706 | void start_animation(void) | ||
1707 | { | ||
1708 | step = (target < center_slide.slide_index) ? -1 : 1; | ||
1709 | pf_state = pf_scrolling; | ||
1710 | } | ||
1711 | |||
1712 | /** | ||
1713 | Go to the previous slide | ||
1714 | */ | ||
1715 | void show_previous_slide(void) | ||
1716 | { | ||
1717 | if (step == 0) { | ||
1718 | if (center_index > 0) { | ||
1719 | target = center_index - 1; | ||
1720 | start_animation(); | ||
1721 | } | ||
1722 | } else if ( step > 0 ) { | ||
1723 | target = center_index; | ||
1724 | start_animation(); | ||
1725 | } else { | ||
1726 | target = fmax(0, center_index - 2); | ||
1727 | } | ||
1728 | } | ||
1729 | |||
1730 | |||
1731 | /** | ||
1732 | Go to the next slide | ||
1733 | */ | ||
1734 | void show_next_slide(void) | ||
1735 | { | ||
1736 | if (step == 0) { | ||
1737 | if (center_index < number_of_slides - 1) { | ||
1738 | target = center_index + 1; | ||
1739 | start_animation(); | ||
1740 | } | ||
1741 | } else if ( step < 0 ) { | ||
1742 | target = center_index; | ||
1743 | start_animation(); | ||
1744 | } else { | ||
1745 | target = fmin(center_index + 2, number_of_slides - 1); | ||
1746 | } | ||
1747 | } | ||
1748 | |||
1749 | |||
1750 | /** | ||
1751 | Render the slides. Updates only the offscreen buffer. | ||
1752 | */ | ||
1753 | void render_all_slides(void) | ||
1754 | { | ||
1755 | MYLCD(set_background)(G_BRIGHT(0)); | ||
1756 | /* TODO: Optimizes this by e.g. invalidating rects */ | ||
1757 | MYLCD(clear_display)(); | ||
1758 | |||
1759 | int nleft = num_slides; | ||
1760 | int nright = num_slides; | ||
1761 | |||
1762 | int index; | ||
1763 | if (step == 0) { | ||
1764 | /* no animation, boring plain rendering */ | ||
1765 | for (index = nleft - 2; index >= 0; index--) { | ||
1766 | int alpha = (index < nleft - 2) ? 256 : 128; | ||
1767 | alpha -= extra_fade; | ||
1768 | if (alpha > 0 ) | ||
1769 | render_slide(&left_slides[index], alpha); | ||
1770 | } | ||
1771 | for (index = nright - 2; index >= 0; index--) { | ||
1772 | int alpha = (index < nright - 2) ? 256 : 128; | ||
1773 | alpha -= extra_fade; | ||
1774 | if (alpha > 0 ) | ||
1775 | render_slide(&right_slides[index], alpha); | ||
1776 | } | ||
1777 | } else { | ||
1778 | /* the first and last slide must fade in/fade out */ | ||
1779 | for (index = nleft - 1; index >= 0; index--) { | ||
1780 | int alpha = 256; | ||
1781 | if (index == nleft - 1) | ||
1782 | alpha = (step > 0) ? 0 : 128 - fade / 2; | ||
1783 | if (index == nleft - 2) | ||
1784 | alpha = (step > 0) ? 128 - fade / 2 : 256 - fade / 2; | ||
1785 | if (index == nleft - 3) | ||
1786 | alpha = (step > 0) ? 256 - fade / 2 : 256; | ||
1787 | render_slide(&left_slides[index], alpha); | ||
1788 | } | ||
1789 | for (index = nright - 1; index >= 0; index--) { | ||
1790 | int alpha = (index < nright - 2) ? 256 : 128; | ||
1791 | if (index == nright - 1) | ||
1792 | alpha = (step > 0) ? fade / 2 : 0; | ||
1793 | if (index == nright - 2) | ||
1794 | alpha = (step > 0) ? 128 + fade / 2 : fade / 2; | ||
1795 | if (index == nright - 3) | ||
1796 | alpha = (step > 0) ? 256 : 128 + fade / 2; | ||
1797 | render_slide(&right_slides[index], alpha); | ||
1798 | } | ||
1799 | } | ||
1800 | render_slide(¢er_slide, 256); | ||
1801 | } | ||
1802 | |||
1803 | |||
1804 | /** | ||
1805 | Updates the animation effect. Call this periodically from a timer. | ||
1806 | */ | ||
1807 | void update_scroll_animation(void) | ||
1808 | { | ||
1809 | if (step == 0) | ||
1810 | return; | ||
1811 | |||
1812 | int speed = 16384; | ||
1813 | int i; | ||
1814 | |||
1815 | /* deaccelerate when approaching the target */ | ||
1816 | if (true) { | ||
1817 | const int max = 2 * 65536; | ||
1818 | |||
1819 | int fi = slide_frame; | ||
1820 | fi -= (target << 16); | ||
1821 | if (fi < 0) | ||
1822 | fi = -fi; | ||
1823 | fi = fmin(fi, max); | ||
1824 | |||
1825 | int ia = IANGLE_MAX * (fi - max / 2) / (max * 2); | ||
1826 | speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE; | ||
1827 | } | ||
1828 | |||
1829 | slide_frame += speed * step; | ||
1830 | |||
1831 | int index = slide_frame >> 16; | ||
1832 | int pos = slide_frame & 0xffff; | ||
1833 | int neg = 65536 - pos; | ||
1834 | int tick = (step < 0) ? neg : pos; | ||
1835 | PFreal ftick = (tick * PFREAL_ONE) >> 16; | ||
1836 | |||
1837 | /* the leftmost and rightmost slide must fade away */ | ||
1838 | fade = pos / 256; | ||
1839 | |||
1840 | if (step < 0) | ||
1841 | index++; | ||
1842 | if (center_index != index) { | ||
1843 | center_index = index; | ||
1844 | rb->queue_post(&thread_q, EV_WAKEUP, 0); | ||
1845 | slide_frame = index << 16; | ||
1846 | center_slide.slide_index = center_index; | ||
1847 | for (i = 0; i < num_slides; i++) | ||
1848 | left_slides[i].slide_index = center_index - 1 - i; | ||
1849 | for (i = 0; i < num_slides; i++) | ||
1850 | right_slides[i].slide_index = center_index + 1 + i; | ||
1851 | } | ||
1852 | |||
1853 | center_slide.angle = (step * tick * itilt) >> 16; | ||
1854 | center_slide.cx = -step * fmul(offsetX, ftick); | ||
1855 | center_slide.cy = fmul(offsetY, ftick); | ||
1856 | |||
1857 | if (center_index == target) { | ||
1858 | reset_slides(); | ||
1859 | pf_state = pf_idle; | ||
1860 | step = 0; | ||
1861 | fade = 256; | ||
1862 | return; | ||
1863 | } | ||
1864 | |||
1865 | for (i = 0; i < num_slides; i++) { | ||
1866 | struct slide_data *si = &left_slides[i]; | ||
1867 | si->angle = itilt; | ||
1868 | si->cx = | ||
1869 | -(offsetX + slide_spacing * i * PFREAL_ONE + step | ||
1870 | * slide_spacing * ftick); | ||
1871 | si->cy = offsetY; | ||
1872 | } | ||
1873 | |||
1874 | for (i = 0; i < num_slides; i++) { | ||
1875 | struct slide_data *si = &right_slides[i]; | ||
1876 | si->angle = -itilt; | ||
1877 | si->cx = | ||
1878 | offsetX + slide_spacing * i * PFREAL_ONE - step | ||
1879 | * slide_spacing * ftick; | ||
1880 | si->cy = offsetY; | ||
1881 | } | ||
1882 | |||
1883 | if (step > 0) { | ||
1884 | PFreal ftick = (neg * PFREAL_ONE) >> 16; | ||
1885 | right_slides[0].angle = -(neg * itilt) >> 16; | ||
1886 | right_slides[0].cx = fmul(offsetX, ftick); | ||
1887 | right_slides[0].cy = fmul(offsetY, ftick); | ||
1888 | } else { | ||
1889 | PFreal ftick = (pos * PFREAL_ONE) >> 16; | ||
1890 | left_slides[0].angle = (pos * itilt) >> 16; | ||
1891 | left_slides[0].cx = -fmul(offsetX, ftick); | ||
1892 | left_slides[0].cy = fmul(offsetY, ftick); | ||
1893 | } | ||
1894 | |||
1895 | /* must change direction ? */ | ||
1896 | if (target < index) | ||
1897 | if (step > 0) | ||
1898 | step = -1; | ||
1899 | if (target > index) | ||
1900 | if (step < 0) | ||
1901 | step = 1; | ||
1902 | } | ||
1903 | |||
1904 | |||
1905 | /** | ||
1906 | Cleanup the plugin | ||
1907 | */ | ||
1908 | void cleanup(void *parameter) | ||
1909 | { | ||
1910 | (void) parameter; | ||
1911 | /* Turn on backlight timeout (revert to settings) */ | ||
1912 | backlight_use_settings(); /* backlight control in lib/helper.c */ | ||
1913 | |||
1914 | #ifdef USEGSLIB | ||
1915 | grey_release(); | ||
1916 | #endif | ||
1917 | } | ||
1918 | |||
1919 | /** | ||
1920 | Create the "?" slide, that is shown while loading | ||
1921 | or when no cover was found. | ||
1922 | */ | ||
1923 | int create_empty_slide(bool force) | ||
1924 | { | ||
1925 | if ( force || ! rb->file_exists( EMPTY_SLIDE ) ) { | ||
1926 | struct bitmap input_bmp; | ||
1927 | int ret; | ||
1928 | input_bmp.width = DISPLAY_WIDTH; | ||
1929 | input_bmp.height = DISPLAY_HEIGHT; | ||
1930 | #if LCD_DEPTH > 1 | ||
1931 | input_bmp.format = FORMAT_NATIVE; | ||
1932 | #endif | ||
1933 | input_bmp.data = (char*)buf; | ||
1934 | ret = scaled_read_bmp_file(EMPTY_SLIDE_BMP, &input_bmp, | ||
1935 | buf_size, | ||
1936 | FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_KEEP_ASPECT, | ||
1937 | &format_transposed); | ||
1938 | if (!save_pfraw(EMPTY_SLIDE, &input_bmp)) | ||
1939 | return false; | ||
1940 | } | ||
1941 | |||
1942 | return true; | ||
1943 | } | ||
1944 | |||
1945 | /** | ||
1946 | Shows the album name setting menu | ||
1947 | */ | ||
1948 | int album_name_menu(void) | ||
1949 | { | ||
1950 | int selection = show_album_name; | ||
1951 | |||
1952 | MENUITEM_STRINGLIST(album_name_menu,"Show album title",NULL, | ||
1953 | "Hide album title", "Show at the bottom", "Show at the top"); | ||
1954 | rb->do_menu(&album_name_menu, &selection, NULL, false); | ||
1955 | |||
1956 | show_album_name = selection; | ||
1957 | return GO_TO_PREVIOUS; | ||
1958 | } | ||
1959 | |||
1960 | /** | ||
1961 | Shows the settings menu | ||
1962 | */ | ||
1963 | int settings_menu(void) | ||
1964 | { | ||
1965 | int selection = 0; | ||
1966 | bool old_val; | ||
1967 | |||
1968 | MENUITEM_STRINGLIST(settings_menu, "PictureFlow Settings", NULL, "Show FPS", | ||
1969 | "Spacing", "Centre margin", "Number of slides", "Zoom", | ||
1970 | "Show album title", "Resize Covers", "Rebuild cache"); | ||
1971 | |||
1972 | do { | ||
1973 | selection=rb->do_menu(&settings_menu,&selection, NULL, false); | ||
1974 | switch(selection) { | ||
1975 | case 0: | ||
1976 | rb->set_bool("Show FPS", &show_fps); | ||
1977 | reset_track_list(); | ||
1978 | break; | ||
1979 | |||
1980 | case 1: | ||
1981 | rb->set_int("Spacing between slides", "", 1, | ||
1982 | &slide_spacing, | ||
1983 | NULL, 1, 0, 100, NULL ); | ||
1984 | recalc_offsets(); | ||
1985 | reset_slides(); | ||
1986 | break; | ||
1987 | |||
1988 | case 2: | ||
1989 | rb->set_int("Centre margin", "", 1, | ||
1990 | ¢er_margin, | ||
1991 | NULL, 1, 0, 80, NULL ); | ||
1992 | recalc_offsets(); | ||
1993 | reset_slides(); | ||
1994 | break; | ||
1995 | |||
1996 | case 3: | ||
1997 | rb->set_int("Number of slides", "", 1, &num_slides, | ||
1998 | NULL, 1, 1, MAX_SLIDES_COUNT, NULL ); | ||
1999 | recalc_offsets(); | ||
2000 | reset_slides(); | ||
2001 | break; | ||
2002 | |||
2003 | case 4: | ||
2004 | rb->set_int("Zoom", "", 1, &zoom, | ||
2005 | NULL, 1, 10, 300, NULL ); | ||
2006 | recalc_offsets(); | ||
2007 | reset_slides(); | ||
2008 | break; | ||
2009 | case 5: | ||
2010 | album_name_menu(); | ||
2011 | reset_track_list(); | ||
2012 | recalc_offsets(); | ||
2013 | reset_slides(); | ||
2014 | break; | ||
2015 | case 6: | ||
2016 | old_val = resize; | ||
2017 | rb->set_bool("Resize Covers", &resize); | ||
2018 | if (old_val == resize) /* changed? */ | ||
2019 | break; | ||
2020 | /* fallthrough if changed, since cache needs to be rebuilt */ | ||
2021 | case 7: | ||
2022 | cache_version = 0; | ||
2023 | rb->remove(EMPTY_SLIDE); | ||
2024 | rb->splash(HZ, "Cache will be rebuilt on next restart"); | ||
2025 | break; | ||
2026 | |||
2027 | case MENU_ATTACHED_USB: | ||
2028 | return PLUGIN_USB_CONNECTED; | ||
2029 | } | ||
2030 | } while ( selection >= 0 ); | ||
2031 | configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); | ||
2032 | return 0; | ||
2033 | } | ||
2034 | |||
2035 | /** | ||
2036 | Show the main menu | ||
2037 | */ | ||
2038 | int main_menu(void) | ||
2039 | { | ||
2040 | int selection = 0; | ||
2041 | int result; | ||
2042 | |||
2043 | #if LCD_DEPTH > 1 | ||
2044 | rb->lcd_set_foreground(N_BRIGHT(255)); | ||
2045 | #endif | ||
2046 | |||
2047 | MENUITEM_STRINGLIST(main_menu,"PictureFlow Main Menu",NULL, | ||
2048 | "Settings", "Return", "Quit"); | ||
2049 | while (1) { | ||
2050 | switch (rb->do_menu(&main_menu,&selection, NULL, false)) { | ||
2051 | case 0: | ||
2052 | result = settings_menu(); | ||
2053 | if ( result != 0 ) return result; | ||
2054 | break; | ||
2055 | |||
2056 | case 1: | ||
2057 | return 0; | ||
2058 | |||
2059 | case 2: | ||
2060 | return -1; | ||
2061 | |||
2062 | case MENU_ATTACHED_USB: | ||
2063 | return PLUGIN_USB_CONNECTED; | ||
2064 | |||
2065 | default: | ||
2066 | return 0; | ||
2067 | } | ||
2068 | } | ||
2069 | } | ||
2070 | |||
2071 | /** | ||
2072 | Animation step for zooming into the current cover | ||
2073 | */ | ||
2074 | void update_cover_in_animation(void) | ||
2075 | { | ||
2076 | cover_animation_keyframe++; | ||
2077 | if( cover_animation_keyframe < 20 ) { | ||
2078 | center_slide.distance-=5; | ||
2079 | center_slide.angle+=1; | ||
2080 | extra_fade += 13; | ||
2081 | } | ||
2082 | else if( cover_animation_keyframe < 35 ) { | ||
2083 | center_slide.angle+=16; | ||
2084 | } | ||
2085 | else { | ||
2086 | cover_animation_keyframe = 0; | ||
2087 | pf_state = pf_show_tracks; | ||
2088 | } | ||
2089 | } | ||
2090 | |||
2091 | /** | ||
2092 | Animation step for zooming out the current cover | ||
2093 | */ | ||
2094 | void update_cover_out_animation(void) | ||
2095 | { | ||
2096 | cover_animation_keyframe++; | ||
2097 | if( cover_animation_keyframe <= 15 ) { | ||
2098 | center_slide.angle-=16; | ||
2099 | } | ||
2100 | else if( cover_animation_keyframe < 35 ) { | ||
2101 | center_slide.distance+=5; | ||
2102 | center_slide.angle-=1; | ||
2103 | extra_fade -= 13; | ||
2104 | } | ||
2105 | else { | ||
2106 | cover_animation_keyframe = 0; | ||
2107 | pf_state = pf_idle; | ||
2108 | } | ||
2109 | } | ||
2110 | |||
2111 | /** | ||
2112 | Draw a blue gradient at y with height h | ||
2113 | */ | ||
2114 | static inline void draw_gradient(int y, int h) | ||
2115 | { | ||
2116 | static int r, inc, c; | ||
2117 | inc = (100 << 8) / h; | ||
2118 | c = 0; | ||
2119 | selected_track_pulse = (selected_track_pulse+1) % 10; | ||
2120 | int c2 = selected_track_pulse - 5; | ||
2121 | for (r=0; r<h; r++) { | ||
2122 | #ifdef HAVE_LCD_COLOR | ||
2123 | MYLCD(set_foreground)(G_PIX(c2+80-(c >> 9), c2+100-(c >> 9), | ||
2124 | c2+250-(c >> 8))); | ||
2125 | #else | ||
2126 | MYLCD(set_foreground)(G_BRIGHT(c2+160-(c >> 8))); | ||
2127 | #endif | ||
2128 | MYLCD(hline)(0, LCD_WIDTH, r+y); | ||
2129 | if ( r > h/2 ) | ||
2130 | c-=inc; | ||
2131 | else | ||
2132 | c+=inc; | ||
2133 | } | ||
2134 | } | ||
2135 | |||
2136 | |||
2137 | static void track_list_yh(int char_height) | ||
2138 | { | ||
2139 | switch (show_album_name) | ||
2140 | { | ||
2141 | case album_name_hide: | ||
2142 | track_list_y = (show_fps ? char_height : 0); | ||
2143 | track_list_h = LCD_HEIGHT - track_list_y; | ||
2144 | break; | ||
2145 | case album_name_bottom: | ||
2146 | track_list_y = (show_fps ? char_height : 0); | ||
2147 | track_list_h = LCD_HEIGHT - track_list_y - char_height * 2; | ||
2148 | break; | ||
2149 | default: /* case album_name_top */ | ||
2150 | track_list_y = char_height * 2; | ||
2151 | track_list_h = LCD_HEIGHT - track_list_y - | ||
2152 | (show_fps ? char_height : 0); | ||
2153 | break; | ||
2154 | } | ||
2155 | } | ||
2156 | |||
2157 | /** | ||
2158 | Reset the track list after a album change | ||
2159 | */ | ||
2160 | void reset_track_list(void) | ||
2161 | { | ||
2162 | int albumtxt_h = rb->screens[SCREEN_MAIN]->getcharheight(); | ||
2163 | track_list_yh(albumtxt_h); | ||
2164 | track_list_visible_entries = fmin( track_list_h/albumtxt_h , track_count ); | ||
2165 | start_index_track_list = 0; | ||
2166 | track_scroll_index = 0; | ||
2167 | track_scroll_dir = 1; | ||
2168 | selected_track = 0; | ||
2169 | |||
2170 | /* let the tracklist start more centered | ||
2171 | * if the screen isn't filled with tracks */ | ||
2172 | if (track_count*albumtxt_h < track_list_h) | ||
2173 | { | ||
2174 | track_list_h = track_count * albumtxt_h; | ||
2175 | track_list_y = LCD_HEIGHT / 2 - (track_list_h / 2); | ||
2176 | } | ||
2177 | } | ||
2178 | |||
2179 | /** | ||
2180 | Display the list of tracks | ||
2181 | */ | ||
2182 | void show_track_list(void) | ||
2183 | { | ||
2184 | MYLCD(clear_display)(); | ||
2185 | if ( center_slide.slide_index != track_index ) { | ||
2186 | create_track_index(center_slide.slide_index); | ||
2187 | reset_track_list(); | ||
2188 | } | ||
2189 | static int titletxt_w, titletxt_x, color, titletxt_h; | ||
2190 | titletxt_h = rb->screens[SCREEN_MAIN]->getcharheight(); | ||
2191 | |||
2192 | int titletxt_y = track_list_y; | ||
2193 | int track_i; | ||
2194 | track_i = start_index_track_list; | ||
2195 | for (;track_i < track_list_visible_entries+start_index_track_list; | ||
2196 | track_i++) | ||
2197 | { | ||
2198 | MYLCD(getstringsize)(get_track_name(track_i), &titletxt_w, NULL); | ||
2199 | titletxt_x = (LCD_WIDTH-titletxt_w)/2; | ||
2200 | if ( track_i == selected_track ) { | ||
2201 | draw_gradient(titletxt_y, titletxt_h); | ||
2202 | MYLCD(set_foreground)(G_BRIGHT(255)); | ||
2203 | if (titletxt_w > LCD_WIDTH ) { | ||
2204 | if ( titletxt_w + track_scroll_index <= LCD_WIDTH ) | ||
2205 | track_scroll_dir = 1; | ||
2206 | else if ( track_scroll_index >= 0 ) track_scroll_dir = -1; | ||
2207 | track_scroll_index += track_scroll_dir*2; | ||
2208 | titletxt_x = track_scroll_index; | ||
2209 | } | ||
2210 | MYLCD(putsxy)(titletxt_x,titletxt_y,get_track_name(track_i)); | ||
2211 | } | ||
2212 | else { | ||
2213 | color = 250 - (abs(selected_track - track_i) * 200 / track_count); | ||
2214 | MYLCD(set_foreground)(G_BRIGHT(color)); | ||
2215 | MYLCD(putsxy)(titletxt_x,titletxt_y,get_track_name(track_i)); | ||
2216 | } | ||
2217 | titletxt_y += titletxt_h; | ||
2218 | } | ||
2219 | } | ||
2220 | |||
2221 | void select_next_track(void) | ||
2222 | { | ||
2223 | if ( selected_track < track_count - 1 ) { | ||
2224 | selected_track++; | ||
2225 | track_scroll_index = 0; | ||
2226 | track_scroll_dir = 1; | ||
2227 | if (selected_track==(track_list_visible_entries+start_index_track_list)) | ||
2228 | start_index_track_list++; | ||
2229 | } | ||
2230 | } | ||
2231 | |||
2232 | void select_prev_track(void) | ||
2233 | { | ||
2234 | if (selected_track > 0 ) { | ||
2235 | if (selected_track==start_index_track_list) start_index_track_list--; | ||
2236 | track_scroll_index = 0; | ||
2237 | track_scroll_dir = 1; | ||
2238 | selected_track--; | ||
2239 | } | ||
2240 | } | ||
2241 | |||
2242 | /** | ||
2243 | Draw the current album name | ||
2244 | */ | ||
2245 | void draw_album_text(void) | ||
2246 | { | ||
2247 | if (0 == show_album_name) | ||
2248 | return; | ||
2249 | |||
2250 | int albumtxt_w, albumtxt_h; | ||
2251 | int albumtxt_y = 0; | ||
2252 | |||
2253 | char *albumtxt; | ||
2254 | int c; | ||
2255 | /* Draw album text */ | ||
2256 | if ( pf_state == pf_scrolling ) { | ||
2257 | c = ((slide_frame & 0xffff )/ 255); | ||
2258 | if (step < 0) c = 255-c; | ||
2259 | if (c > 128 ) { /* half way to next slide .. still not perfect! */ | ||
2260 | albumtxt = get_album_name(center_index+step); | ||
2261 | c = (c-128)*2; | ||
2262 | } | ||
2263 | else { | ||
2264 | albumtxt = get_album_name(center_index); | ||
2265 | c = (128-c)*2; | ||
2266 | } | ||
2267 | } | ||
2268 | else { | ||
2269 | c= 255; | ||
2270 | albumtxt = get_album_name(center_index); | ||
2271 | } | ||
2272 | |||
2273 | MYLCD(set_foreground)(G_BRIGHT(c)); | ||
2274 | MYLCD(getstringsize)(albumtxt, &albumtxt_w, &albumtxt_h); | ||
2275 | if (center_index != prev_center_index) { | ||
2276 | albumtxt_x = 0; | ||
2277 | albumtxt_dir = -1; | ||
2278 | prev_center_index = center_index; | ||
2279 | } | ||
2280 | |||
2281 | if (show_album_name == album_name_top) | ||
2282 | albumtxt_y = albumtxt_h / 2; | ||
2283 | else | ||
2284 | albumtxt_y = LCD_HEIGHT - albumtxt_h - albumtxt_h/2; | ||
2285 | |||
2286 | if (albumtxt_w > LCD_WIDTH ) { | ||
2287 | MYLCD(putsxy)(albumtxt_x, albumtxt_y , albumtxt); | ||
2288 | if ( pf_state == pf_idle || pf_state == pf_show_tracks ) { | ||
2289 | if ( albumtxt_w + albumtxt_x <= LCD_WIDTH ) albumtxt_dir = 1; | ||
2290 | else if ( albumtxt_x >= 0 ) albumtxt_dir = -1; | ||
2291 | albumtxt_x += albumtxt_dir; | ||
2292 | } | ||
2293 | } | ||
2294 | else { | ||
2295 | MYLCD(putsxy)((LCD_WIDTH - albumtxt_w) /2, albumtxt_y , albumtxt); | ||
2296 | } | ||
2297 | |||
2298 | |||
2299 | } | ||
2300 | |||
2301 | |||
2302 | /** | ||
2303 | Main function that also contain the main plasma | ||
2304 | algorithm. | ||
2305 | */ | ||
2306 | int main(void) | ||
2307 | { | ||
2308 | int ret; | ||
2309 | |||
2310 | rb->lcd_setfont(FONT_UI); | ||
2311 | draw_splashscreen(); | ||
2312 | |||
2313 | if ( ! rb->dir_exists( CACHE_PREFIX ) ) { | ||
2314 | if ( rb->mkdir( CACHE_PREFIX ) < 0 ) { | ||
2315 | rb->splash(HZ, "Could not create directory " CACHE_PREFIX ); | ||
2316 | return PLUGIN_ERROR; | ||
2317 | } | ||
2318 | } | ||
2319 | |||
2320 | configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); | ||
2321 | |||
2322 | init_reflect_table(); | ||
2323 | |||
2324 | ALIGN_BUFFER(buf, buf_size, 4); | ||
2325 | ret = create_album_index(); | ||
2326 | if (ret == ERROR_BUFFER_FULL) { | ||
2327 | rb->splash(HZ, "Not enough memory for album names"); | ||
2328 | return PLUGIN_ERROR; | ||
2329 | } else if (ret == ERROR_NO_ALBUMS) { | ||
2330 | rb->splash(HZ, "No albums found. Please enable database"); | ||
2331 | return PLUGIN_ERROR; | ||
2332 | } | ||
2333 | |||
2334 | ALIGN_BUFFER(buf, buf_size, 4); | ||
2335 | number_of_slides = album_count; | ||
2336 | if ((cache_version != CACHE_VERSION) && !create_albumart_cache()) { | ||
2337 | rb->splash(HZ, "Could not create album art cache"); | ||
2338 | return PLUGIN_ERROR; | ||
2339 | } | ||
2340 | |||
2341 | if (!create_empty_slide(cache_version != CACHE_VERSION)) { | ||
2342 | rb->splash(HZ, "Could not load the empty slide"); | ||
2343 | return PLUGIN_ERROR; | ||
2344 | } | ||
2345 | cache_version = CACHE_VERSION; | ||
2346 | configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); | ||
2347 | |||
2348 | |||
2349 | #ifdef USEGSLIB | ||
2350 | long grey_buf_used; | ||
2351 | if (!grey_init(buf, buf_size, GREY_BUFFERED|GREY_ON_COP, | ||
2352 | LCD_WIDTH, LCD_HEIGHT, &grey_buf_used)) | ||
2353 | { | ||
2354 | rb->splash(HZ, "Greylib init failed!"); | ||
2355 | return PLUGIN_ERROR; | ||
2356 | } | ||
2357 | grey_setfont(FONT_UI); | ||
2358 | buf_size -= grey_buf_used; | ||
2359 | buf = (void*)(grey_buf_used + (char*)buf); | ||
2360 | #endif | ||
2361 | buflib_init(&buf_ctx, (void *)buf, buf_size); | ||
2362 | |||
2363 | if (!(empty_slide_hid = read_pfraw(EMPTY_SLIDE, 0))) | ||
2364 | { | ||
2365 | rb->splash(HZ, "Unable to load empty slide image"); | ||
2366 | return PLUGIN_ERROR; | ||
2367 | } | ||
2368 | |||
2369 | if (!create_pf_thread()) { | ||
2370 | rb->splash(HZ, "Cannot create thread!"); | ||
2371 | return PLUGIN_ERROR; | ||
2372 | } | ||
2373 | |||
2374 | int i; | ||
2375 | |||
2376 | /* initialize */ | ||
2377 | for (i = 0; i < SLIDE_CACHE_SIZE; i++) { | ||
2378 | cache[i].hid = 0; | ||
2379 | cache[i].index = 0; | ||
2380 | cache[i].next = i + 1; | ||
2381 | cache[i].prev = i - 1; | ||
2382 | } | ||
2383 | cache[0].prev = i - 1; | ||
2384 | cache[i - 1].next = 0; | ||
2385 | cache_free = 0; | ||
2386 | buffer = LCD_BUF; | ||
2387 | |||
2388 | pf_state = pf_idle; | ||
2389 | |||
2390 | track_index = -1; | ||
2391 | extra_fade = 0; | ||
2392 | slide_frame = 0; | ||
2393 | step = 0; | ||
2394 | target = 0; | ||
2395 | fade = 256; | ||
2396 | |||
2397 | recalc_offsets(); | ||
2398 | reset_slides(); | ||
2399 | |||
2400 | char fpstxt[10]; | ||
2401 | int button; | ||
2402 | |||
2403 | int frames = 0; | ||
2404 | long last_update = *rb->current_tick; | ||
2405 | long current_update; | ||
2406 | long update_interval = 100; | ||
2407 | int fps = 0; | ||
2408 | int fpstxt_y; | ||
2409 | |||
2410 | bool instant_update; | ||
2411 | #ifdef USEGSLIB | ||
2412 | grey_show(true); | ||
2413 | grey_set_drawmode(DRMODE_FG); | ||
2414 | #endif | ||
2415 | rb->lcd_set_drawmode(DRMODE_FG); | ||
2416 | while (true) { | ||
2417 | current_update = *rb->current_tick; | ||
2418 | frames++; | ||
2419 | |||
2420 | /* Initial rendering */ | ||
2421 | instant_update = false; | ||
2422 | |||
2423 | /* Handle states */ | ||
2424 | switch ( pf_state ) { | ||
2425 | case pf_scrolling: | ||
2426 | update_scroll_animation(); | ||
2427 | render_all_slides(); | ||
2428 | instant_update = true; | ||
2429 | break; | ||
2430 | case pf_cover_in: | ||
2431 | update_cover_in_animation(); | ||
2432 | render_all_slides(); | ||
2433 | instant_update = true; | ||
2434 | break; | ||
2435 | case pf_cover_out: | ||
2436 | update_cover_out_animation(); | ||
2437 | render_all_slides(); | ||
2438 | instant_update = true; | ||
2439 | break; | ||
2440 | case pf_show_tracks: | ||
2441 | show_track_list(); | ||
2442 | break; | ||
2443 | case pf_idle: | ||
2444 | render_all_slides(); | ||
2445 | break; | ||
2446 | } | ||
2447 | |||
2448 | /* Calculate FPS */ | ||
2449 | if (current_update - last_update > update_interval) { | ||
2450 | fps = frames * HZ / (current_update - last_update); | ||
2451 | last_update = current_update; | ||
2452 | frames = 0; | ||
2453 | } | ||
2454 | /* Draw FPS */ | ||
2455 | if (show_fps) | ||
2456 | { | ||
2457 | #ifdef USEGSLIB | ||
2458 | MYLCD(set_foreground)(G_BRIGHT(255)); | ||
2459 | #else | ||
2460 | MYLCD(set_foreground)(G_PIX(255,0,0)); | ||
2461 | #endif | ||
2462 | rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps); | ||
2463 | if (show_album_name == album_name_top) | ||
2464 | fpstxt_y = LCD_HEIGHT - | ||
2465 | rb->screens[SCREEN_MAIN]->getcharheight(); | ||
2466 | else | ||
2467 | fpstxt_y = 0; | ||
2468 | MYLCD(putsxy)(0, fpstxt_y, fpstxt); | ||
2469 | } | ||
2470 | draw_album_text(); | ||
2471 | |||
2472 | |||
2473 | /* Copy offscreen buffer to LCD and give time to other threads */ | ||
2474 | MYLCD(update)(); | ||
2475 | rb->yield(); | ||
2476 | |||
2477 | /*/ Handle buttons */ | ||
2478 | button = rb->get_custom_action(CONTEXT_CUSTOM| | ||
2479 | (pf_state == pf_show_tracks ? 1 : 0), | ||
2480 | instant_update ? 0 : HZ/16, | ||
2481 | get_context_map); | ||
2482 | |||
2483 | switch (button) { | ||
2484 | case PF_QUIT: | ||
2485 | return PLUGIN_OK; | ||
2486 | |||
2487 | case PF_BACK: | ||
2488 | if ( pf_state == pf_show_tracks ) | ||
2489 | pf_state = pf_cover_out; | ||
2490 | if (pf_state == pf_idle || pf_state == pf_scrolling) | ||
2491 | return PLUGIN_OK; | ||
2492 | break; | ||
2493 | |||
2494 | case PF_MENU: | ||
2495 | #ifdef USEGSLIB | ||
2496 | grey_show(false); | ||
2497 | #endif | ||
2498 | ret = main_menu(); | ||
2499 | if ( ret == -1 ) return PLUGIN_OK; | ||
2500 | if ( ret != 0 ) return i; | ||
2501 | #ifdef USEGSLIB | ||
2502 | grey_show(true); | ||
2503 | #endif | ||
2504 | MYLCD(set_drawmode)(DRMODE_FG); | ||
2505 | break; | ||
2506 | |||
2507 | case PF_NEXT: | ||
2508 | case PF_NEXT_REPEAT: | ||
2509 | if ( pf_state == pf_show_tracks ) | ||
2510 | select_next_track(); | ||
2511 | if ( pf_state == pf_idle || pf_state == pf_scrolling ) | ||
2512 | show_next_slide(); | ||
2513 | break; | ||
2514 | |||
2515 | case PF_PREV: | ||
2516 | case PF_PREV_REPEAT: | ||
2517 | if ( pf_state == pf_show_tracks ) | ||
2518 | select_prev_track(); | ||
2519 | if ( pf_state == pf_idle || pf_state == pf_scrolling ) | ||
2520 | show_previous_slide(); | ||
2521 | break; | ||
2522 | |||
2523 | case PF_SELECT: | ||
2524 | if ( pf_state == pf_idle ) { | ||
2525 | pf_state = pf_cover_in; | ||
2526 | } | ||
2527 | break; | ||
2528 | |||
2529 | default: | ||
2530 | if (rb->default_event_handler_ex(button, cleanup, NULL) | ||
2531 | == SYS_USB_CONNECTED) | ||
2532 | return PLUGIN_USB_CONNECTED; | ||
2533 | break; | ||
2534 | } | ||
2535 | } | ||
2536 | |||
2537 | |||
2538 | } | ||
2539 | |||
2540 | /*************************** Plugin entry point ****************************/ | ||
2541 | |||
2542 | enum plugin_status plugin_start(const void *parameter) | ||
2543 | { | ||
2544 | int ret; | ||
2545 | (void) parameter; | ||
2546 | #if LCD_DEPTH > 1 | ||
2547 | rb->lcd_set_backdrop(NULL); | ||
2548 | #endif | ||
2549 | /* Turn off backlight timeout */ | ||
2550 | backlight_force_on(); /* backlight control in lib/helper.c */ | ||
2551 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
2552 | rb->cpu_boost(true); | ||
2553 | #endif | ||
2554 | #if PLUGIN_BUFFER_SIZE > 0x10000 && 0 | ||
2555 | buf = rb->plugin_get_buffer(&buf_size); | ||
2556 | #else | ||
2557 | buf = rb->plugin_get_audio_buffer(&buf_size); | ||
2558 | #endif | ||
2559 | ret = main(); | ||
2560 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
2561 | rb->cpu_boost(false); | ||
2562 | #endif | ||
2563 | if ( ret == PLUGIN_OK ) { | ||
2564 | if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, | ||
2565 | CONFIG_VERSION)) | ||
2566 | { | ||
2567 | rb->splash(HZ, "Error writing config."); | ||
2568 | ret = PLUGIN_ERROR; | ||
2569 | } | ||
2570 | } | ||
2571 | |||
2572 | end_pf_thread(); | ||
2573 | cleanup(NULL); | ||
2574 | return ret; | ||
2575 | } | ||
diff --git a/apps/plugins/pictureflow/pictureflow.make b/apps/plugins/pictureflow/pictureflow.make new file mode 100644 index 0000000000..c1fb1f3957 --- /dev/null +++ b/apps/plugins/pictureflow/pictureflow.make | |||
@@ -0,0 +1,58 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | PICTUREFLOW_SRCDIR = $(APPSDIR)/plugins/pictureflow | ||
11 | PICTUREFLOW_OBJDIR = $(BUILDDIR)/apps/plugins/pictureflow | ||
12 | |||
13 | PICTUREFLOW_SRC := $(call preprocess, $(PICTUREFLOW_SRCDIR)/SOURCES) | ||
14 | PICTUREFLOW_OBJ := $(call c2obj, $(PICTUREFLOW_SRC)) | ||
15 | |||
16 | OTHER_SRC += $(PICTUREFLOW_SRC) | ||
17 | |||
18 | ifndef SIMVER | ||
19 | ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET))))) | ||
20 | ### lowmem targets | ||
21 | ROCKS += $(PICTUREFLOW_OBJDIR)/pictureflow.ovl | ||
22 | PICTUREFLOW_OUTLDS = $(PICTUREFLOW_OBJDIR)/picutreflow.link | ||
23 | PICTUREFLOW_OVLFLAGS = -T$(PICTUREFLOW_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map | ||
24 | else | ||
25 | ### all other targets | ||
26 | ROCKS += $(PICTUREFLOW_OBJDIR)/pictureflow.rock | ||
27 | endif | ||
28 | else | ||
29 | ### simulator | ||
30 | ROCKS += $(PICTUREFLOW_OBJDIR)/pictureflow.rock | ||
31 | endif | ||
32 | |||
33 | ifeq ($(CPU),sh) | ||
34 | # sh need to retain its' -Os | ||
35 | PICTUREFLOWFLAGS = $(PLUGINFLAGS) | ||
36 | else | ||
37 | PICTUREFLOWFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -O2 | ||
38 | endif | ||
39 | |||
40 | $(PICTUREFLOW_OBJDIR)/pictureflow.rock: $(PICTUREFLOW_OBJ) | ||
41 | |||
42 | $(PICTUREFLOW_OBJDIR)/pictureflow.refmap: $(PICTUREFLOW_OBJ) | ||
43 | |||
44 | $(PICTUREFLOW_OUTLDS): $(PLUGIN_LDS) $(PICTUREFLOW_OBJDIR)/pictureflow.refmap | ||
45 | $(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DOVERLAY_OFFSET=$(shell \ | ||
46 | $(TOOLSDIR)/ovl_offset.pl $(PICTUREFLOW_OBJDIR)/pictureflow.refmap)) | ||
47 | |||
48 | $(PICTUREFLOW_OBJDIR)/pictureflow.ovl: $(PICTUREFLOW_OBJ) $(PICTUREFLOW_OUTLDS) | ||
49 | $(SILENT)$(CC) $(PLUGINFLAGS) -o $(basename $@).elf \ | ||
50 | $(filter %.o, $^) \ | ||
51 | $(filter %.a, $+) \ | ||
52 | -lgcc $(PICTUREFLOW_OVLFLAGS) | ||
53 | $(call PRINTS,LD $(@F))$(OC) -O binary $(basename $@).elf $@ | ||
54 | |||
55 | # special pattern rule for compiling pictureflow with extra flags | ||
56 | $(PICTUREFLOW_OBJDIR)/%.o: $(PICTUREFLOW_SRCDIR)/%.c $(PLUGINBITMAPLIB) $(PICTUREFLOW_SRCDIR)/pictureflow.make | ||
57 | $(SILENT)mkdir -p $(dir $@) | ||
58 | $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PICTUREFLOWFLAGS) -c $< -o $@ | ||