1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
29 #include <lib/playback_control.h>
30 #include <lib/helper.h>
31 #include <lib/configfile.h>
37 #include "jpeg_decoder.h"
45 /* different graphics libraries */
49 #define MYLCD(fn) grey_ub_ ## fn
50 #define MYLCD_UPDATE()
51 #define MYXLCD(fn) grey_ub_ ## fn
53 #define MYLCD(fn) rb->lcd_ ## fn
54 #define MYLCD_UPDATE() rb->lcd_update();
55 #define MYXLCD(fn) xlcd_ ## fn
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
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 */
81 static int immediate_ata_off
= false; /* power down disk after loading */
85 fb_data rgb_linebuf
[LCD_WIDTH
]; /* Line buffer for scrolling when
86 DITHER_DIFFUSION is set */
90 /* Persistent configuration */
91 #define JPEG_CONFIGFILE "jpeg.cfg"
92 #define JPEG_SETTINGS_MINVERSION 1
93 #define JPEG_SETTINGS_VERSION 2
96 #define SS_MIN_TIMEOUT 1
97 #define SS_MAX_TIMEOUT 20
98 #define SS_DEFAULT_TIMEOUT 5
102 #ifdef HAVE_LCD_COLOR
109 static struct jpeg_settings jpeg_settings
=
111 #ifdef HAVE_LCD_COLOR
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" } },
127 { TYPE_INT
, SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
,
128 { .int_p
= &jpeg_settings
.ss_timeout
}, "Slideshow Time", NULL
},
132 static fb_data
* old_backdrop
;
135 /**************** begin Application ********************/
138 /************************* Types ***************************/
142 #ifdef HAVE_LCD_COLOR
143 unsigned char* bitmap
[3]; /* Y, Cr, Cb */
146 unsigned char* bitmap
[1]; /* Y only */
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;
190 /************************* Implementation ***************************/
192 bool jpg_ext(const char ext
[])
196 if(!rb
->strcasecmp(ext
,".jpg") ||
197 !rb
->strcasecmp(ext
,".jpe") ||
198 !rb
->strcasecmp(ext
,".jpeg"))
204 /*Read directory contents for scrolling. */
205 void get_pic_list(void)
208 struct entry
*dircache
;
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
,'/');
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
))
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
);
241 curfile
+= (direct
== DIR_PREV
? entries
- 1: 1);
242 if (curfile
>= entries
)
247 /* remove 'erased' file names from list. */
249 for (count
= i
= 0; i
< entries
; i
++)
253 if (file_pt
[i
] != NULL
)
254 file_pt
[count
++] = file_pt
[i
];
261 rb
->splash(HZ
, "No supported files");
265 if (rb
->strlen(tree
->currdir
) > 1)
267 rb
->strcpy(np_file
, tree
->currdir
);
268 rb
->strcat(np_file
, "/");
271 rb
->strcpy(np_file
, tree
->currdir
);
273 rb
->strcat(np_file
, file_pt
[curfile
]);
278 /* switch off overlay, for handling SYS_ events */
279 void cleanup(void *parameter
)
287 #define VSCROLL (LCD_HEIGHT/8)
288 #define HSCROLL (LCD_WIDTH/10)
290 #define ZOOM_IN 100 /* return codes for below function */
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
;
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
);
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 */
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
);
336 rb
->lcd_set_foreground(LCD_BLACK
);
337 rb
->lcd_set_background(LCD_WHITE
);
347 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
348 MIID_SHOW_PLAYBACK_MENU
,
350 #ifdef HAVE_LCD_COLOR
351 MIID_DISPLAY_OPTIONS
,
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",
362 #ifdef HAVE_LCD_COLOR
367 static const struct opt_items slideshow
[2] = {
372 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
378 case MIID_TOGGLE_SS_MODE
:
379 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
380 slideshow
, 2, NULL
);
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
);
388 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
389 case MIID_SHOW_PLAYBACK_MENU
:
392 playback_control(NULL
);
396 rb
->splash(HZ
, "Cannot restart playback");
400 #ifdef HAVE_LCD_COLOR
401 case MIID_DISPLAY_OPTIONS
:
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;
430 rb
->lcd_set_backdrop(NULL
);
431 rb
->lcd_set_foreground(LCD_WHITE
);
432 rb
->lcd_set_background(LCD_BLACK
);
434 rb
->lcd_clear_display();
438 void draw_image_rect(struct t_disp
* pdisp
, int x
, int y
, int width
, int height
)
440 #ifdef HAVE_LCD_COLOR
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),
447 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
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),
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
)
463 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
466 MYXLCD(scroll_left
)(move
); /* scroll left */
468 draw_image_rect(pdisp
, LCD_WIDTH
- move
, 0, move
, pdisp
->height
-pdisp
->y
);
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
)
479 move
= MIN(HSCROLL
, pdisp
->x
);
482 MYXLCD(scroll_right
)(move
); /* scroll right */
484 draw_image_rect(pdisp
, 0, 0, move
, pdisp
->height
-pdisp
->y
);
489 /* Pan the viewing window up - move image down and fill in
491 static void pan_view_up(struct t_disp
* pdisp
)
495 move
= MIN(VSCROLL
, pdisp
->y
);
498 MYXLCD(scroll_down
)(move
); /* scroll down */
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
);
508 draw_image_rect(pdisp
, 0, 0, pdisp
->width
-pdisp
->x
, move
);
513 /* Pan the viewing window down - move image up and fill in
515 static void pan_view_down(struct t_disp
* pdisp
)
519 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
522 MYXLCD(scroll_up
)(move
); /* scroll up */
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.
532 rb
->memcpy(rgb_linebuf
,
533 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
534 LCD_WIDTH
*sizeof (fb_data
));
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
));
553 /* interactively scroll around the image */
554 int scroll_bmp(struct t_disp
* pdisp
)
561 if (slideshow_enabled
)
562 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
564 button
= rb
->button_get(true);
566 running_slideshow
= false;
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
);
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
);
587 case JPEG_UP
| BUTTON_REPEAT
:
592 case JPEG_DOWN
| BUTTON_REPEAT
:
593 pan_view_down(pdisp
);
597 if (!slideshow_enabled
)
599 running_slideshow
= true;
601 return change_filename(DIR_NEXT
);
604 #ifdef JPEG_SLIDE_SHOW
605 case JPEG_SLIDE_SHOW
:
606 slideshow_enabled
= !slideshow_enabled
;
607 running_slideshow
= slideshow_enabled
;
611 #ifdef JPEG_NEXT_REPEAT
612 case JPEG_NEXT_REPEAT
:
616 return change_filename(DIR_NEXT
);
619 #ifdef JPEG_PREVIOUS_REPEAT
620 case JPEG_PREVIOUS_REPEAT
:
624 return change_filename(DIR_PREV
);
629 if (lastbutton
!= JPEG_ZOOM_PRE
)
637 if (lastbutton
!= JPEG_ZOOM_PRE
)
647 grey_show(false); /* switch off greyscale overlay */
649 if (show_menu() == 1)
653 grey_show(true); /* switch on greyscale overlay */
655 draw_image_rect(pdisp
, 0, 0,
656 pdisp
->width
-pdisp
->x
, pdisp
->height
-pdisp
->y
);
661 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
662 == SYS_USB_CONNECTED
)
663 return PLUGIN_USB_CONNECTED
;
668 if (button
!= BUTTON_NONE
)
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);
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);
698 int jpegmem(struct jpeg
*p_jpg
, int ds
)
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]);
716 /* how far can we zoom in without running out of memory */
717 int min_downscale(struct jpeg
*p_jpg
, int bufsize
)
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
)
730 /* how far can we zoom out, to fit image into the LCD */
731 int max_downscale(struct jpeg
*p_jpg
)
735 while (downscale
< 8 && (p_jpg
->x_size
/downscale
> LCD_WIDTH
736 || p_jpg
->y_size
/downscale
> LCD_HEIGHT
))
744 /* load image from filename. */
745 int load_image(char* filename
, struct jpeg
*p_jpg
)
749 unsigned char* buf_jpeg
; /* compressed JPEG image */
752 fd
= rb
->open(filename
, O_RDONLY
);
755 rb
->splashf(HZ
, "err opening %s:%d", filename
, fd
);
758 filesize
= rb
->filesize(fd
);
760 /* allocate JPEG buffer */
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)
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
);
779 rb
->snprintf(print
, sizeof(print
), "loading %d bytes", filesize
);
780 rb
->lcd_puts(0, 1, print
);
784 rb
->read(fd
, buf_jpeg
, filesize
);
787 if(!running_slideshow
)
789 rb
->snprintf(print
, sizeof(print
), "decoding markers");
790 rb
->lcd_puts(0, 2, print
);
794 else if(immediate_ata_off
)
796 /* running slideshow and time is long enough: power down disk */
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
);
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
);
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 */
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 */
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 */
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
;
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];
871 p_disp
->csub_x
= p_disp
->csub_y
= 0;
872 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf_images
;
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
;
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
);
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
898 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progress
);
899 rb
->cpu_boost(false);
901 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progress
);
905 rb
->splashf(HZ
, "decode error %d", status
);
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
);
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
)
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 */
939 p_disp
->x
= x
; /* set the values */
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
)
954 struct t_disp
* p_disp
; /* currenly displayed image */
955 int cx
, cy
; /* view center */
958 rb
->lcd_set_foreground(LCD_WHITE
);
959 rb
->lcd_set_background(LCD_BLACK
);
960 rb
->lcd_set_backdrop(NULL
);
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
;
970 status
= load_image(filename
, &jpg
);
972 if (status
== PLUGIN_OUTOFMEM
)
974 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
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.");
984 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
985 rb
->lcd_puts(0,4,"Show Menu: Quit.");
987 rb
->lcd_setfont(FONT_UI
);
989 rb
->button_clear_queue();
993 int button
= rb
->button_get(true);
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
;
1010 rb
->lcd_clear_display();
1011 return change_filename(DIR_PREV
);
1018 rb
->lcd_clear_display();
1019 return change_filename(DIR_NEXT
);
1023 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
1024 == SYS_USB_CONNECTED
)
1025 return PLUGIN_USB_CONNECTED
;
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");
1048 ds_max
= max_downscale(&jpg
); /* check display constraint */
1049 ds_min
= min_downscale(&jpg
, buf_images_size
); /* check memory constraint */
1052 rb
->splash(HZ
, "too large");
1053 file_pt
[curfile
] = NULL
;
1054 return change_filename(direction
);
1056 else if (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 */
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
);
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
);
1088 grey_show(true); /* switch on greyscale overlay */
1091 /* drawing is now finished, play around with scrolling
1092 * until you press OFF or connect USB
1096 status
= scroll_bmp(p_disp
);
1097 if (status
== ZOOM_IN
)
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 */
1110 if (status
== ZOOM_OUT
)
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 */
1126 grey_show(false); /* switch off overlay */
1128 rb
->lcd_clear_display();
1130 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
1131 && status
!= PLUGIN_OTHER
);
1138 /******************** Plugin entry point *********************/
1140 enum plugin_status
plugin_start(const void* parameter
)
1144 long greysize
; /* helper */
1147 old_backdrop
= rb
->lcd_get_backdrop();
1150 if(!parameter
) return PLUGIN_ERROR
;
1152 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1153 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
);
1155 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1158 rb
->strcpy(np_file
, parameter
);
1161 if(!entries
) return PLUGIN_ERROR
;
1163 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1164 if(!rb
->audio_status())
1167 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
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
;
1179 buf_size
-= greysize
;
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
);
1210 /* Turn on backlight timeout (revert to settings) */
1211 backlight_use_settings(); /* backlight control in lib/helper.c */
1214 grey_release(); /* deinitialize */