diff options
Diffstat (limited to 'apps/plugins/imageviewer/imageviewer.c')
-rw-r--r-- | apps/plugins/imageviewer/imageviewer.c | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c new file mode 100644 index 0000000000..1b39c5a9e3 --- /dev/null +++ b/apps/plugins/imageviewer/imageviewer.c | |||
@@ -0,0 +1,944 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * user intereface of image viewers (jpeg, png, etc.) | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "plugin.h" | ||
23 | #include <lib/playback_control.h> | ||
24 | #include <lib/helper.h> | ||
25 | #include <lib/configfile.h> | ||
26 | #include "imageviewer.h" | ||
27 | |||
28 | PLUGIN_HEADER | ||
29 | |||
30 | #ifdef USEGSLIB | ||
31 | GREY_INFO_STRUCT | ||
32 | #endif | ||
33 | |||
34 | /* Headings */ | ||
35 | #define DIR_PREV 1 | ||
36 | #define DIR_NEXT -1 | ||
37 | #define DIR_NONE 0 | ||
38 | |||
39 | /******************************* Globals ***********************************/ | ||
40 | |||
41 | bool slideshow_enabled = false; /* run slideshow */ | ||
42 | bool running_slideshow = false; /* loading image because of slideshw */ | ||
43 | #ifdef DISK_SPINDOWN | ||
44 | bool immediate_ata_off = false; /* power down disk after loading */ | ||
45 | #endif | ||
46 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
47 | /* are we using the plugin buffer or the audio buffer? */ | ||
48 | bool plug_buf = true; | ||
49 | #endif | ||
50 | |||
51 | /* Persistent configuration */ | ||
52 | #define IMGVIEW_CONFIGFILE "imageviewer.cfg" | ||
53 | #define IMGVIEW_SETTINGS_MINVERSION 1 | ||
54 | #define IMGVIEW_SETTINGS_VERSION 2 | ||
55 | |||
56 | /* Slideshow times */ | ||
57 | #define SS_MIN_TIMEOUT 1 | ||
58 | #define SS_MAX_TIMEOUT 20 | ||
59 | #define SS_DEFAULT_TIMEOUT 5 | ||
60 | |||
61 | #ifdef HAVE_LCD_COLOR | ||
62 | /* needed for value of settings */ | ||
63 | #include "jpeg/yuv2rgb.h" | ||
64 | #endif | ||
65 | |||
66 | /* jpeg use this */ | ||
67 | struct imgview_settings settings = | ||
68 | { | ||
69 | #ifdef HAVE_LCD_COLOR | ||
70 | COLOURMODE_COLOUR, | ||
71 | DITHER_NONE, | ||
72 | #endif | ||
73 | SS_DEFAULT_TIMEOUT | ||
74 | }; | ||
75 | static struct imgview_settings old_settings; | ||
76 | |||
77 | static struct configdata config[] = | ||
78 | { | ||
79 | #ifdef HAVE_LCD_COLOR | ||
80 | { TYPE_ENUM, 0, COLOUR_NUM_MODES, { .int_p = &settings.jpeg_colour_mode }, | ||
81 | "Colour Mode", (char *[]){ "Colour", "Grayscale" } }, | ||
82 | { TYPE_ENUM, 0, DITHER_NUM_MODES, { .int_p = &settings.jpeg_dither_mode }, | ||
83 | "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" } }, | ||
84 | #endif | ||
85 | { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, | ||
86 | { .int_p = &settings.ss_timeout }, "Slideshow Time", NULL }, | ||
87 | }; | ||
88 | |||
89 | #if LCD_DEPTH > 1 | ||
90 | static fb_data* old_backdrop; | ||
91 | #endif | ||
92 | |||
93 | /**************** begin Application ********************/ | ||
94 | |||
95 | |||
96 | /************************* Globals ***************************/ | ||
97 | |||
98 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
99 | static fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when | ||
100 | DITHER_DIFFUSION is set */ | ||
101 | #endif | ||
102 | |||
103 | /* my memory pool (from the mp3 buffer) */ | ||
104 | static char print[32]; /* use a common snprintf() buffer */ | ||
105 | /* the remaining free part of the buffer for loaded+resized images */ | ||
106 | static unsigned char* buf; | ||
107 | static ssize_t buf_size; | ||
108 | |||
109 | static int ds, ds_min, ds_max; /* downscaling and limits */ | ||
110 | static struct image_info image_info; | ||
111 | |||
112 | static struct tree_context *tree; | ||
113 | |||
114 | /* the current full file name */ | ||
115 | static char np_file[MAX_PATH]; | ||
116 | static int curfile = 0, direction = DIR_NONE, entries = 0; | ||
117 | |||
118 | /* list of the supported image files */ | ||
119 | static char **file_pt; | ||
120 | |||
121 | /************************* Implementation ***************************/ | ||
122 | |||
123 | /*Read directory contents for scrolling. */ | ||
124 | static void get_pic_list(void) | ||
125 | { | ||
126 | int i; | ||
127 | struct entry *dircache; | ||
128 | char *pname; | ||
129 | tree = rb->tree_get_context(); | ||
130 | dircache = tree->dircache; | ||
131 | |||
132 | file_pt = (char **) buf; | ||
133 | |||
134 | /* Remove path and leave only the name.*/ | ||
135 | pname = rb->strrchr(np_file,'/'); | ||
136 | pname++; | ||
137 | |||
138 | for (i = 0; i < tree->filesindir; i++) | ||
139 | { | ||
140 | if (!(dircache[i].attr & ATTR_DIRECTORY) | ||
141 | && img_ext(rb->strrchr(dircache[i].name,'.'))) | ||
142 | { | ||
143 | file_pt[entries] = dircache[i].name; | ||
144 | /* Set Selected File. */ | ||
145 | if (!rb->strcmp(file_pt[entries], pname)) | ||
146 | curfile = entries; | ||
147 | entries++; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | buf += (entries * sizeof(char**)); | ||
152 | buf_size -= (entries * sizeof(char**)); | ||
153 | } | ||
154 | |||
155 | static int change_filename(int direct) | ||
156 | { | ||
157 | bool file_erased = (file_pt[curfile] == NULL); | ||
158 | direction = direct; | ||
159 | |||
160 | curfile += (direct == DIR_PREV? entries - 1: 1); | ||
161 | if (curfile >= entries) | ||
162 | curfile -= entries; | ||
163 | |||
164 | if (file_erased) | ||
165 | { | ||
166 | /* remove 'erased' file names from list. */ | ||
167 | int count, i; | ||
168 | for (count = i = 0; i < entries; i++) | ||
169 | { | ||
170 | if (curfile == i) | ||
171 | curfile = count; | ||
172 | if (file_pt[i] != NULL) | ||
173 | file_pt[count++] = file_pt[i]; | ||
174 | } | ||
175 | entries = count; | ||
176 | } | ||
177 | |||
178 | if (entries == 0) | ||
179 | { | ||
180 | rb->splash(HZ, "No supported files"); | ||
181 | return PLUGIN_ERROR; | ||
182 | } | ||
183 | |||
184 | if (rb->strlen(tree->currdir) > 1) | ||
185 | { | ||
186 | rb->strcpy(np_file, tree->currdir); | ||
187 | rb->strcat(np_file, "/"); | ||
188 | } | ||
189 | else | ||
190 | rb->strcpy(np_file, tree->currdir); | ||
191 | |||
192 | rb->strcat(np_file, file_pt[curfile]); | ||
193 | |||
194 | return PLUGIN_OTHER; | ||
195 | } | ||
196 | |||
197 | /* switch off overlay, for handling SYS_ events */ | ||
198 | static void cleanup(void *parameter) | ||
199 | { | ||
200 | (void)parameter; | ||
201 | #ifdef USEGSLIB | ||
202 | grey_show(false); | ||
203 | #endif | ||
204 | } | ||
205 | |||
206 | #define VSCROLL (LCD_HEIGHT/8) | ||
207 | #define HSCROLL (LCD_WIDTH/10) | ||
208 | |||
209 | #define ZOOM_IN 100 /* return codes for below function */ | ||
210 | #define ZOOM_OUT 101 | ||
211 | |||
212 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
213 | static bool set_option_grayscale(void) | ||
214 | { | ||
215 | bool gray = settings.jpeg_colour_mode == COLOURMODE_GRAY; | ||
216 | rb->set_bool("Grayscale", &gray); | ||
217 | settings.jpeg_colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR; | ||
218 | return false; | ||
219 | } | ||
220 | |||
221 | static bool set_option_dithering(void) | ||
222 | { | ||
223 | static const struct opt_items dithering[DITHER_NUM_MODES] = { | ||
224 | [DITHER_NONE] = { "Off", -1 }, | ||
225 | [DITHER_ORDERED] = { "Ordered", -1 }, | ||
226 | [DITHER_DIFFUSION] = { "Diffusion", -1 }, | ||
227 | }; | ||
228 | |||
229 | rb->set_option("Dithering", &settings.jpeg_dither_mode, INT, | ||
230 | dithering, DITHER_NUM_MODES, NULL); | ||
231 | return false; | ||
232 | } | ||
233 | |||
234 | MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale", | ||
235 | set_option_grayscale, NULL, NULL, Icon_NOICON); | ||
236 | MENUITEM_FUNCTION(dithering_item, 0, "Dithering", | ||
237 | set_option_dithering, NULL, NULL, Icon_NOICON); | ||
238 | MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON, | ||
239 | &grayscale_item, &dithering_item); | ||
240 | |||
241 | static void display_options(void) | ||
242 | { | ||
243 | rb->do_menu(&display_menu, NULL, NULL, false); | ||
244 | } | ||
245 | #endif /* defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) */ | ||
246 | |||
247 | static int show_menu(void) /* return 1 to quit */ | ||
248 | { | ||
249 | #if LCD_DEPTH > 1 | ||
250 | rb->lcd_set_backdrop(old_backdrop); | ||
251 | #ifdef HAVE_LCD_COLOR | ||
252 | rb->lcd_set_foreground(rb->global_settings->fg_color); | ||
253 | rb->lcd_set_background(rb->global_settings->bg_color); | ||
254 | #else | ||
255 | rb->lcd_set_foreground(LCD_BLACK); | ||
256 | rb->lcd_set_background(LCD_WHITE); | ||
257 | #endif | ||
258 | #endif | ||
259 | int result; | ||
260 | |||
261 | enum menu_id | ||
262 | { | ||
263 | MIID_RETURN = 0, | ||
264 | MIID_TOGGLE_SS_MODE, | ||
265 | MIID_CHANGE_SS_MODE, | ||
266 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
267 | MIID_SHOW_PLAYBACK_MENU, | ||
268 | #endif | ||
269 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
270 | MIID_DISPLAY_OPTIONS, | ||
271 | #endif | ||
272 | MIID_QUIT, | ||
273 | }; | ||
274 | |||
275 | MENUITEM_STRINGLIST(menu, MENU_TITLE, NULL, | ||
276 | "Return", "Toggle Slideshow Mode", | ||
277 | "Change Slideshow Time", | ||
278 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
279 | "Show Playback Menu", | ||
280 | #endif | ||
281 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
282 | "Display Options", | ||
283 | #endif | ||
284 | "Quit"); | ||
285 | |||
286 | static const struct opt_items slideshow[2] = { | ||
287 | { "Disable", -1 }, | ||
288 | { "Enable", -1 }, | ||
289 | }; | ||
290 | |||
291 | result=rb->do_menu(&menu, NULL, NULL, false); | ||
292 | |||
293 | switch (result) | ||
294 | { | ||
295 | case MIID_RETURN: | ||
296 | break; | ||
297 | case MIID_TOGGLE_SS_MODE: | ||
298 | rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, | ||
299 | slideshow , 2, NULL); | ||
300 | break; | ||
301 | case MIID_CHANGE_SS_MODE: | ||
302 | rb->set_int("Slideshow Time", "s", UNIT_SEC, | ||
303 | &settings.ss_timeout, NULL, 1, | ||
304 | SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL); | ||
305 | break; | ||
306 | |||
307 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
308 | case MIID_SHOW_PLAYBACK_MENU: | ||
309 | if (plug_buf) | ||
310 | { | ||
311 | playback_control(NULL); | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | rb->splash(HZ, "Cannot restart playback"); | ||
316 | } | ||
317 | break; | ||
318 | #endif | ||
319 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
320 | case MIID_DISPLAY_OPTIONS: | ||
321 | display_options(); | ||
322 | break; | ||
323 | #endif | ||
324 | case MIID_QUIT: | ||
325 | return 1; | ||
326 | break; | ||
327 | } | ||
328 | |||
329 | #ifdef DISK_SPINDOWN | ||
330 | /* change ata spindown time based on slideshow time setting */ | ||
331 | immediate_ata_off = false; | ||
332 | rb->storage_spindown(rb->global_settings->disk_spindown); | ||
333 | |||
334 | if (slideshow_enabled) | ||
335 | { | ||
336 | if(settings.ss_timeout < 10) | ||
337 | { | ||
338 | /* slideshow times < 10s keep disk spinning */ | ||
339 | rb->storage_spindown(0); | ||
340 | } | ||
341 | else if (!rb->mp3_is_playing()) | ||
342 | { | ||
343 | /* slideshow times > 10s and not playing: ata_off after load */ | ||
344 | immediate_ata_off = true; | ||
345 | } | ||
346 | } | ||
347 | #endif | ||
348 | #if LCD_DEPTH > 1 | ||
349 | rb->lcd_set_backdrop(NULL); | ||
350 | rb->lcd_set_foreground(LCD_WHITE); | ||
351 | rb->lcd_set_background(LCD_BLACK); | ||
352 | #endif | ||
353 | rb->lcd_clear_display(); | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /* Pan the viewing window right - move image to the left and fill in | ||
358 | the right-hand side */ | ||
359 | static void pan_view_right(struct image_info *info) | ||
360 | { | ||
361 | int move; | ||
362 | |||
363 | move = MIN(HSCROLL, info->width - info->x - LCD_WIDTH); | ||
364 | if (move > 0) | ||
365 | { | ||
366 | MYXLCD(scroll_left)(move); /* scroll left */ | ||
367 | info->x += move; | ||
368 | draw_image_rect(info, LCD_WIDTH - move, 0, move, info->height-info->y); | ||
369 | MYLCD_UPDATE(); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /* Pan the viewing window left - move image to the right and fill in | ||
374 | the left-hand side */ | ||
375 | static void pan_view_left(struct image_info *info) | ||
376 | { | ||
377 | int move; | ||
378 | |||
379 | move = MIN(HSCROLL, info->x); | ||
380 | if (move > 0) | ||
381 | { | ||
382 | MYXLCD(scroll_right)(move); /* scroll right */ | ||
383 | info->x -= move; | ||
384 | draw_image_rect(info, 0, 0, move, info->height-info->y); | ||
385 | MYLCD_UPDATE(); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | /* Pan the viewing window up - move image down and fill in | ||
390 | the top */ | ||
391 | static void pan_view_up(struct image_info *info) | ||
392 | { | ||
393 | int move; | ||
394 | |||
395 | move = MIN(VSCROLL, info->y); | ||
396 | if (move > 0) | ||
397 | { | ||
398 | MYXLCD(scroll_down)(move); /* scroll down */ | ||
399 | info->y -= move; | ||
400 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
401 | if (settings.jpeg_dither_mode == DITHER_DIFFUSION) | ||
402 | { | ||
403 | /* Draw over the band at the top of the last update | ||
404 | caused by lack of error history on line zero. */ | ||
405 | move = MIN(move + 1, info->y + info->height); | ||
406 | } | ||
407 | #endif | ||
408 | draw_image_rect(info, 0, 0, info->width-info->x, move); | ||
409 | MYLCD_UPDATE(); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | /* Pan the viewing window down - move image up and fill in | ||
414 | the bottom */ | ||
415 | static void pan_view_down(struct image_info *info) | ||
416 | { | ||
417 | int move; | ||
418 | |||
419 | move = MIN(VSCROLL, info->height - info->y - LCD_HEIGHT); | ||
420 | if (move > 0) | ||
421 | { | ||
422 | MYXLCD(scroll_up)(move); /* scroll up */ | ||
423 | info->y += move; | ||
424 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
425 | if (settings.jpeg_dither_mode == DITHER_DIFFUSION) | ||
426 | { | ||
427 | /* Save the line that was on the last line of the display | ||
428 | and draw one extra line above then recover the line with | ||
429 | image data that had an error history when it was drawn. | ||
430 | */ | ||
431 | move++, info->y--; | ||
432 | rb->memcpy(rgb_linebuf, | ||
433 | rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, | ||
434 | LCD_WIDTH*sizeof (fb_data)); | ||
435 | } | ||
436 | #endif | ||
437 | |||
438 | draw_image_rect(info, 0, LCD_HEIGHT - move, info->width-info->x, move); | ||
439 | |||
440 | #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) | ||
441 | if (settings.jpeg_dither_mode == DITHER_DIFFUSION) | ||
442 | { | ||
443 | /* Cover the first row drawn with previous image data. */ | ||
444 | rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, | ||
445 | rgb_linebuf, LCD_WIDTH*sizeof (fb_data)); | ||
446 | info->y++; | ||
447 | } | ||
448 | #endif | ||
449 | MYLCD_UPDATE(); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /* interactively scroll around the image */ | ||
454 | static int scroll_bmp(struct image_info *info) | ||
455 | { | ||
456 | int button; | ||
457 | int lastbutton = 0; | ||
458 | |||
459 | while (true) | ||
460 | { | ||
461 | if (slideshow_enabled) | ||
462 | button = rb->button_get_w_tmo(settings.ss_timeout * HZ); | ||
463 | else | ||
464 | button = rb->button_get(true); | ||
465 | |||
466 | running_slideshow = false; | ||
467 | |||
468 | switch(button) | ||
469 | { | ||
470 | case IMGVIEW_LEFT: | ||
471 | if (entries > 1 && info->width <= LCD_WIDTH | ||
472 | && info->height <= LCD_HEIGHT) | ||
473 | return change_filename(DIR_PREV); | ||
474 | case IMGVIEW_LEFT | BUTTON_REPEAT: | ||
475 | pan_view_left(info); | ||
476 | break; | ||
477 | |||
478 | case IMGVIEW_RIGHT: | ||
479 | if (entries > 1 && info->width <= LCD_WIDTH | ||
480 | && info->height <= LCD_HEIGHT) | ||
481 | return change_filename(DIR_NEXT); | ||
482 | case IMGVIEW_RIGHT | BUTTON_REPEAT: | ||
483 | pan_view_right(info); | ||
484 | break; | ||
485 | |||
486 | case IMGVIEW_UP: | ||
487 | case IMGVIEW_UP | BUTTON_REPEAT: | ||
488 | pan_view_up(info); | ||
489 | break; | ||
490 | |||
491 | case IMGVIEW_DOWN: | ||
492 | case IMGVIEW_DOWN | BUTTON_REPEAT: | ||
493 | pan_view_down(info); | ||
494 | break; | ||
495 | |||
496 | case BUTTON_NONE: | ||
497 | if (!slideshow_enabled) | ||
498 | break; | ||
499 | running_slideshow = true; | ||
500 | if (entries > 1) | ||
501 | return change_filename(DIR_NEXT); | ||
502 | break; | ||
503 | |||
504 | #ifdef IMGVIEW_SLIDE_SHOW | ||
505 | case IMGVIEW_SLIDE_SHOW: | ||
506 | slideshow_enabled = !slideshow_enabled; | ||
507 | running_slideshow = slideshow_enabled; | ||
508 | break; | ||
509 | #endif | ||
510 | |||
511 | #ifdef IMGVIEW_NEXT_REPEAT | ||
512 | case IMGVIEW_NEXT_REPEAT: | ||
513 | #endif | ||
514 | case IMGVIEW_NEXT: | ||
515 | if (entries > 1) | ||
516 | return change_filename(DIR_NEXT); | ||
517 | break; | ||
518 | |||
519 | #ifdef IMGVIEW_PREVIOUS_REPEAT | ||
520 | case IMGVIEW_PREVIOUS_REPEAT: | ||
521 | #endif | ||
522 | case IMGVIEW_PREVIOUS: | ||
523 | if (entries > 1) | ||
524 | return change_filename(DIR_PREV); | ||
525 | break; | ||
526 | |||
527 | case IMGVIEW_ZOOM_IN: | ||
528 | #ifdef IMGVIEW_ZOOM_PRE | ||
529 | if (lastbutton != IMGVIEW_ZOOM_PRE) | ||
530 | break; | ||
531 | #endif | ||
532 | return ZOOM_IN; | ||
533 | break; | ||
534 | |||
535 | case IMGVIEW_ZOOM_OUT: | ||
536 | #ifdef IMGVIEW_ZOOM_PRE | ||
537 | if (lastbutton != IMGVIEW_ZOOM_PRE) | ||
538 | break; | ||
539 | #endif | ||
540 | return ZOOM_OUT; | ||
541 | break; | ||
542 | #ifdef IMGVIEW_RC_MENU | ||
543 | case IMGVIEW_RC_MENU: | ||
544 | #endif | ||
545 | case IMGVIEW_MENU: | ||
546 | #ifdef USEGSLIB | ||
547 | grey_show(false); /* switch off greyscale overlay */ | ||
548 | #endif | ||
549 | if (show_menu() == 1) | ||
550 | return PLUGIN_OK; | ||
551 | |||
552 | #ifdef USEGSLIB | ||
553 | grey_show(true); /* switch on greyscale overlay */ | ||
554 | #else | ||
555 | draw_image_rect(info, 0, 0, | ||
556 | info->width-info->x, info->height-info->y); | ||
557 | MYLCD_UPDATE(); | ||
558 | #endif | ||
559 | break; | ||
560 | default: | ||
561 | if (rb->default_event_handler_ex(button, cleanup, NULL) | ||
562 | == SYS_USB_CONNECTED) | ||
563 | return PLUGIN_USB_CONNECTED; | ||
564 | break; | ||
565 | |||
566 | } /* switch */ | ||
567 | |||
568 | if (button != BUTTON_NONE) | ||
569 | lastbutton = button; | ||
570 | } /* while (true) */ | ||
571 | } | ||
572 | |||
573 | /********************* main function *************************/ | ||
574 | |||
575 | /* callback updating a progress meter while image decoding */ | ||
576 | void cb_progress(int current, int total) | ||
577 | { | ||
578 | rb->yield(); /* be nice to the other threads */ | ||
579 | if(!running_slideshow) | ||
580 | { | ||
581 | rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], | ||
582 | 0, LCD_HEIGHT-8, LCD_WIDTH, 8, | ||
583 | total, 0, current, HORIZONTAL); | ||
584 | rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); | ||
585 | } | ||
586 | #ifndef USEGSLIB | ||
587 | else | ||
588 | { | ||
589 | /* in slideshow mode, keep gui interference to a minimum */ | ||
590 | rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], | ||
591 | 0, LCD_HEIGHT-4, LCD_WIDTH, 4, | ||
592 | total, 0, current, HORIZONTAL); | ||
593 | rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4); | ||
594 | } | ||
595 | #endif | ||
596 | } | ||
597 | |||
598 | /* how far can we zoom in without running out of memory */ | ||
599 | static int min_downscale(int bufsize) | ||
600 | { | ||
601 | int downscale = 8; | ||
602 | |||
603 | if (img_mem(8) > bufsize) | ||
604 | return 0; /* error, too large, even 1:8 doesn't fit */ | ||
605 | |||
606 | while (downscale > 1 && img_mem(downscale/2) <= bufsize) | ||
607 | downscale /= 2; | ||
608 | |||
609 | return downscale; | ||
610 | } | ||
611 | |||
612 | /* how far can we zoom out, to fit image into the LCD */ | ||
613 | static int max_downscale(struct image_info *info) | ||
614 | { | ||
615 | int downscale = 1; | ||
616 | |||
617 | while (downscale < 8 && (info->x_size/downscale > LCD_WIDTH | ||
618 | || info->y_size/downscale > LCD_HEIGHT)) | ||
619 | { | ||
620 | downscale *= 2; | ||
621 | } | ||
622 | |||
623 | return downscale; | ||
624 | } | ||
625 | |||
626 | /* set the view to the given center point, limit if necessary */ | ||
627 | static void set_view(struct image_info *info, int cx, int cy) | ||
628 | { | ||
629 | int x, y; | ||
630 | |||
631 | /* plain center to available width/height */ | ||
632 | x = cx - MIN(LCD_WIDTH, info->width) / 2; | ||
633 | y = cy - MIN(LCD_HEIGHT, info->height) / 2; | ||
634 | |||
635 | /* limit against upper image size */ | ||
636 | x = MIN(info->width - LCD_WIDTH, x); | ||
637 | y = MIN(info->height - LCD_HEIGHT, y); | ||
638 | |||
639 | /* limit against negative side */ | ||
640 | x = MAX(0, x); | ||
641 | y = MAX(0, y); | ||
642 | |||
643 | info->x = x; /* set the values */ | ||
644 | info->y = y; | ||
645 | } | ||
646 | |||
647 | /* calculate the view center based on the bitmap position */ | ||
648 | static void get_view(struct image_info *info, int *p_cx, int *p_cy) | ||
649 | { | ||
650 | *p_cx = info->x + MIN(LCD_WIDTH, info->width) / 2; | ||
651 | *p_cy = info->y + MIN(LCD_HEIGHT, info->height) / 2; | ||
652 | } | ||
653 | |||
654 | /* load, decode, display the image */ | ||
655 | static int load_and_show(char* filename, struct image_info *info) | ||
656 | { | ||
657 | int status; | ||
658 | int cx, cy; | ||
659 | ssize_t remaining; | ||
660 | |||
661 | #if LCD_DEPTH > 1 | ||
662 | rb->lcd_set_foreground(LCD_WHITE); | ||
663 | rb->lcd_set_background(LCD_BLACK); | ||
664 | rb->lcd_set_backdrop(NULL); | ||
665 | #endif | ||
666 | rb->lcd_clear_display(); | ||
667 | |||
668 | rb->memset(info, 0, sizeof(*info)); | ||
669 | remaining = buf_size; | ||
670 | |||
671 | if (rb->button_get(false) == IMGVIEW_MENU) | ||
672 | status = PLUGIN_ABORT; | ||
673 | else | ||
674 | status = load_image(filename, info, buf, &remaining); | ||
675 | |||
676 | if (status == PLUGIN_OUTOFMEM) | ||
677 | { | ||
678 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
679 | if(plug_buf) | ||
680 | { | ||
681 | rb->lcd_setfont(FONT_SYSFIXED); | ||
682 | rb->lcd_clear_display(); | ||
683 | rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1); | ||
684 | rb->lcd_puts(0,0,print); | ||
685 | rb->lcd_puts(0,1,"Not enough plugin memory!"); | ||
686 | rb->lcd_puts(0,2,"Zoom In: Stop playback."); | ||
687 | if(entries>1) | ||
688 | rb->lcd_puts(0,3,"Left/Right: Skip File."); | ||
689 | rb->lcd_puts(0,4,"Show Menu: Quit."); | ||
690 | rb->lcd_update(); | ||
691 | rb->lcd_setfont(FONT_UI); | ||
692 | |||
693 | rb->button_clear_queue(); | ||
694 | |||
695 | while (1) | ||
696 | { | ||
697 | int button = rb->button_get(true); | ||
698 | switch(button) | ||
699 | { | ||
700 | case IMGVIEW_ZOOM_IN: | ||
701 | plug_buf = false; | ||
702 | buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); | ||
703 | /*try again this file, now using the audio buffer */ | ||
704 | return PLUGIN_OTHER; | ||
705 | #ifdef IMGVIEW_RC_MENU | ||
706 | case IMGVIEW_RC_MENU: | ||
707 | #endif | ||
708 | case IMGVIEW_MENU: | ||
709 | return PLUGIN_OK; | ||
710 | |||
711 | case IMGVIEW_LEFT: | ||
712 | if(entries>1) | ||
713 | { | ||
714 | rb->lcd_clear_display(); | ||
715 | return change_filename(DIR_PREV); | ||
716 | } | ||
717 | break; | ||
718 | |||
719 | case IMGVIEW_RIGHT: | ||
720 | if(entries>1) | ||
721 | { | ||
722 | rb->lcd_clear_display(); | ||
723 | return change_filename(DIR_NEXT); | ||
724 | } | ||
725 | break; | ||
726 | default: | ||
727 | if(rb->default_event_handler_ex(button, cleanup, NULL) | ||
728 | == SYS_USB_CONNECTED) | ||
729 | return PLUGIN_USB_CONNECTED; | ||
730 | |||
731 | } | ||
732 | } | ||
733 | } | ||
734 | else | ||
735 | #endif | ||
736 | { | ||
737 | rb->splash(HZ, "Out of Memory"); | ||
738 | file_pt[curfile] = NULL; | ||
739 | return change_filename(direction); | ||
740 | } | ||
741 | } | ||
742 | else if (status == PLUGIN_ERROR) | ||
743 | { | ||
744 | file_pt[curfile] = NULL; | ||
745 | return change_filename(direction); | ||
746 | } | ||
747 | else if (status == PLUGIN_ABORT) { | ||
748 | rb->splash(HZ, "aborted"); | ||
749 | return PLUGIN_OK; | ||
750 | } | ||
751 | |||
752 | ds_max = max_downscale(info); /* check display constraint */ | ||
753 | ds_min = min_downscale(remaining); /* check memory constraint */ | ||
754 | if (ds_min == 0) | ||
755 | { | ||
756 | #if UNSCALED_IS_AVAILABLE | ||
757 | /* Can not resize the image but original one is available, so use it. */ | ||
758 | ds_min = ds_max = 1; | ||
759 | #else | ||
760 | /* not enough memory to decode image. */ | ||
761 | rb->splash(HZ, "too large"); | ||
762 | file_pt[curfile] = NULL; | ||
763 | return change_filename(direction); | ||
764 | #endif | ||
765 | } | ||
766 | else if (ds_max < ds_min) | ||
767 | ds_max = ds_min; | ||
768 | |||
769 | ds = ds_max; /* initialize setting */ | ||
770 | cx = info->x_size/ds/2; /* center the view */ | ||
771 | cy = info->y_size/ds/2; | ||
772 | |||
773 | do /* loop the image prepare and decoding when zoomed */ | ||
774 | { | ||
775 | status = get_image(info, ds); /* decode or fetch from cache */ | ||
776 | if (status == PLUGIN_ERROR) | ||
777 | { | ||
778 | file_pt[curfile] = NULL; | ||
779 | return change_filename(direction); | ||
780 | } | ||
781 | |||
782 | set_view(info, cx, cy); | ||
783 | |||
784 | if(!running_slideshow) | ||
785 | { | ||
786 | rb->snprintf(print, sizeof(print), "showing %dx%d", | ||
787 | info->width, info->height); | ||
788 | rb->lcd_puts(0, 3, print); | ||
789 | rb->lcd_update(); | ||
790 | } | ||
791 | |||
792 | MYLCD(clear_display)(); | ||
793 | draw_image_rect(info, 0, 0, | ||
794 | info->width-info->x, info->height-info->y); | ||
795 | MYLCD_UPDATE(); | ||
796 | |||
797 | #ifdef USEGSLIB | ||
798 | grey_show(true); /* switch on greyscale overlay */ | ||
799 | #endif | ||
800 | |||
801 | /* drawing is now finished, play around with scrolling | ||
802 | * until you press OFF or connect USB | ||
803 | */ | ||
804 | while (1) | ||
805 | { | ||
806 | status = scroll_bmp(info); | ||
807 | if (status == ZOOM_IN) | ||
808 | { | ||
809 | #if UNSCALED_IS_AVAILABLE | ||
810 | if (ds > 1) | ||
811 | #else | ||
812 | if (ds > ds_min) | ||
813 | #endif | ||
814 | { | ||
815 | #if UNSCALED_IS_AVAILABLE | ||
816 | /* if 1/1 is always available, jump ds from ds_min to 1. */ | ||
817 | int zoom = (ds == ds_min)? ds_min: 2; | ||
818 | #else | ||
819 | const int zoom = 2; | ||
820 | #endif | ||
821 | ds /= zoom; /* reduce downscaling to zoom in */ | ||
822 | get_view(info, &cx, &cy); | ||
823 | cx *= zoom; /* prepare the position in the new image */ | ||
824 | cy *= zoom; | ||
825 | } | ||
826 | else | ||
827 | continue; | ||
828 | } | ||
829 | |||
830 | if (status == ZOOM_OUT) | ||
831 | { | ||
832 | if (ds < ds_max) | ||
833 | { | ||
834 | #if UNSCALED_IS_AVAILABLE | ||
835 | /* if ds is 1 and ds_min is > 1, jump ds to ds_min. */ | ||
836 | int zoom = (ds < ds_min)? ds_min: 2; | ||
837 | #else | ||
838 | const int zoom = 2; | ||
839 | #endif | ||
840 | ds *= zoom; /* increase downscaling to zoom out */ | ||
841 | get_view(info, &cx, &cy); | ||
842 | cx /= zoom; /* prepare the position in the new image */ | ||
843 | cy /= zoom; | ||
844 | } | ||
845 | else | ||
846 | continue; | ||
847 | } | ||
848 | break; | ||
849 | } | ||
850 | |||
851 | #ifdef USEGSLIB | ||
852 | grey_show(false); /* switch off overlay */ | ||
853 | #endif | ||
854 | rb->lcd_clear_display(); | ||
855 | } | ||
856 | while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED | ||
857 | && status != PLUGIN_OTHER); | ||
858 | #ifdef USEGSLIB | ||
859 | rb->lcd_update(); | ||
860 | #endif | ||
861 | return status; | ||
862 | } | ||
863 | |||
864 | /******************** Plugin entry point *********************/ | ||
865 | |||
866 | enum plugin_status plugin_start(const void* parameter) | ||
867 | { | ||
868 | int condition; | ||
869 | #ifdef USEGSLIB | ||
870 | long greysize; /* helper */ | ||
871 | #endif | ||
872 | #if LCD_DEPTH > 1 | ||
873 | old_backdrop = rb->lcd_get_backdrop(); | ||
874 | #endif | ||
875 | |||
876 | if(!parameter) return PLUGIN_ERROR; | ||
877 | |||
878 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
879 | buf = rb->plugin_get_buffer((size_t *)&buf_size); | ||
880 | #else | ||
881 | buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); | ||
882 | #endif | ||
883 | |||
884 | rb->strcpy(np_file, parameter); | ||
885 | get_pic_list(); | ||
886 | |||
887 | if(!entries) return PLUGIN_ERROR; | ||
888 | |||
889 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
890 | if(!rb->audio_status()) | ||
891 | { | ||
892 | plug_buf = false; | ||
893 | buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); | ||
894 | } | ||
895 | #endif | ||
896 | |||
897 | #ifdef USEGSLIB | ||
898 | if (!grey_init(buf, buf_size, GREY_ON_COP, | ||
899 | LCD_WIDTH, LCD_HEIGHT, &greysize)) | ||
900 | { | ||
901 | rb->splash(HZ, "grey buf error"); | ||
902 | return PLUGIN_ERROR; | ||
903 | } | ||
904 | buf += greysize; | ||
905 | buf_size -= greysize; | ||
906 | #endif | ||
907 | |||
908 | /* should be ok to just load settings since the plugin itself has | ||
909 | just been loaded from disk and the drive should be spinning */ | ||
910 | configfile_load(IMGVIEW_CONFIGFILE, config, | ||
911 | ARRAYLEN(config), IMGVIEW_SETTINGS_MINVERSION); | ||
912 | rb->memcpy(&old_settings, &settings, sizeof (settings)); | ||
913 | |||
914 | /* Turn off backlight timeout */ | ||
915 | backlight_force_on(); /* backlight control in lib/helper.c */ | ||
916 | |||
917 | do | ||
918 | { | ||
919 | condition = load_and_show(np_file, &image_info); | ||
920 | } while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED | ||
921 | && condition != PLUGIN_ERROR); | ||
922 | |||
923 | if (rb->memcmp(&settings, &old_settings, sizeof (settings))) | ||
924 | { | ||
925 | /* Just in case drive has to spin, keep it from looking locked */ | ||
926 | rb->splash(0, "Saving Settings"); | ||
927 | configfile_save(IMGVIEW_CONFIGFILE, config, | ||
928 | ARRAYLEN(config), IMGVIEW_SETTINGS_VERSION); | ||
929 | } | ||
930 | |||
931 | #ifdef DISK_SPINDOWN | ||
932 | /* set back ata spindown time in case we changed it */ | ||
933 | rb->storage_spindown(rb->global_settings->disk_spindown); | ||
934 | #endif | ||
935 | |||
936 | /* Turn on backlight timeout (revert to settings) */ | ||
937 | backlight_use_settings(); /* backlight control in lib/helper.c */ | ||
938 | |||
939 | #ifdef USEGSLIB | ||
940 | grey_release(); /* deinitialize */ | ||
941 | #endif | ||
942 | |||
943 | return condition; | ||
944 | } | ||