summaryrefslogtreecommitdiff
path: root/apps/plugins/imageviewer/imageviewer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/imageviewer/imageviewer.c')
-rw-r--r--apps/plugins/imageviewer/imageviewer.c944
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
28PLUGIN_HEADER
29
30#ifdef USEGSLIB
31GREY_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
41bool slideshow_enabled = false; /* run slideshow */
42bool running_slideshow = false; /* loading image because of slideshw */
43#ifdef DISK_SPINDOWN
44bool 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? */
48bool 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 */
67struct imgview_settings settings =
68{
69#ifdef HAVE_LCD_COLOR
70 COLOURMODE_COLOUR,
71 DITHER_NONE,
72#endif
73 SS_DEFAULT_TIMEOUT
74};
75static struct imgview_settings old_settings;
76
77static 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
90static fb_data* old_backdrop;
91#endif
92
93/**************** begin Application ********************/
94
95
96/************************* Globals ***************************/
97
98#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
99static 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) */
104static char print[32]; /* use a common snprintf() buffer */
105/* the remaining free part of the buffer for loaded+resized images */
106static unsigned char* buf;
107static ssize_t buf_size;
108
109static int ds, ds_min, ds_max; /* downscaling and limits */
110static struct image_info image_info;
111
112static struct tree_context *tree;
113
114/* the current full file name */
115static char np_file[MAX_PATH];
116static int curfile = 0, direction = DIR_NONE, entries = 0;
117
118/* list of the supported image files */
119static char **file_pt;
120
121/************************* Implementation ***************************/
122
123/*Read directory contents for scrolling. */
124static 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
155static 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 */
198static 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)
213static 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
221static 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
234MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale",
235 set_option_grayscale, NULL, NULL, Icon_NOICON);
236MENUITEM_FUNCTION(dithering_item, 0, "Dithering",
237 set_option_dithering, NULL, NULL, Icon_NOICON);
238MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
239 &grayscale_item, &dithering_item);
240
241static 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
247static 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 */
359static 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 */
375static 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 */
391static 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 */
415static 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 */
454static 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 */
576void 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 */
599static 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 */
613static 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 */
627static 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 */
648static 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 */
655static 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
866enum 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}