Fix possible buffer overflow
[kugel-rb.git] / apps / plugins / imageviewer / imageviewer.c
blobed41719ccac77eb0bd0da94cc3b68c68dfc3c493
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * user intereface of image viewers (jpeg, png, etc.)
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
23 #include <lib/playback_control.h>
24 #include <lib/helper.h>
25 #include <lib/configfile.h>
26 #include "imageviewer.h"
28 PLUGIN_HEADER
30 #ifdef USEGSLIB
31 GREY_INFO_STRUCT
32 #endif
34 /* Headings */
35 #define DIR_PREV 1
36 #define DIR_NEXT -1
37 #define DIR_NONE 0
39 /******************************* Globals ***********************************/
41 bool slideshow_enabled = false; /* run slideshow */
42 bool running_slideshow = false; /* loading image because of slideshow */
43 #ifdef DISK_SPINDOWN
44 bool immediate_ata_off = false; /* power down disk after loading */
45 #endif
46 #ifdef USE_PLUG_BUF
47 /* are we using the plugin buffer or the audio buffer? */
48 bool plug_buf = true;
49 #endif
51 /* Persistent configuration */
52 #define IMGVIEW_CONFIGFILE "imageviewer.cfg"
53 #define IMGVIEW_SETTINGS_MINVERSION 1
54 #define IMGVIEW_SETTINGS_VERSION 2
56 /* Slideshow times */
57 #define SS_MIN_TIMEOUT 1
58 #define SS_MAX_TIMEOUT 20
59 #define SS_DEFAULT_TIMEOUT 5
61 #ifdef HAVE_LCD_COLOR
62 /* needed for value of settings */
63 #include "jpeg/yuv2rgb.h"
64 #endif
66 /* jpeg use this */
67 struct imgview_settings settings =
69 #ifdef HAVE_LCD_COLOR
70 COLOURMODE_COLOUR,
71 DITHER_NONE,
72 #endif
73 SS_DEFAULT_TIMEOUT
75 static struct imgview_settings old_settings;
77 static struct configdata config[] =
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 },
89 /**************** begin Application ********************/
92 /************************* Globals ***************************/
94 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
95 static fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when
96 DITHER_DIFFUSION is set */
97 #endif
99 /* my memory pool (from the mp3 buffer) */
100 static char print[32]; /* use a common snprintf() buffer */
101 /* the remaining free part of the buffer for loaded+resized images */
102 static unsigned char* buf;
103 static size_t buf_size;
105 static int ds, ds_min, ds_max; /* downscaling and limits */
106 static struct image_info image_info;
108 /* the current full file name */
109 static char np_file[MAX_PATH];
110 static int curfile = 0, direction = DIR_NEXT, entries = 0;
112 /* list of the supported image files */
113 static char **file_pt;
115 /************************* Implementation ***************************/
117 /* Read directory contents for scrolling. */
118 static void get_pic_list(void)
120 struct tree_context *tree = rb->tree_get_context();
121 struct entry *dircache = tree->dircache;
122 int i;
123 char *pname;
125 file_pt = (char **) buf;
127 /* Remove path and leave only the name.*/
128 pname = rb->strrchr(np_file,'/');
129 pname++;
131 for (i = 0; i < tree->filesindir && buf_size > sizeof(char**); i++)
133 if (!(dircache[i].attr & ATTR_DIRECTORY)
134 && img_ext(rb->strrchr(dircache[i].name,'.')))
136 file_pt[entries] = dircache[i].name;
137 /* Set Selected File. */
138 if (!rb->strcmp(file_pt[entries], pname))
139 curfile = entries;
140 entries++;
142 buf += (sizeof(char**));
143 buf_size -= (sizeof(char**));
148 static int change_filename(int direct)
150 bool file_erased = (file_pt[curfile] == NULL);
151 direction = direct;
153 curfile += (direct == DIR_PREV? entries - 1: 1);
154 if (curfile >= entries)
155 curfile -= entries;
157 if (file_erased)
159 /* remove 'erased' file names from list. */
160 int count, i;
161 for (count = i = 0; i < entries; i++)
163 if (curfile == i)
164 curfile = count;
165 if (file_pt[i] != NULL)
166 file_pt[count++] = file_pt[i];
168 entries = count;
171 if (entries == 0)
173 rb->splash(HZ, "No supported files");
174 return PLUGIN_ERROR;
177 rb->strcpy(rb->strrchr(np_file, '/')+1, file_pt[curfile]);
179 return PLUGIN_OTHER;
182 /* switch off overlay, for handling SYS_ events */
183 static void cleanup(void *parameter)
185 (void)parameter;
186 #ifdef USEGSLIB
187 grey_show(false);
188 #endif
191 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
192 static bool set_option_grayscale(void)
194 bool gray = settings.jpeg_colour_mode == COLOURMODE_GRAY;
195 rb->set_bool("Grayscale", &gray);
196 settings.jpeg_colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR;
197 return false;
200 static bool set_option_dithering(void)
202 static const struct opt_items dithering[DITHER_NUM_MODES] = {
203 [DITHER_NONE] = { "Off", -1 },
204 [DITHER_ORDERED] = { "Ordered", -1 },
205 [DITHER_DIFFUSION] = { "Diffusion", -1 },
208 rb->set_option("Dithering", &settings.jpeg_dither_mode, INT,
209 dithering, DITHER_NUM_MODES, NULL);
210 return false;
213 MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale",
214 set_option_grayscale, NULL, NULL, Icon_NOICON);
215 MENUITEM_FUNCTION(dithering_item, 0, "Dithering",
216 set_option_dithering, NULL, NULL, Icon_NOICON);
217 MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
218 &grayscale_item, &dithering_item);
220 static void display_options(void)
222 rb->do_menu(&display_menu, NULL, NULL, false);
224 #endif /* defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) */
226 static int show_menu(void) /* return 1 to quit */
228 int result;
230 enum menu_id
232 MIID_RETURN = 0,
233 MIID_TOGGLE_SS_MODE,
234 MIID_CHANGE_SS_MODE,
235 #ifdef USE_PLUG_BUF
236 MIID_SHOW_PLAYBACK_MENU,
237 #endif
238 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
239 MIID_DISPLAY_OPTIONS,
240 #endif
241 MIID_QUIT,
244 MENUITEM_STRINGLIST(menu, MENU_TITLE, NULL,
245 "Return", "Toggle Slideshow Mode",
246 "Change Slideshow Time",
247 #ifdef USE_PLUG_BUF
248 "Show Playback Menu",
249 #endif
250 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
251 "Display Options",
252 #endif
253 "Quit");
255 static const struct opt_items slideshow[2] = {
256 { "Disable", -1 },
257 { "Enable", -1 },
260 result=rb->do_menu(&menu, NULL, NULL, false);
262 switch (result)
264 case MIID_RETURN:
265 break;
266 case MIID_TOGGLE_SS_MODE:
267 rb->set_option("Toggle Slideshow", &slideshow_enabled, BOOL,
268 slideshow , 2, NULL);
269 break;
270 case MIID_CHANGE_SS_MODE:
271 rb->set_int("Slideshow Time", "s", UNIT_SEC,
272 &settings.ss_timeout, NULL, 1,
273 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
274 break;
276 #ifdef USE_PLUG_BUF
277 case MIID_SHOW_PLAYBACK_MENU:
278 if (plug_buf)
280 playback_control(NULL);
282 else
284 rb->splash(HZ, "Cannot restart playback");
286 break;
287 #endif
288 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
289 case MIID_DISPLAY_OPTIONS:
290 display_options();
291 break;
292 #endif
293 case MIID_QUIT:
294 return 1;
295 break;
298 #ifdef DISK_SPINDOWN
299 /* change ata spindown time based on slideshow time setting */
300 immediate_ata_off = false;
301 rb->storage_spindown(rb->global_settings->disk_spindown);
303 if (slideshow_enabled)
305 if(settings.ss_timeout < 10)
307 /* slideshow times < 10s keep disk spinning */
308 rb->storage_spindown(0);
310 else if (!rb->mp3_is_playing())
312 /* slideshow times > 10s and not playing: ata_off after load */
313 immediate_ata_off = true;
316 #endif
317 #if LCD_DEPTH > 1
318 rb->lcd_set_backdrop(NULL);
319 rb->lcd_set_foreground(LCD_WHITE);
320 rb->lcd_set_background(LCD_BLACK);
321 #endif
322 rb->lcd_clear_display();
323 return 0;
326 #ifdef USE_PLUG_BUF
327 static int ask_and_get_audio_buffer(const char *filename)
329 rb->lcd_setfont(FONT_SYSFIXED);
330 rb->lcd_clear_display();
331 rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1);
332 rb->lcd_puts(0, 1, "Not enough plugin memory!");
333 rb->lcd_puts(0, 2, "Zoom In: Stop playback.");
334 if(entries > 1)
335 rb->lcd_puts(0, 3, "Left/Right: Skip File.");
336 rb->lcd_puts(0, 4, "Show Menu: Quit.");
337 rb->lcd_update();
338 rb->lcd_setfont(FONT_UI);
340 rb->button_clear_queue();
342 while (1)
344 int button = rb->button_get(true);
345 switch(button)
347 case IMGVIEW_ZOOM_IN:
348 plug_buf = false;
349 buf = rb->plugin_get_audio_buffer(&buf_size);
350 /*try again this file, now using the audio buffer */
351 return PLUGIN_OTHER;
352 #ifdef IMGVIEW_RC_MENU
353 case IMGVIEW_RC_MENU:
354 #endif
355 #ifdef IMGVIEW_QUIT
356 case IMGVIEW_QUIT:
357 #endif
358 case IMGVIEW_MENU:
359 return PLUGIN_OK;
361 case IMGVIEW_LEFT:
362 if(entries>1)
364 rb->lcd_clear_display();
365 return change_filename(DIR_PREV);
367 break;
369 case IMGVIEW_RIGHT:
370 if(entries>1)
372 rb->lcd_clear_display();
373 return change_filename(DIR_NEXT);
375 break;
376 default:
377 if(rb->default_event_handler_ex(button, cleanup, NULL)
378 == SYS_USB_CONNECTED)
379 return PLUGIN_USB_CONNECTED;
383 #endif /* USE_PLUG_BUF */
385 /* callback updating a progress meter while image decoding */
386 void cb_progress(int current, int total)
388 rb->yield(); /* be nice to the other threads */
389 #ifndef USEGSLIB
390 /* in slideshow mode, keep gui interference to a minimum */
391 const int size = (!running_slideshow ? 8 : 4);
392 #else
393 const int size = 8;
394 if(!running_slideshow)
395 #endif
397 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
398 0, LCD_HEIGHT-size, LCD_WIDTH, size,
399 total, 0, current, HORIZONTAL);
400 rb->lcd_update_rect(0, LCD_HEIGHT-size, LCD_WIDTH, size);
404 #define VSCROLL (LCD_HEIGHT/8)
405 #define HSCROLL (LCD_WIDTH/10)
407 #define ZOOM_IN 100 /* return codes for below function */
408 #define ZOOM_OUT 101
410 /* Pan the viewing window right - move image to the left and fill in
411 the right-hand side */
412 static void pan_view_right(struct image_info *info)
414 int move;
416 move = MIN(HSCROLL, info->width - info->x - LCD_WIDTH);
417 if (move > 0)
419 MYXLCD(scroll_left)(move); /* scroll left */
420 info->x += move;
421 draw_image_rect(info, LCD_WIDTH - move, 0, move, info->height-info->y);
422 MYLCD_UPDATE();
426 /* Pan the viewing window left - move image to the right and fill in
427 the left-hand side */
428 static void pan_view_left(struct image_info *info)
430 int move;
432 move = MIN(HSCROLL, info->x);
433 if (move > 0)
435 MYXLCD(scroll_right)(move); /* scroll right */
436 info->x -= move;
437 draw_image_rect(info, 0, 0, move, info->height-info->y);
438 MYLCD_UPDATE();
442 /* Pan the viewing window up - move image down and fill in
443 the top */
444 static void pan_view_up(struct image_info *info)
446 int move;
448 move = MIN(VSCROLL, info->y);
449 if (move > 0)
451 MYXLCD(scroll_down)(move); /* scroll down */
452 info->y -= move;
453 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
454 if (settings.jpeg_dither_mode == DITHER_DIFFUSION)
456 /* Draw over the band at the top of the last update
457 caused by lack of error history on line zero. */
458 move = MIN(move + 1, info->y + info->height);
460 #endif
461 draw_image_rect(info, 0, 0, info->width-info->x, move);
462 MYLCD_UPDATE();
466 /* Pan the viewing window down - move image up and fill in
467 the bottom */
468 static void pan_view_down(struct image_info *info)
470 int move;
472 move = MIN(VSCROLL, info->height - info->y - LCD_HEIGHT);
473 if (move > 0)
475 MYXLCD(scroll_up)(move); /* scroll up */
476 info->y += move;
477 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
478 if (settings.jpeg_dither_mode == DITHER_DIFFUSION)
480 /* Save the line that was on the last line of the display
481 and draw one extra line above then recover the line with
482 image data that had an error history when it was drawn.
484 move++, info->y--;
485 rb->memcpy(rgb_linebuf,
486 rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
487 LCD_WIDTH*sizeof (fb_data));
489 #endif
491 draw_image_rect(info, 0, LCD_HEIGHT - move, info->width-info->x, move);
493 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
494 if (settings.jpeg_dither_mode == DITHER_DIFFUSION)
496 /* Cover the first row drawn with previous image data. */
497 rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
498 rgb_linebuf, LCD_WIDTH*sizeof (fb_data));
499 info->y++;
501 #endif
502 MYLCD_UPDATE();
506 /* interactively scroll around the image */
507 static int scroll_bmp(struct image_info *info)
509 int button;
510 int lastbutton = BUTTON_NONE;
512 while (true)
514 if (slideshow_enabled)
515 button = rb->button_get_w_tmo(settings.ss_timeout * HZ);
516 else
517 button = rb->button_get(true);
519 running_slideshow = false;
521 switch(button)
523 case IMGVIEW_LEFT:
524 if (entries > 1 && info->width <= LCD_WIDTH
525 && info->height <= LCD_HEIGHT)
526 return change_filename(DIR_PREV);
527 case IMGVIEW_LEFT | BUTTON_REPEAT:
528 pan_view_left(info);
529 break;
531 case IMGVIEW_RIGHT:
532 if (entries > 1 && info->width <= LCD_WIDTH
533 && info->height <= LCD_HEIGHT)
534 return change_filename(DIR_NEXT);
535 case IMGVIEW_RIGHT | BUTTON_REPEAT:
536 pan_view_right(info);
537 break;
539 case IMGVIEW_UP:
540 case IMGVIEW_UP | BUTTON_REPEAT:
541 pan_view_up(info);
542 break;
544 case IMGVIEW_DOWN:
545 case IMGVIEW_DOWN | BUTTON_REPEAT:
546 pan_view_down(info);
547 break;
549 case BUTTON_NONE:
550 if (slideshow_enabled && entries > 1)
552 running_slideshow = true;
553 return change_filename(DIR_NEXT);
555 break;
557 #ifdef IMGVIEW_SLIDE_SHOW
558 case IMGVIEW_SLIDE_SHOW:
559 slideshow_enabled = !slideshow_enabled;
560 break;
561 #endif
563 #ifdef IMGVIEW_NEXT_REPEAT
564 case IMGVIEW_NEXT_REPEAT:
565 #endif
566 case IMGVIEW_NEXT:
567 if (entries > 1)
568 return change_filename(DIR_NEXT);
569 break;
571 #ifdef IMGVIEW_PREVIOUS_REPEAT
572 case IMGVIEW_PREVIOUS_REPEAT:
573 #endif
574 case IMGVIEW_PREVIOUS:
575 if (entries > 1)
576 return change_filename(DIR_PREV);
577 break;
579 case IMGVIEW_ZOOM_IN:
580 #ifdef IMGVIEW_ZOOM_PRE
581 if (lastbutton != IMGVIEW_ZOOM_PRE)
582 break;
583 #endif
584 return ZOOM_IN;
585 break;
587 case IMGVIEW_ZOOM_OUT:
588 #ifdef IMGVIEW_ZOOM_PRE
589 if (lastbutton != IMGVIEW_ZOOM_PRE)
590 break;
591 #endif
592 return ZOOM_OUT;
593 break;
595 #ifdef IMGVIEW_RC_MENU
596 case IMGVIEW_RC_MENU:
597 #endif
598 case IMGVIEW_MENU:
599 #ifdef IMGVIEW_MENU_PRE
600 if (lastbutton != IMGVIEW_MENU_PRE)
601 break;
602 #endif
603 #ifdef USEGSLIB
604 grey_show(false); /* switch off greyscale overlay */
605 #endif
606 if (show_menu() == 1)
607 return PLUGIN_OK;
609 #ifdef USEGSLIB
610 grey_show(true); /* switch on greyscale overlay */
611 #else
612 draw_image_rect(info, 0, 0,
613 info->width-info->x, info->height-info->y);
614 MYLCD_UPDATE();
615 #endif
616 break;
618 #ifdef IMGVIEW_QUIT
619 case IMGVIEW_QUIT:
620 return PLUGIN_OK;
621 break;
622 #endif
624 default:
625 if (rb->default_event_handler_ex(button, cleanup, NULL)
626 == SYS_USB_CONNECTED)
627 return PLUGIN_USB_CONNECTED;
628 break;
630 } /* switch */
632 if (button != BUTTON_NONE)
633 lastbutton = button;
634 } /* while (true) */
637 /********************* main function *************************/
639 /* how far can we zoom in without running out of memory */
640 static int min_downscale(int bufsize)
642 int downscale = 8;
644 if (img_mem(8) > bufsize)
645 return 0; /* error, too large, even 1:8 doesn't fit */
647 while (downscale > 1 && img_mem(downscale/2) <= bufsize)
648 downscale /= 2;
650 return downscale;
653 /* how far can we zoom out, to fit image into the LCD */
654 static int max_downscale(struct image_info *info)
656 int downscale = 1;
658 while (downscale < 8 && (info->x_size/downscale > LCD_WIDTH
659 || info->y_size/downscale > LCD_HEIGHT))
661 downscale *= 2;
664 return downscale;
667 /* set the view to the given center point, limit if necessary */
668 static void set_view(struct image_info *info, int cx, int cy)
670 int x, y;
672 /* plain center to available width/height */
673 x = cx - MIN(LCD_WIDTH, info->width) / 2;
674 y = cy - MIN(LCD_HEIGHT, info->height) / 2;
676 /* limit against upper image size */
677 x = MIN(info->width - LCD_WIDTH, x);
678 y = MIN(info->height - LCD_HEIGHT, y);
680 /* limit against negative side */
681 x = MAX(0, x);
682 y = MAX(0, y);
684 info->x = x; /* set the values */
685 info->y = y;
688 /* calculate the view center based on the bitmap position */
689 static void get_view(struct image_info *info, int *p_cx, int *p_cy)
691 *p_cx = info->x + MIN(LCD_WIDTH, info->width) / 2;
692 *p_cy = info->y + MIN(LCD_HEIGHT, info->height) / 2;
695 /* load, decode, display the image */
696 static int load_and_show(char* filename, struct image_info *info)
698 int status;
699 int cx, cy;
700 ssize_t remaining;
702 rb->lcd_clear_display();
704 rb->memset(info, 0, sizeof(*info));
705 remaining = buf_size;
707 if (rb->button_get(false) == IMGVIEW_MENU)
708 status = PLUGIN_ABORT;
709 else
710 status = load_image(filename, info, buf, &remaining);
712 if (status == PLUGIN_OUTOFMEM)
714 #ifdef USE_PLUG_BUF
715 if(plug_buf)
717 return ask_and_get_audio_buffer(filename);
719 else
720 #endif
722 rb->splash(HZ, "Out of Memory");
723 file_pt[curfile] = NULL;
724 return change_filename(direction);
727 else if (status == PLUGIN_ERROR)
729 file_pt[curfile] = NULL;
730 return change_filename(direction);
732 else if (status == PLUGIN_ABORT) {
733 rb->splash(HZ, "aborted");
734 return PLUGIN_OK;
737 ds_max = max_downscale(info); /* check display constraint */
738 ds_min = min_downscale(remaining); /* check memory constraint */
739 if (ds_min == 0)
741 #if UNSCALED_IS_AVAILABLE
742 /* Can not resize the image but original one is available, so use it. */
743 ds_min = ds_max = 1;
744 #else
745 /* not enough memory to decode image. */
746 #ifdef USE_PLUG_BUF
747 if(plug_buf)
749 return ask_and_get_audio_buffer(filename);
751 else
752 #endif
754 rb->splash(HZ, "too large");
755 file_pt[curfile] = NULL;
756 return change_filename(direction);
758 #endif
760 else if (ds_max < ds_min)
761 ds_max = ds_min;
763 ds = ds_max; /* initialize setting */
764 cx = info->x_size/ds/2; /* center the view */
765 cy = info->y_size/ds/2;
767 do /* loop the image prepare and decoding when zoomed */
769 status = get_image(info, ds); /* decode or fetch from cache */
770 if (status == PLUGIN_ERROR)
772 file_pt[curfile] = NULL;
773 return change_filename(direction);
776 set_view(info, cx, cy);
778 if(!running_slideshow)
780 rb->snprintf(print, sizeof(print), "showing %dx%d",
781 info->width, info->height);
782 rb->lcd_puts(0, 3, print);
783 rb->lcd_update();
786 MYLCD(clear_display)();
787 draw_image_rect(info, 0, 0,
788 info->width-info->x, info->height-info->y);
789 MYLCD_UPDATE();
791 #ifdef USEGSLIB
792 grey_show(true); /* switch on greyscale overlay */
793 #endif
795 /* drawing is now finished, play around with scrolling
796 * until you press OFF or connect USB
798 while (1)
800 status = scroll_bmp(info);
801 if (status == ZOOM_IN)
803 #if UNSCALED_IS_AVAILABLE
804 if (ds > 1)
805 #else
806 if (ds > ds_min)
807 #endif
809 #if UNSCALED_IS_AVAILABLE
810 /* if 1/1 is always available, jump ds from ds_min to 1. */
811 int zoom = (ds == ds_min)? ds_min: 2;
812 #else
813 const int zoom = 2;
814 #endif
815 ds /= zoom; /* reduce downscaling to zoom in */
816 get_view(info, &cx, &cy);
817 cx *= zoom; /* prepare the position in the new image */
818 cy *= zoom;
820 else
821 continue;
824 if (status == ZOOM_OUT)
826 if (ds < ds_max)
828 #if UNSCALED_IS_AVAILABLE
829 /* if ds is 1 and ds_min is > 1, jump ds to ds_min. */
830 int zoom = (ds < ds_min)? ds_min: 2;
831 #else
832 const int zoom = 2;
833 #endif
834 ds *= zoom; /* increase downscaling to zoom out */
835 get_view(info, &cx, &cy);
836 cx /= zoom; /* prepare the position in the new image */
837 cy /= zoom;
839 else
840 continue;
842 break;
845 #ifdef USEGSLIB
846 grey_show(false); /* switch off overlay */
847 #endif
848 rb->lcd_clear_display();
850 while (status > PLUGIN_OTHER);
851 #ifdef USEGSLIB
852 rb->lcd_update();
853 #endif
854 return status;
857 /******************** Plugin entry point *********************/
859 enum plugin_status plugin_start(const void* parameter)
861 int condition;
862 #ifdef USEGSLIB
863 long greysize; /* helper */
864 #endif
866 if(!parameter) return PLUGIN_ERROR;
868 #ifdef USE_PLUG_BUF
869 buf = rb->plugin_get_buffer(&buf_size);
870 #else
871 buf = rb->plugin_get_audio_buffer(&buf_size);
872 #endif
874 rb->strcpy(np_file, parameter);
875 get_pic_list();
877 if(!entries) return PLUGIN_ERROR;
879 #ifdef USE_PLUG_BUF
880 if(!rb->audio_status())
882 plug_buf = false;
883 buf = rb->plugin_get_audio_buffer(&buf_size);
885 #endif
887 #ifdef USEGSLIB
888 if (!grey_init(buf, buf_size, GREY_ON_COP,
889 LCD_WIDTH, LCD_HEIGHT, &greysize))
891 rb->splash(HZ, "grey buf error");
892 return PLUGIN_ERROR;
894 buf += greysize;
895 buf_size -= greysize;
896 #endif
898 /* should be ok to just load settings since the plugin itself has
899 just been loaded from disk and the drive should be spinning */
900 configfile_load(IMGVIEW_CONFIGFILE, config,
901 ARRAYLEN(config), IMGVIEW_SETTINGS_MINVERSION);
902 rb->memcpy(&old_settings, &settings, sizeof (settings));
904 /* Turn off backlight timeout */
905 backlight_force_on(); /* backlight control in lib/helper.c */
907 #if LCD_DEPTH > 1
908 rb->lcd_set_backdrop(NULL);
909 rb->lcd_set_foreground(LCD_WHITE);
910 rb->lcd_set_background(LCD_BLACK);
911 #endif
915 condition = load_and_show(np_file, &image_info);
916 } while (condition >= PLUGIN_OTHER);
918 if (rb->memcmp(&settings, &old_settings, sizeof (settings)))
920 /* Just in case drive has to spin, keep it from looking locked */
921 rb->splash(0, "Saving Settings");
922 configfile_save(IMGVIEW_CONFIGFILE, config,
923 ARRAYLEN(config), IMGVIEW_SETTINGS_VERSION);
926 #ifdef DISK_SPINDOWN
927 /* set back ata spindown time in case we changed it */
928 rb->storage_spindown(rb->global_settings->disk_spindown);
929 #endif
931 /* Turn on backlight timeout (revert to settings) */
932 backlight_use_settings(); /* backlight control in lib/helper.c */
934 #ifdef USEGSLIB
935 grey_release(); /* deinitialize */
936 #endif
938 return condition;