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 #define MAX_X_SIZE LCD_WIDTH*8
60 /* Min memory allowing us to use the plugin buffer
61 * and thus not stopping the music
62 * *Very* rough estimation:
63 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
64 * + 20k code size = 60 000
65 * + 50k min for jpeg = 120 000
67 #define MIN_MEM 120000
74 #define PLUGIN_OTHER 10 /* State code for output with return. */
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 /* are we using the plugin buffer or the audio buffer? */
185 bool plug_buf
= false;
188 /************************* Implementation ***************************/
190 bool jpg_ext(const char ext
[])
194 if(!rb
->strcasecmp(ext
,".jpg") ||
195 !rb
->strcasecmp(ext
,".jpe") ||
196 !rb
->strcasecmp(ext
,".jpeg"))
202 /*Read directory contents for scrolling. */
203 void get_pic_list(void)
206 struct entry
*dircache
;
208 tree
= rb
->tree_get_context();
209 dircache
= tree
->dircache
;
211 file_pt
= (char **) buf
;
213 /* Remove path and leave only the name.*/
214 pname
= rb
->strrchr(np_file
,'/');
217 for (i
= 0; i
< tree
->filesindir
; i
++)
219 if (!(dircache
[i
].attr
& ATTR_DIRECTORY
)
220 && jpg_ext(rb
->strrchr(dircache
[i
].name
,'.')))
222 file_pt
[entries
] = dircache
[i
].name
;
223 /* Set Selected File. */
224 if (!rb
->strcmp(file_pt
[entries
], pname
))
230 buf
+= (entries
* sizeof(char**));
231 buf_size
-= (entries
* sizeof(char**));
234 int change_filename(int direct
)
239 if(direct
== DIR_PREV
)
245 curfile
= entries
- 1;
248 }while(file_pt
[curfile
] == NULL
&& count
< entries
);
249 /* we "erase" the file name if we encounter
250 * a non-supported file, so skip it now */
252 else /* DIR_NEXT/DIR_NONE */
257 if(curfile
== entries
- 1)
261 }while(file_pt
[curfile
] == NULL
&& count
< entries
);
266 rb
->splash(HZ
, "No supported files");
269 if(rb
->strlen(tree
->currdir
) > 1)
271 rb
->strcpy(np_file
, tree
->currdir
);
272 rb
->strcat(np_file
, "/");
275 rb
->strcpy(np_file
, tree
->currdir
);
277 rb
->strcat(np_file
, file_pt
[curfile
]);
282 /* switch off overlay, for handling SYS_ events */
283 void cleanup(void *parameter
)
291 #define VSCROLL (LCD_HEIGHT/8)
292 #define HSCROLL (LCD_WIDTH/10)
294 #define ZOOM_IN 100 /* return codes for below function */
297 #ifdef HAVE_LCD_COLOR
298 bool set_option_grayscale(void)
300 bool gray
= jpeg_settings
.colour_mode
== COLOURMODE_GRAY
;
301 rb
->set_bool("Grayscale", &gray
);
302 jpeg_settings
.colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
306 bool set_option_dithering(void)
308 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
309 [DITHER_NONE
] = { "Off", -1 },
310 [DITHER_ORDERED
] = { "Ordered", -1 },
311 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
314 rb
->set_option("Dithering", &jpeg_settings
.dither_mode
, INT
,
315 dithering
, DITHER_NUM_MODES
, NULL
);
319 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
320 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
321 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
322 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
323 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
324 &grayscale_item
, &dithering_item
);
326 static void display_options(void)
328 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
330 #endif /* HAVE_LCD_COLOR */
332 int show_menu(void) /* return 1 to quit */
335 rb
->lcd_set_backdrop(old_backdrop
);
336 #ifdef HAVE_LCD_COLOR
337 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
338 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
340 rb
->lcd_set_foreground(LCD_BLACK
);
341 rb
->lcd_set_background(LCD_WHITE
);
351 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
352 MIID_SHOW_PLAYBACK_MENU
,
354 #ifdef HAVE_LCD_COLOR
355 MIID_DISPLAY_OPTIONS
,
360 MENUITEM_STRINGLIST(menu
, "Jpeg Menu", NULL
,
361 "Return", "Toggle Slideshow Mode",
362 "Change Slideshow Time",
363 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
364 "Show Playback Menu",
366 #ifdef HAVE_LCD_COLOR
371 static const struct opt_items slideshow
[2] = {
376 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
382 case MIID_TOGGLE_SS_MODE
:
383 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
384 slideshow
, 2, NULL
);
386 case MIID_CHANGE_SS_MODE
:
387 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
388 &jpeg_settings
.ss_timeout
, NULL
, 1,
389 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
392 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
393 case MIID_SHOW_PLAYBACK_MENU
:
396 playback_control(NULL
);
400 rb
->splash(HZ
, "Cannot restart playback");
404 #ifdef HAVE_LCD_COLOR
405 case MIID_DISPLAY_OPTIONS
:
414 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
415 /* change ata spindown time based on slideshow time setting */
416 immediate_ata_off
= false;
417 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
419 if (slideshow_enabled
)
421 if(jpeg_settings
.ss_timeout
< 10)
423 /* slideshow times < 10s keep disk spinning */
424 rb
->storage_spindown(0);
426 else if (!rb
->mp3_is_playing())
428 /* slideshow times > 10s and not playing: ata_off after load */
429 immediate_ata_off
= true;
434 rb
->lcd_set_backdrop(NULL
);
435 rb
->lcd_set_foreground(LCD_WHITE
);
436 rb
->lcd_set_background(LCD_BLACK
);
438 rb
->lcd_clear_display();
442 void draw_image_rect(struct t_disp
* pdisp
, int x
, int y
, int width
, int height
)
444 #ifdef HAVE_LCD_COLOR
446 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
447 pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
448 x
+ MAX(0, (LCD_WIDTH
- pdisp
->width
) / 2),
449 y
+ MAX(0, (LCD_HEIGHT
- pdisp
->height
) / 2),
451 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
453 MYXLCD(gray_bitmap_part
)(
454 pdisp
->bitmap
[0], pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
455 x
+ MAX(0, (LCD_WIDTH
-pdisp
->width
)/2),
456 y
+ MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2),
461 /* Pan the viewing window right - move image to the left and fill in
462 the right-hand side */
463 static void pan_view_right(struct t_disp
* pdisp
)
467 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
470 MYXLCD(scroll_left
)(move
); /* scroll left */
472 draw_image_rect(pdisp
, LCD_WIDTH
- move
, 0, move
, pdisp
->height
-pdisp
->y
);
477 /* Pan the viewing window left - move image to the right and fill in
478 the left-hand side */
479 static void pan_view_left(struct t_disp
* pdisp
)
483 move
= MIN(HSCROLL
, pdisp
->x
);
486 MYXLCD(scroll_right
)(move
); /* scroll right */
488 draw_image_rect(pdisp
, 0, 0, move
, pdisp
->height
-pdisp
->y
);
493 /* Pan the viewing window up - move image down and fill in
495 static void pan_view_up(struct t_disp
* pdisp
)
499 move
= MIN(VSCROLL
, pdisp
->y
);
502 MYXLCD(scroll_down
)(move
); /* scroll down */
504 #ifdef HAVE_LCD_COLOR
505 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
507 /* Draw over the band at the top of the last update
508 caused by lack of error history on line zero. */
509 move
= MIN(move
+ 1, pdisp
->y
+ pdisp
->height
);
512 draw_image_rect(pdisp
, 0, 0, pdisp
->width
-pdisp
->x
, move
);
517 /* Pan the viewing window down - move image up and fill in
519 static void pan_view_down(struct t_disp
* pdisp
)
523 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
526 MYXLCD(scroll_up
)(move
); /* scroll up */
528 #ifdef HAVE_LCD_COLOR
529 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
531 /* Save the line that was on the last line of the display
532 and draw one extra line above then recover the line with
533 image data that had an error history when it was drawn.
536 rb
->memcpy(rgb_linebuf
,
537 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
538 LCD_WIDTH
*sizeof (fb_data
));
542 draw_image_rect(pdisp
, 0, LCD_HEIGHT
- move
, pdisp
->width
-pdisp
->x
, move
);
544 #ifdef HAVE_LCD_COLOR
545 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
547 /* Cover the first row drawn with previous image data. */
548 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
550 LCD_WIDTH
*sizeof (fb_data
));
558 /* interactively scroll around the image */
559 int scroll_bmp(struct t_disp
* pdisp
)
566 if (slideshow_enabled
)
567 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
568 else button
= rb
->button_get(true);
570 running_slideshow
= false;
575 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
576 return change_filename(DIR_PREV
);
577 case JPEG_LEFT
| BUTTON_REPEAT
:
578 pan_view_left(pdisp
);
582 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
583 return change_filename(DIR_NEXT
);
584 case JPEG_RIGHT
| BUTTON_REPEAT
:
585 pan_view_right(pdisp
);
589 case JPEG_UP
| BUTTON_REPEAT
:
594 case JPEG_DOWN
| BUTTON_REPEAT
:
595 pan_view_down(pdisp
);
599 if (!slideshow_enabled
)
601 running_slideshow
= true;
603 return change_filename(DIR_NEXT
);
606 #ifdef JPEG_SLIDE_SHOW
607 case JPEG_SLIDE_SHOW
:
608 slideshow_enabled
= !slideshow_enabled
;
609 running_slideshow
= slideshow_enabled
;
613 #ifdef JPEG_NEXT_REPEAT
614 case JPEG_NEXT_REPEAT
:
618 return change_filename(DIR_NEXT
);
621 #ifdef JPEG_PREVIOUS_REPEAT
622 case JPEG_PREVIOUS_REPEAT
:
626 return change_filename(DIR_PREV
);
631 if (lastbutton
!= JPEG_ZOOM_PRE
)
639 if (lastbutton
!= JPEG_ZOOM_PRE
)
649 grey_show(false); /* switch off greyscale overlay */
651 if (show_menu() == 1)
655 grey_show(true); /* switch on greyscale overlay */
657 draw_image_rect(pdisp
, 0, 0,
658 pdisp
->width
-pdisp
->x
, pdisp
->height
-pdisp
->y
);
663 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
664 == SYS_USB_CONNECTED
)
665 return PLUGIN_USB_CONNECTED
;
670 if (button
!= BUTTON_NONE
)
675 /********************* main function *************************/
677 /* callback updating a progress meter while JPEG decoding */
678 void cb_progess(int current
, int total
)
680 rb
->yield(); /* be nice to the other threads */
681 if(!running_slideshow
)
683 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-8, LCD_WIDTH
, 8, total
, 0,
684 current
, HORIZONTAL
);
685 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
690 /* in slideshow mode, keep gui interference to a minimum */
691 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-4, LCD_WIDTH
, 4, total
, 0,
692 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
)
731 /* how far can we zoom out, to fit image into the LCD */
732 int max_downscale(struct jpeg
*p_jpg
)
736 while (downscale
< 8 && (p_jpg
->x_size
> LCD_WIDTH
*downscale
737 || p_jpg
->y_size
> LCD_HEIGHT
*downscale
))
746 /* return decoded or cached image */
747 struct t_disp
* get_image(struct jpeg
* p_jpg
, int ds
)
749 int w
, h
; /* used to center output */
750 int size
; /* decompressed image size */
751 long time
; /* measured ticks */
754 struct t_disp
* p_disp
= &disp
[ds
]; /* short cut */
756 if (p_disp
->bitmap
[0] != NULL
)
758 return p_disp
; /* we still have it */
761 /* assign image buffer */
763 /* physical size needed for decoding */
764 size
= jpegmem(p_jpg
, ds
);
765 if (buf_images_size
<= size
)
766 { /* have to discard the current */
769 disp
[i
].bitmap
[0] = NULL
; /* invalidate all bitmaps */
770 buf_images
= buf_root
; /* start again from the beginning of the buffer */
771 buf_images_size
= root_size
;
774 #ifdef HAVE_LCD_COLOR
775 if (p_jpg
->blocks
> 1) /* colour jpeg */
779 for (i
= 1; i
< 3; i
++)
781 size
= (p_jpg
->x_phys
/ ds
/ p_jpg
->subsample_x
[i
])
782 * (p_jpg
->y_phys
/ ds
/ p_jpg
->subsample_y
[i
]);
783 p_disp
->bitmap
[i
] = buf_images
;
785 buf_images_size
-= size
;
787 p_disp
->csub_x
= p_jpg
->subsample_x
[1];
788 p_disp
->csub_y
= p_jpg
->subsample_y
[1];
792 p_disp
->csub_x
= p_disp
->csub_y
= 0;
793 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf_images
;
796 /* size may be less when decoded (if height is not block aligned) */
797 size
= (p_jpg
->x_phys
/ds
) * (p_jpg
->y_size
/ ds
);
798 p_disp
->bitmap
[0] = buf_images
;
800 buf_images_size
-= size
;
802 if(!running_slideshow
)
804 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
805 p_jpg
->x_size
/ds
, p_jpg
->y_size
/ds
);
806 rb
->lcd_puts(0, 3, print
);
810 /* update image properties */
811 p_disp
->width
= p_jpg
->x_size
/ ds
;
812 p_disp
->stride
= p_jpg
->x_phys
/ ds
; /* use physical size for stride */
813 p_disp
->height
= p_jpg
->y_size
/ ds
;
815 /* the actual decoding */
816 time
= *rb
->current_tick
;
817 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
819 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
820 rb
->cpu_boost(false);
822 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
826 rb
->splashf(HZ
, "decode error %d", status
);
827 file_pt
[curfile
] = NULL
;
830 time
= *rb
->current_tick
- time
;
832 if(!running_slideshow
)
834 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
835 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
836 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
844 /* set the view to the given center point, limit if necessary */
845 void set_view (struct t_disp
* p_disp
, int cx
, int cy
)
849 /* plain center to available width/height */
850 x
= cx
- MIN(LCD_WIDTH
, p_disp
->width
) / 2;
851 y
= cy
- MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
853 /* limit against upper image size */
854 x
= MIN(p_disp
->width
- LCD_WIDTH
, x
);
855 y
= MIN(p_disp
->height
- LCD_HEIGHT
, y
);
857 /* limit against negative side */
861 p_disp
->x
= x
; /* set the values */
866 /* calculate the view center based on the bitmap position */
867 void get_view(struct t_disp
* p_disp
, int* p_cx
, int* p_cy
)
869 *p_cx
= p_disp
->x
+ MIN(LCD_WIDTH
, p_disp
->width
) / 2;
870 *p_cy
= p_disp
->y
+ MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
874 /* load, decode, display the image */
875 int load_and_show(char* filename
)
879 unsigned char* buf_jpeg
; /* compressed JPEG image */
881 struct t_disp
* p_disp
; /* currenly displayed image */
882 int cx
, cy
; /* view center */
884 fd
= rb
->open(filename
, O_RDONLY
);
887 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
888 rb
->splash(HZ
, print
);
891 filesize
= rb
->filesize(fd
);
892 rb
->memset(&disp
, 0, sizeof(disp
));
894 /* allocate JPEG buffer */
897 /* we can start the decompressed images behind it */
898 buf_images
= buf_root
= buf
+ filesize
;
899 buf_images_size
= root_size
= buf_size
- filesize
;
901 if (buf_images_size
<= 0)
904 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
907 rb
->lcd_setfont(FONT_SYSFIXED
);
908 rb
->lcd_clear_display();
909 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
910 rb
->lcd_puts(0,0,print
);
911 rb
->lcd_puts(0,1,"Not enough plugin memory!");
912 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
914 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
915 rb
->lcd_puts(0,4,"Off: Quit.");
917 rb
->lcd_setfont(FONT_UI
);
919 rb
->button_clear_queue();
923 int button
= rb
->button_get(true);
928 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
929 /*try again this file, now using the audio buffer */
940 rb
->lcd_clear_display();
941 return change_filename(DIR_PREV
);
948 rb
->lcd_clear_display();
949 return change_filename(DIR_NEXT
);
953 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
954 == SYS_USB_CONNECTED
)
955 return PLUGIN_USB_CONNECTED
;
963 rb
->splash(HZ
, "Out of Memory");
968 if(!running_slideshow
)
971 rb
->lcd_set_foreground(LCD_WHITE
);
972 rb
->lcd_set_background(LCD_BLACK
);
973 rb
->lcd_set_backdrop(NULL
);
976 rb
->lcd_clear_display();
977 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
978 rb
->lcd_puts(0, 0, print
);
981 rb
->snprintf(print
, sizeof(print
), "loading %d bytes", filesize
);
982 rb
->lcd_puts(0, 1, print
);
986 rb
->read(fd
, buf_jpeg
, filesize
);
989 if(!running_slideshow
)
991 rb
->snprintf(print
, sizeof(print
), "decoding markers");
992 rb
->lcd_puts(0, 2, print
);
996 else if(immediate_ata_off
)
998 /* running slideshow and time is long enough: power down disk */
1003 rb
->memset(&jpg
, 0, sizeof(jpg
)); /* clear info struct */
1004 /* process markers, unstuffing */
1005 status
= process_markers(buf_jpeg
, filesize
, &jpg
);
1007 if (status
< 0 || (status
& (DQT
| SOF0
)) != (DQT
| SOF0
))
1008 { /* bad format or minimum components not contained */
1009 rb
->splashf(HZ
, "unsupported %d", status
);
1010 file_pt
[curfile
] = NULL
;
1011 return change_filename(direction
);
1014 if (!(status
& DHT
)) /* if no Huffman table present: */
1015 default_huff_tbl(&jpg
); /* use default */
1016 build_lut(&jpg
); /* derive Huffman and other lookup-tables */
1018 if(!running_slideshow
)
1020 rb
->snprintf(print
, sizeof(print
), "image %dx%d", jpg
.x_size
, jpg
.y_size
);
1021 rb
->lcd_puts(0, 2, print
);
1024 ds_max
= max_downscale(&jpg
); /* check display constraint */
1025 ds_min
= min_downscale(&jpg
, buf_images_size
); /* check memory constraint */
1028 rb
->splash(HZ
, "too large");
1029 file_pt
[curfile
] = NULL
;
1030 return change_filename(direction
);
1033 ds
= ds_max
; /* initialize setting */
1034 cx
= jpg
.x_size
/ds
/2; /* center the view */
1035 cy
= jpg
.y_size
/ds
/2;
1037 do /* loop the image prepare and decoding when zoomed */
1039 p_disp
= get_image(&jpg
, ds
); /* decode or fetch from cache */
1041 return change_filename(direction
);
1043 set_view(p_disp
, cx
, cy
);
1045 if(!running_slideshow
)
1047 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
1048 p_disp
->width
, p_disp
->height
);
1049 rb
->lcd_puts(0, 3, print
);
1053 MYLCD(clear_display
)();
1054 draw_image_rect(p_disp
, 0, 0,
1055 p_disp
->width
-p_disp
->x
, p_disp
->height
-p_disp
->y
);
1059 grey_show(true); /* switch on greyscale overlay */
1062 /* drawing is now finished, play around with scrolling
1063 * until you press OFF or connect USB
1067 status
= scroll_bmp(p_disp
);
1068 if (status
== ZOOM_IN
)
1072 ds
/= 2; /* reduce downscaling to zoom in */
1073 get_view(p_disp
, &cx
, &cy
);
1074 cx
*= 2; /* prepare the position in the new image */
1081 if (status
== ZOOM_OUT
)
1085 ds
*= 2; /* increase downscaling to zoom out */
1086 get_view(p_disp
, &cx
, &cy
);
1087 cx
/= 2; /* prepare the position in the new image */
1097 grey_show(false); /* switch off overlay */
1099 rb
->lcd_clear_display();
1101 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
1102 && status
!= PLUGIN_OTHER
);
1109 /******************** Plugin entry point *********************/
1111 enum plugin_status
plugin_start(const void* parameter
)
1115 long greysize
; /* helper */
1118 old_backdrop
= rb
->lcd_get_backdrop();
1121 if(!parameter
) return PLUGIN_ERROR
;
1123 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1124 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
);
1126 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1129 rb
->strcpy(np_file
, parameter
);
1132 if(!entries
) return PLUGIN_ERROR
;
1134 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1135 if(rb
->audio_status())
1138 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1142 if (!grey_init(buf
, buf_size
, GREY_ON_COP
,
1143 LCD_WIDTH
, LCD_HEIGHT
, &greysize
))
1145 rb
->splash(HZ
, "grey buf error");
1146 return PLUGIN_ERROR
;
1149 buf_size
-= greysize
;
1152 /* should be ok to just load settings since the plugin itself has
1153 just been loaded from disk and the drive should be spinning */
1154 configfile_load(JPEG_CONFIGFILE
, jpeg_config
,
1155 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_MINVERSION
);
1156 old_settings
= jpeg_settings
;
1158 /* Turn off backlight timeout */
1159 backlight_force_on(); /* backlight control in lib/helper.c */
1163 condition
= load_and_show(np_file
);
1164 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
1165 && condition
!= PLUGIN_ERROR
);
1167 if (rb
->memcmp(&jpeg_settings
, &old_settings
, sizeof (jpeg_settings
)))
1169 /* Just in case drive has to spin, keep it from looking locked */
1170 rb
->splash(0, "Saving Settings");
1171 configfile_save(JPEG_CONFIGFILE
, jpeg_config
,
1172 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_VERSION
);
1175 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1176 /* set back ata spindown time in case we changed it */
1177 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1180 /* Turn on backlight timeout (revert to settings) */
1181 backlight_use_settings(); /* backlight control in lib/helper.c */
1184 grey_release(); /* deinitialize */