This should fix FS#10917, and also fix channel swapping i introduced in r23784. Lets...
[kugel-rb.git] / apps / plugins / jpeg / jpeg.c
blob4a61f13e51cf3324c62574dfbf3aad5ff92336a9
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * JPEG image viewer
11 * (This is a real mess if it has to be coded in one single C file)
13 * File scrolling addition (C) 2005 Alexander Spyridakis
14 * Copyright (C) 2004 Jörg Hohensohn aka [IDC]Dragon
15 * Heavily borrowed from the IJG implementation (C) Thomas G. Lane
16 * Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24 * KIND, either express or implied.
26 ****************************************************************************/
28 #include "plugin.h"
29 #include <lib/playback_control.h>
30 #include <lib/helper.h>
31 #include <lib/configfile.h>
33 #include <lib/grey.h>
34 #include <lib/xlcd.h>
36 #include "jpeg.h"
37 #include "jpeg_decoder.h"
39 PLUGIN_HEADER
41 #ifdef HAVE_LCD_COLOR
42 #include "yuv2rgb.h"
43 #endif
45 /* different graphics libraries */
46 #if LCD_DEPTH < 8
47 #define USEGSLIB
48 GREY_INFO_STRUCT
49 #define MYLCD(fn) grey_ub_ ## fn
50 #define MYLCD_UPDATE()
51 #define MYXLCD(fn) grey_ub_ ## fn
52 #else
53 #define MYLCD(fn) rb->lcd_ ## fn
54 #define MYLCD_UPDATE() rb->lcd_update();
55 #define MYXLCD(fn) xlcd_ ## fn
56 #endif
58 /* Min memory allowing us to use the plugin buffer
59 * and thus not stopping the music
60 * *Very* rough estimation:
61 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
62 * + 20k code size = 60 000
63 * + 50k min for jpeg = 120 000
65 #define MIN_MEM 120000
67 /* Headings */
68 #define DIR_PREV 1
69 #define DIR_NEXT -1
70 #define DIR_NONE 0
72 #define PLUGIN_OTHER 10 /* State code for output with return. */
73 #define PLUGIN_ABORT 11
74 #define PLUGIN_OUTOFMEM 12
76 /******************************* Globals ***********************************/
78 static int slideshow_enabled = false; /* run slideshow */
79 static int running_slideshow = false; /* loading image because of slideshw */
80 #ifndef SIMULATOR
81 static int immediate_ata_off = false; /* power down disk after loading */
82 #endif
84 #ifdef HAVE_LCD_COLOR
85 fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when
86 DITHER_DIFFUSION is set */
87 #endif
90 /* Persistent configuration */
91 #define JPEG_CONFIGFILE "jpeg.cfg"
92 #define JPEG_SETTINGS_MINVERSION 1
93 #define JPEG_SETTINGS_VERSION 2
95 /* Slideshow times */
96 #define SS_MIN_TIMEOUT 1
97 #define SS_MAX_TIMEOUT 20
98 #define SS_DEFAULT_TIMEOUT 5
100 struct jpeg_settings
102 #ifdef HAVE_LCD_COLOR
103 int colour_mode;
104 int dither_mode;
105 #endif
106 int ss_timeout;
109 static struct jpeg_settings jpeg_settings =
111 #ifdef HAVE_LCD_COLOR
112 COLOURMODE_COLOUR,
113 DITHER_NONE,
114 #endif
115 SS_DEFAULT_TIMEOUT
117 static struct jpeg_settings old_settings;
119 static struct configdata jpeg_config[] =
121 #ifdef HAVE_LCD_COLOR
122 { TYPE_ENUM, 0, COLOUR_NUM_MODES, { .int_p = &jpeg_settings.colour_mode },
123 "Colour Mode", (char *[]){ "Colour", "Grayscale" } },
124 { TYPE_ENUM, 0, DITHER_NUM_MODES, { .int_p = &jpeg_settings.dither_mode },
125 "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" } },
126 #endif
127 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
128 { .int_p = &jpeg_settings.ss_timeout }, "Slideshow Time", NULL },
131 #if LCD_DEPTH > 1
132 static fb_data* old_backdrop;
133 #endif
135 /**************** begin Application ********************/
138 /************************* Types ***************************/
140 struct t_disp
142 #ifdef HAVE_LCD_COLOR
143 unsigned char* bitmap[3]; /* Y, Cr, Cb */
144 int csub_x, csub_y;
145 #else
146 unsigned char* bitmap[1]; /* Y only */
147 #endif
148 int width;
149 int height;
150 int stride;
151 int x, y;
154 /************************* Globals ***************************/
156 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
157 static struct t_disp disp[9];
159 /* my memory pool (from the mp3 buffer) */
160 static char print[32]; /* use a common snprintf() buffer */
161 /* the remaining free part of the buffer for compressed+uncompressed images */
162 static unsigned char* buf;
163 static ssize_t buf_size;
165 /* the root of the images, hereafter are decompresed ones */
166 static unsigned char* buf_root;
167 static int root_size;
169 /* up to here currently used by image(s) */
170 static unsigned char* buf_images;
171 static ssize_t buf_images_size;
173 static int ds, ds_min, ds_max; /* downscaling and limits */
174 static struct jpeg jpg; /* too large for stack */
176 static struct tree_context *tree;
178 /* the current full file name */
179 static char np_file[MAX_PATH];
180 static int curfile = 0, direction = DIR_NONE, entries = 0;
182 /* list of the jpeg files */
183 static char **file_pt;
184 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
185 /* are we using the plugin buffer or the audio buffer? */
186 static bool plug_buf = true;
187 #endif
190 /************************* Implementation ***************************/
192 bool jpg_ext(const char ext[])
194 if(!ext)
195 return false;
196 if(!rb->strcasecmp(ext,".jpg") ||
197 !rb->strcasecmp(ext,".jpe") ||
198 !rb->strcasecmp(ext,".jpeg"))
199 return true;
200 else
201 return false;
204 /*Read directory contents for scrolling. */
205 void get_pic_list(void)
207 int i;
208 struct entry *dircache;
209 char *pname;
210 tree = rb->tree_get_context();
211 dircache = tree->dircache;
213 file_pt = (char **) buf;
215 /* Remove path and leave only the name.*/
216 pname = rb->strrchr(np_file,'/');
217 pname++;
219 for (i = 0; i < tree->filesindir; i++)
221 if (!(dircache[i].attr & ATTR_DIRECTORY)
222 && jpg_ext(rb->strrchr(dircache[i].name,'.')))
224 file_pt[entries] = dircache[i].name;
225 /* Set Selected File. */
226 if (!rb->strcmp(file_pt[entries], pname))
227 curfile = entries;
228 entries++;
232 buf += (entries * sizeof(char**));
233 buf_size -= (entries * sizeof(char**));
236 int change_filename(int direct)
238 bool file_erased = (file_pt[curfile] == NULL);
239 direction = direct;
241 curfile += (direct == DIR_PREV? entries - 1: 1);
242 if (curfile >= entries)
243 curfile -= entries;
245 if (file_erased)
247 /* remove 'erased' file names from list. */
248 int count, i;
249 for (count = i = 0; i < entries; i++)
251 if (curfile == i)
252 curfile = count;
253 if (file_pt[i] != NULL)
254 file_pt[count++] = file_pt[i];
256 entries = count;
259 if (entries == 0)
261 rb->splash(HZ, "No supported files");
262 return PLUGIN_ERROR;
265 if (rb->strlen(tree->currdir) > 1)
267 rb->strcpy(np_file, tree->currdir);
268 rb->strcat(np_file, "/");
270 else
271 rb->strcpy(np_file, tree->currdir);
273 rb->strcat(np_file, file_pt[curfile]);
275 return PLUGIN_OTHER;
278 /* switch off overlay, for handling SYS_ events */
279 void cleanup(void *parameter)
281 (void)parameter;
282 #ifdef USEGSLIB
283 grey_show(false);
284 #endif
287 #define VSCROLL (LCD_HEIGHT/8)
288 #define HSCROLL (LCD_WIDTH/10)
290 #define ZOOM_IN 100 /* return codes for below function */
291 #define ZOOM_OUT 101
293 #ifdef HAVE_LCD_COLOR
294 bool set_option_grayscale(void)
296 bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY;
297 rb->set_bool("Grayscale", &gray);
298 jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR;
299 return false;
302 bool set_option_dithering(void)
304 static const struct opt_items dithering[DITHER_NUM_MODES] = {
305 [DITHER_NONE] = { "Off", -1 },
306 [DITHER_ORDERED] = { "Ordered", -1 },
307 [DITHER_DIFFUSION] = { "Diffusion", -1 },
310 rb->set_option("Dithering", &jpeg_settings.dither_mode, INT,
311 dithering, DITHER_NUM_MODES, NULL);
312 return false;
315 MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale",
316 set_option_grayscale, NULL, NULL, Icon_NOICON);
317 MENUITEM_FUNCTION(dithering_item, 0, "Dithering",
318 set_option_dithering, NULL, NULL, Icon_NOICON);
319 MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
320 &grayscale_item, &dithering_item);
322 static void display_options(void)
324 rb->do_menu(&display_menu, NULL, NULL, false);
326 #endif /* HAVE_LCD_COLOR */
328 int show_menu(void) /* return 1 to quit */
330 #if LCD_DEPTH > 1
331 rb->lcd_set_backdrop(old_backdrop);
332 #ifdef HAVE_LCD_COLOR
333 rb->lcd_set_foreground(rb->global_settings->fg_color);
334 rb->lcd_set_background(rb->global_settings->bg_color);
335 #else
336 rb->lcd_set_foreground(LCD_BLACK);
337 rb->lcd_set_background(LCD_WHITE);
338 #endif
339 #endif
340 int result;
342 enum menu_id
344 MIID_RETURN = 0,
345 MIID_TOGGLE_SS_MODE,
346 MIID_CHANGE_SS_MODE,
347 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
348 MIID_SHOW_PLAYBACK_MENU,
349 #endif
350 #ifdef HAVE_LCD_COLOR
351 MIID_DISPLAY_OPTIONS,
352 #endif
353 MIID_QUIT,
356 MENUITEM_STRINGLIST(menu, "Jpeg Menu", NULL,
357 "Return", "Toggle Slideshow Mode",
358 "Change Slideshow Time",
359 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
360 "Show Playback Menu",
361 #endif
362 #ifdef HAVE_LCD_COLOR
363 "Display Options",
364 #endif
365 "Quit");
367 static const struct opt_items slideshow[2] = {
368 { "Disable", -1 },
369 { "Enable", -1 },
372 result=rb->do_menu(&menu, NULL, NULL, false);
374 switch (result)
376 case MIID_RETURN:
377 break;
378 case MIID_TOGGLE_SS_MODE:
379 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
380 slideshow , 2, NULL);
381 break;
382 case MIID_CHANGE_SS_MODE:
383 rb->set_int("Slideshow Time", "s", UNIT_SEC,
384 &jpeg_settings.ss_timeout, NULL, 1,
385 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
386 break;
388 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
389 case MIID_SHOW_PLAYBACK_MENU:
390 if (plug_buf)
392 playback_control(NULL);
394 else
396 rb->splash(HZ, "Cannot restart playback");
398 break;
399 #endif
400 #ifdef HAVE_LCD_COLOR
401 case MIID_DISPLAY_OPTIONS:
402 display_options();
403 break;
404 #endif
405 case MIID_QUIT:
406 return 1;
407 break;
410 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
411 /* change ata spindown time based on slideshow time setting */
412 immediate_ata_off = false;
413 rb->storage_spindown(rb->global_settings->disk_spindown);
415 if (slideshow_enabled)
417 if(jpeg_settings.ss_timeout < 10)
419 /* slideshow times < 10s keep disk spinning */
420 rb->storage_spindown(0);
422 else if (!rb->mp3_is_playing())
424 /* slideshow times > 10s and not playing: ata_off after load */
425 immediate_ata_off = true;
428 #endif
429 #if LCD_DEPTH > 1
430 rb->lcd_set_backdrop(NULL);
431 rb->lcd_set_foreground(LCD_WHITE);
432 rb->lcd_set_background(LCD_BLACK);
433 #endif
434 rb->lcd_clear_display();
435 return 0;
438 void draw_image_rect(struct t_disp* pdisp, int x, int y, int width, int height)
440 #ifdef HAVE_LCD_COLOR
441 yuv_bitmap_part(
442 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
443 pdisp->x + x, pdisp->y + y, pdisp->stride,
444 x + MAX(0, (LCD_WIDTH - pdisp->width) / 2),
445 y + MAX(0, (LCD_HEIGHT - pdisp->height) / 2),
446 width, height,
447 jpeg_settings.colour_mode, jpeg_settings.dither_mode);
448 #else
449 MYXLCD(gray_bitmap_part)(
450 pdisp->bitmap[0], pdisp->x + x, pdisp->y + y, pdisp->stride,
451 x + MAX(0, (LCD_WIDTH-pdisp->width)/2),
452 y + MAX(0, (LCD_HEIGHT-pdisp->height)/2),
453 width, height);
454 #endif
457 /* Pan the viewing window right - move image to the left and fill in
458 the right-hand side */
459 static void pan_view_right(struct t_disp* pdisp)
461 int move;
463 move = MIN(HSCROLL, pdisp->width - pdisp->x - LCD_WIDTH);
464 if (move > 0)
466 MYXLCD(scroll_left)(move); /* scroll left */
467 pdisp->x += move;
468 draw_image_rect(pdisp, LCD_WIDTH - move, 0, move, pdisp->height-pdisp->y);
469 MYLCD_UPDATE();
473 /* Pan the viewing window left - move image to the right and fill in
474 the left-hand side */
475 static void pan_view_left(struct t_disp* pdisp)
477 int move;
479 move = MIN(HSCROLL, pdisp->x);
480 if (move > 0)
482 MYXLCD(scroll_right)(move); /* scroll right */
483 pdisp->x -= move;
484 draw_image_rect(pdisp, 0, 0, move, pdisp->height-pdisp->y);
485 MYLCD_UPDATE();
489 /* Pan the viewing window up - move image down and fill in
490 the top */
491 static void pan_view_up(struct t_disp* pdisp)
493 int move;
495 move = MIN(VSCROLL, pdisp->y);
496 if (move > 0)
498 MYXLCD(scroll_down)(move); /* scroll down */
499 pdisp->y -= move;
500 #ifdef HAVE_LCD_COLOR
501 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
503 /* Draw over the band at the top of the last update
504 caused by lack of error history on line zero. */
505 move = MIN(move + 1, pdisp->y + pdisp->height);
507 #endif
508 draw_image_rect(pdisp, 0, 0, pdisp->width-pdisp->x, move);
509 MYLCD_UPDATE();
513 /* Pan the viewing window down - move image up and fill in
514 the bottom */
515 static void pan_view_down(struct t_disp* pdisp)
517 int move;
519 move = MIN(VSCROLL, pdisp->height - pdisp->y - LCD_HEIGHT);
520 if (move > 0)
522 MYXLCD(scroll_up)(move); /* scroll up */
523 pdisp->y += move;
524 #ifdef HAVE_LCD_COLOR
525 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
527 /* Save the line that was on the last line of the display
528 and draw one extra line above then recover the line with
529 image data that had an error history when it was drawn.
531 move++, pdisp->y--;
532 rb->memcpy(rgb_linebuf,
533 rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
534 LCD_WIDTH*sizeof (fb_data));
536 #endif
538 draw_image_rect(pdisp, 0, LCD_HEIGHT - move, pdisp->width-pdisp->x, move);
540 #ifdef HAVE_LCD_COLOR
541 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
543 /* Cover the first row drawn with previous image data. */
544 rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
545 rgb_linebuf, LCD_WIDTH*sizeof (fb_data));
546 pdisp->y++;
548 #endif
549 MYLCD_UPDATE();
553 /* interactively scroll around the image */
554 int scroll_bmp(struct t_disp* pdisp)
556 int button;
557 int lastbutton = 0;
559 while (true)
561 if (slideshow_enabled)
562 button = rb->button_get_w_tmo(jpeg_settings.ss_timeout * HZ);
563 else
564 button = rb->button_get(true);
566 running_slideshow = false;
568 switch(button)
570 case JPEG_LEFT:
571 if (entries > 1 && pdisp->width <= LCD_WIDTH
572 && pdisp->height <= LCD_HEIGHT)
573 return change_filename(DIR_PREV);
574 case JPEG_LEFT | BUTTON_REPEAT:
575 pan_view_left(pdisp);
576 break;
578 case JPEG_RIGHT:
579 if (entries > 1 && pdisp->width <= LCD_WIDTH
580 && pdisp->height <= LCD_HEIGHT)
581 return change_filename(DIR_NEXT);
582 case JPEG_RIGHT | BUTTON_REPEAT:
583 pan_view_right(pdisp);
584 break;
586 case JPEG_UP:
587 case JPEG_UP | BUTTON_REPEAT:
588 pan_view_up(pdisp);
589 break;
591 case JPEG_DOWN:
592 case JPEG_DOWN | BUTTON_REPEAT:
593 pan_view_down(pdisp);
594 break;
596 case BUTTON_NONE:
597 if (!slideshow_enabled)
598 break;
599 running_slideshow = true;
600 if (entries > 1)
601 return change_filename(DIR_NEXT);
602 break;
604 #ifdef JPEG_SLIDE_SHOW
605 case JPEG_SLIDE_SHOW:
606 slideshow_enabled = !slideshow_enabled;
607 running_slideshow = slideshow_enabled;
608 break;
609 #endif
611 #ifdef JPEG_NEXT_REPEAT
612 case JPEG_NEXT_REPEAT:
613 #endif
614 case JPEG_NEXT:
615 if (entries > 1)
616 return change_filename(DIR_NEXT);
617 break;
619 #ifdef JPEG_PREVIOUS_REPEAT
620 case JPEG_PREVIOUS_REPEAT:
621 #endif
622 case JPEG_PREVIOUS:
623 if (entries > 1)
624 return change_filename(DIR_PREV);
625 break;
627 case JPEG_ZOOM_IN:
628 #ifdef JPEG_ZOOM_PRE
629 if (lastbutton != JPEG_ZOOM_PRE)
630 break;
631 #endif
632 return ZOOM_IN;
633 break;
635 case JPEG_ZOOM_OUT:
636 #ifdef JPEG_ZOOM_PRE
637 if (lastbutton != JPEG_ZOOM_PRE)
638 break;
639 #endif
640 return ZOOM_OUT;
641 break;
642 #ifdef JPEG_RC_MENU
643 case JPEG_RC_MENU:
644 #endif
645 case JPEG_MENU:
646 #ifdef USEGSLIB
647 grey_show(false); /* switch off greyscale overlay */
648 #endif
649 if (show_menu() == 1)
650 return PLUGIN_OK;
652 #ifdef USEGSLIB
653 grey_show(true); /* switch on greyscale overlay */
654 #else
655 draw_image_rect(pdisp, 0, 0,
656 pdisp->width-pdisp->x, pdisp->height-pdisp->y);
657 MYLCD_UPDATE();
658 #endif
659 break;
660 default:
661 if (rb->default_event_handler_ex(button, cleanup, NULL)
662 == SYS_USB_CONNECTED)
663 return PLUGIN_USB_CONNECTED;
664 break;
666 } /* switch */
668 if (button != BUTTON_NONE)
669 lastbutton = button;
670 } /* while (true) */
673 /********************* main function *************************/
675 /* callback updating a progress meter while JPEG decoding */
676 void cb_progress(int current, int total)
678 rb->yield(); /* be nice to the other threads */
679 if(!running_slideshow)
681 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
682 0, LCD_HEIGHT-8, LCD_WIDTH, 8,
683 total, 0, current, HORIZONTAL);
684 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
686 #ifndef USEGSLIB
687 else
689 /* in slideshow mode, keep gui interference to a minimum */
690 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
691 0, LCD_HEIGHT-4, LCD_WIDTH, 4,
692 total, 0, current, HORIZONTAL);
693 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
695 #endif
698 int jpegmem(struct jpeg *p_jpg, int ds)
700 int size;
702 size = (p_jpg->x_phys/ds/p_jpg->subsample_x[0])
703 * (p_jpg->y_phys/ds/p_jpg->subsample_y[0]);
704 #ifdef HAVE_LCD_COLOR
705 if (p_jpg->blocks > 1) /* colour, add requirements for chroma */
707 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[1])
708 * (p_jpg->y_phys/ds/p_jpg->subsample_y[1]);
709 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[2])
710 * (p_jpg->y_phys/ds/p_jpg->subsample_y[2]);
712 #endif
713 return size;
716 /* how far can we zoom in without running out of memory */
717 int min_downscale(struct jpeg *p_jpg, int bufsize)
719 int downscale = 8;
721 if (jpegmem(p_jpg, 8) > bufsize)
722 return 0; /* error, too large, even 1:8 doesn't fit */
724 while (downscale > 1 && jpegmem(p_jpg, downscale/2) <= bufsize)
725 downscale /= 2;
727 return downscale;
730 /* how far can we zoom out, to fit image into the LCD */
731 int max_downscale(struct jpeg *p_jpg)
733 int downscale = 1;
735 while (downscale < 8 && (p_jpg->x_size/downscale > LCD_WIDTH
736 || p_jpg->y_size/downscale > LCD_HEIGHT))
738 downscale *= 2;
741 return downscale;
744 /* load image from filename. */
745 int load_image(char* filename, struct jpeg *p_jpg)
747 int fd;
748 int filesize;
749 unsigned char* buf_jpeg; /* compressed JPEG image */
750 int status;
752 fd = rb->open(filename, O_RDONLY);
753 if (fd < 0)
755 rb->splashf(HZ, "err opening %s:%d", filename, fd);
756 return PLUGIN_ERROR;
758 filesize = rb->filesize(fd);
760 /* allocate JPEG buffer */
761 buf_jpeg = buf;
763 /* we can start the decompressed images behind it */
764 buf_images = buf_root = buf + filesize;
765 buf_images_size = root_size = buf_size - filesize;
767 if (buf_images_size <= 0)
769 rb->close(fd);
770 return PLUGIN_OUTOFMEM;
773 if(!running_slideshow)
775 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
776 rb->lcd_puts(0, 0, print);
777 rb->lcd_update();
779 rb->snprintf(print, sizeof(print), "loading %d bytes", filesize);
780 rb->lcd_puts(0, 1, print);
781 rb->lcd_update();
784 rb->read(fd, buf_jpeg, filesize);
785 rb->close(fd);
787 if(!running_slideshow)
789 rb->snprintf(print, sizeof(print), "decoding markers");
790 rb->lcd_puts(0, 2, print);
791 rb->lcd_update();
793 #ifndef SIMULATOR
794 else if(immediate_ata_off)
796 /* running slideshow and time is long enough: power down disk */
797 rb->storage_sleep();
799 #endif
801 /* process markers, unstuffing */
802 status = process_markers(buf_jpeg, filesize, p_jpg);
804 if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0))
805 { /* bad format or minimum components not contained */
806 rb->splashf(HZ, "unsupported %d", status);
807 return PLUGIN_ERROR;
810 if (!(status & DHT)) /* if no Huffman table present: */
811 default_huff_tbl(p_jpg); /* use default */
812 build_lut(p_jpg); /* derive Huffman and other lookup-tables */
814 if(!running_slideshow)
816 rb->snprintf(print, sizeof(print), "image %dx%d",
817 p_jpg->x_size, p_jpg->y_size);
818 rb->lcd_puts(0, 2, print);
819 rb->lcd_update();
822 return PLUGIN_OK;
825 /* return decoded or cached image */
826 struct t_disp* get_image(struct jpeg* p_jpg, int ds)
828 int w, h; /* used to center output */
829 int size; /* decompressed image size */
830 long time; /* measured ticks */
831 int status;
833 struct t_disp* p_disp = &disp[ds]; /* short cut */
835 if (p_disp->bitmap[0] != NULL)
837 return p_disp; /* we still have it */
840 /* assign image buffer */
842 /* physical size needed for decoding */
843 size = jpegmem(p_jpg, ds);
844 if (buf_images_size <= size)
845 { /* have to discard the current */
846 int i;
847 for (i=1; i<=8; i++)
848 disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */
849 buf_images = buf_root; /* start again from the beginning of the buffer */
850 buf_images_size = root_size;
853 #ifdef HAVE_LCD_COLOR
854 if (p_jpg->blocks > 1) /* colour jpeg */
856 int i;
858 for (i = 1; i < 3; i++)
860 size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i])
861 * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]);
862 p_disp->bitmap[i] = buf_images;
863 buf_images += size;
864 buf_images_size -= size;
866 p_disp->csub_x = p_jpg->subsample_x[1];
867 p_disp->csub_y = p_jpg->subsample_y[1];
869 else
871 p_disp->csub_x = p_disp->csub_y = 0;
872 p_disp->bitmap[1] = p_disp->bitmap[2] = buf_images;
874 #endif
875 /* size may be less when decoded (if height is not block aligned) */
876 size = (p_jpg->x_phys/ds) * (p_jpg->y_size / ds);
877 p_disp->bitmap[0] = buf_images;
878 buf_images += size;
879 buf_images_size -= size;
881 if(!running_slideshow)
883 rb->snprintf(print, sizeof(print), "decoding %d*%d",
884 p_jpg->x_size/ds, p_jpg->y_size/ds);
885 rb->lcd_puts(0, 3, print);
886 rb->lcd_update();
889 /* update image properties */
890 p_disp->width = p_jpg->x_size / ds;
891 p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */
892 p_disp->height = p_jpg->y_size / ds;
894 /* the actual decoding */
895 time = *rb->current_tick;
896 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
897 rb->cpu_boost(true);
898 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress);
899 rb->cpu_boost(false);
900 #else
901 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress);
902 #endif
903 if (status)
905 rb->splashf(HZ, "decode error %d", status);
906 return NULL;
908 time = *rb->current_tick - time;
910 if(!running_slideshow)
912 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
913 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
914 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
915 rb->lcd_update();
918 return p_disp;
922 /* set the view to the given center point, limit if necessary */
923 void set_view (struct t_disp* p_disp, int cx, int cy)
925 int x, y;
927 /* plain center to available width/height */
928 x = cx - MIN(LCD_WIDTH, p_disp->width) / 2;
929 y = cy - MIN(LCD_HEIGHT, p_disp->height) / 2;
931 /* limit against upper image size */
932 x = MIN(p_disp->width - LCD_WIDTH, x);
933 y = MIN(p_disp->height - LCD_HEIGHT, y);
935 /* limit against negative side */
936 x = MAX(0, x);
937 y = MAX(0, y);
939 p_disp->x = x; /* set the values */
940 p_disp->y = y;
943 /* calculate the view center based on the bitmap position */
944 void get_view(struct t_disp* p_disp, int* p_cx, int* p_cy)
946 *p_cx = p_disp->x + MIN(LCD_WIDTH, p_disp->width) / 2;
947 *p_cy = p_disp->y + MIN(LCD_HEIGHT, p_disp->height) / 2;
950 /* load, decode, display the image */
951 int load_and_show(char* filename)
953 int status;
954 struct t_disp* p_disp; /* currenly displayed image */
955 int cx, cy; /* view center */
957 #if LCD_DEPTH > 1
958 rb->lcd_set_foreground(LCD_WHITE);
959 rb->lcd_set_background(LCD_BLACK);
960 rb->lcd_set_backdrop(NULL);
961 #endif
962 rb->lcd_clear_display();
964 rb->memset(&disp, 0, sizeof(disp));
965 rb->memset(&jpg, 0, sizeof(jpg)); /* clear info struct */
967 if (rb->button_get(false) == JPEG_MENU)
968 status = PLUGIN_ABORT;
969 else
970 status = load_image(filename, &jpg);
972 if (status == PLUGIN_OUTOFMEM)
974 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
975 if(plug_buf)
977 rb->lcd_setfont(FONT_SYSFIXED);
978 rb->lcd_clear_display();
979 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
980 rb->lcd_puts(0,0,print);
981 rb->lcd_puts(0,1,"Not enough plugin memory!");
982 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
983 if(entries>1)
984 rb->lcd_puts(0,3,"Left/Right: Skip File.");
985 rb->lcd_puts(0,4,"Show Menu: Quit.");
986 rb->lcd_update();
987 rb->lcd_setfont(FONT_UI);
989 rb->button_clear_queue();
991 while (1)
993 int button = rb->button_get(true);
994 switch(button)
996 case JPEG_ZOOM_IN:
997 plug_buf = false;
998 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
999 /*try again this file, now using the audio buffer */
1000 return PLUGIN_OTHER;
1001 #ifdef JPEG_RC_MENU
1002 case JPEG_RC_MENU:
1003 #endif
1004 case JPEG_MENU:
1005 return PLUGIN_OK;
1007 case JPEG_LEFT:
1008 if(entries>1)
1010 rb->lcd_clear_display();
1011 return change_filename(DIR_PREV);
1013 break;
1015 case JPEG_RIGHT:
1016 if(entries>1)
1018 rb->lcd_clear_display();
1019 return change_filename(DIR_NEXT);
1021 break;
1022 default:
1023 if(rb->default_event_handler_ex(button, cleanup, NULL)
1024 == SYS_USB_CONNECTED)
1025 return PLUGIN_USB_CONNECTED;
1030 else
1031 #endif
1033 rb->splash(HZ, "Out of Memory");
1034 file_pt[curfile] = NULL;
1035 return change_filename(direction);
1038 else if (status == PLUGIN_ERROR)
1040 file_pt[curfile] = NULL;
1041 return change_filename(direction);
1043 else if (status == PLUGIN_ABORT) {
1044 rb->splash(HZ, "aborted");
1045 return PLUGIN_OK;
1048 ds_max = max_downscale(&jpg); /* check display constraint */
1049 ds_min = min_downscale(&jpg, buf_images_size); /* check memory constraint */
1050 if (ds_min == 0)
1052 rb->splash(HZ, "too large");
1053 file_pt[curfile] = NULL;
1054 return change_filename(direction);
1056 else if (ds_max < ds_min)
1057 ds_max = ds_min;
1059 ds = ds_max; /* initialize setting */
1060 cx = jpg.x_size/ds/2; /* center the view */
1061 cy = jpg.y_size/ds/2;
1063 do /* loop the image prepare and decoding when zoomed */
1065 p_disp = get_image(&jpg, ds); /* decode or fetch from cache */
1066 if (p_disp == NULL)
1068 file_pt[curfile] = NULL;
1069 return change_filename(direction);
1072 set_view(p_disp, cx, cy);
1074 if(!running_slideshow)
1076 rb->snprintf(print, sizeof(print), "showing %dx%d",
1077 p_disp->width, p_disp->height);
1078 rb->lcd_puts(0, 3, print);
1079 rb->lcd_update();
1082 MYLCD(clear_display)();
1083 draw_image_rect(p_disp, 0, 0,
1084 p_disp->width-p_disp->x, p_disp->height-p_disp->y);
1085 MYLCD_UPDATE();
1087 #ifdef USEGSLIB
1088 grey_show(true); /* switch on greyscale overlay */
1089 #endif
1091 /* drawing is now finished, play around with scrolling
1092 * until you press OFF or connect USB
1094 while (1)
1096 status = scroll_bmp(p_disp);
1097 if (status == ZOOM_IN)
1099 if (ds > ds_min)
1101 ds /= 2; /* reduce downscaling to zoom in */
1102 get_view(p_disp, &cx, &cy);
1103 cx *= 2; /* prepare the position in the new image */
1104 cy *= 2;
1106 else
1107 continue;
1110 if (status == ZOOM_OUT)
1112 if (ds < ds_max)
1114 ds *= 2; /* increase downscaling to zoom out */
1115 get_view(p_disp, &cx, &cy);
1116 cx /= 2; /* prepare the position in the new image */
1117 cy /= 2;
1119 else
1120 continue;
1122 break;
1125 #ifdef USEGSLIB
1126 grey_show(false); /* switch off overlay */
1127 #endif
1128 rb->lcd_clear_display();
1130 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
1131 && status != PLUGIN_OTHER);
1132 #ifdef USEGSLIB
1133 rb->lcd_update();
1134 #endif
1135 return status;
1138 /******************** Plugin entry point *********************/
1140 enum plugin_status plugin_start(const void* parameter)
1142 int condition;
1143 #ifdef USEGSLIB
1144 long greysize; /* helper */
1145 #endif
1146 #if LCD_DEPTH > 1
1147 old_backdrop = rb->lcd_get_backdrop();
1148 #endif
1150 if(!parameter) return PLUGIN_ERROR;
1152 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1153 buf = rb->plugin_get_buffer((size_t *)&buf_size);
1154 #else
1155 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1156 #endif
1158 rb->strcpy(np_file, parameter);
1159 get_pic_list();
1161 if(!entries) return PLUGIN_ERROR;
1163 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1164 if(!rb->audio_status())
1166 plug_buf = false;
1167 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1169 #endif
1171 #ifdef USEGSLIB
1172 if (!grey_init(buf, buf_size, GREY_ON_COP,
1173 LCD_WIDTH, LCD_HEIGHT, &greysize))
1175 rb->splash(HZ, "grey buf error");
1176 return PLUGIN_ERROR;
1178 buf += greysize;
1179 buf_size -= greysize;
1180 #endif
1182 /* should be ok to just load settings since the plugin itself has
1183 just been loaded from disk and the drive should be spinning */
1184 configfile_load(JPEG_CONFIGFILE, jpeg_config,
1185 ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION);
1186 old_settings = jpeg_settings;
1188 /* Turn off backlight timeout */
1189 backlight_force_on(); /* backlight control in lib/helper.c */
1193 condition = load_and_show(np_file);
1194 } while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
1195 && condition != PLUGIN_ERROR);
1197 if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings)))
1199 /* Just in case drive has to spin, keep it from looking locked */
1200 rb->splash(0, "Saving Settings");
1201 configfile_save(JPEG_CONFIGFILE, jpeg_config,
1202 ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION);
1205 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1206 /* set back ata spindown time in case we changed it */
1207 rb->storage_spindown(rb->global_settings->disk_spindown);
1208 #endif
1210 /* Turn on backlight timeout (revert to settings) */
1211 backlight_use_settings(); /* backlight control in lib/helper.c */
1213 #ifdef USEGSLIB
1214 grey_release(); /* deinitialize */
1215 #endif
1217 return condition;