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. */
74 /******************************* Globals ***********************************/
76 static int slideshow_enabled
= false; /* run slideshow */
77 static int running_slideshow
= false; /* loading image because of slideshw */
79 static int immediate_ata_off
= false; /* power down disk after loading */
83 fb_data rgb_linebuf
[LCD_WIDTH
]; /* Line buffer for scrolling when
84 DITHER_DIFFUSION is set */
88 /* Persistent configuration */
89 #define JPEG_CONFIGFILE "jpeg.cfg"
90 #define JPEG_SETTINGS_MINVERSION 1
91 #define JPEG_SETTINGS_VERSION 2
94 #define SS_MIN_TIMEOUT 1
95 #define SS_MAX_TIMEOUT 20
96 #define SS_DEFAULT_TIMEOUT 5
100 #ifdef HAVE_LCD_COLOR
107 static struct jpeg_settings jpeg_settings
=
109 #ifdef HAVE_LCD_COLOR
115 static struct jpeg_settings old_settings
;
117 static struct configdata jpeg_config
[] =
119 #ifdef HAVE_LCD_COLOR
120 { TYPE_ENUM
, 0, COLOUR_NUM_MODES
, { .int_p
= &jpeg_settings
.colour_mode
},
121 "Colour Mode", (char *[]){ "Colour", "Grayscale" } },
122 { TYPE_ENUM
, 0, DITHER_NUM_MODES
, { .int_p
= &jpeg_settings
.dither_mode
},
123 "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" } },
125 { TYPE_INT
, SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
,
126 { .int_p
= &jpeg_settings
.ss_timeout
}, "Slideshow Time", NULL
},
130 static fb_data
* old_backdrop
;
133 /**************** begin Application ********************/
136 /************************* Types ***************************/
140 #ifdef HAVE_LCD_COLOR
141 unsigned char* bitmap
[3]; /* Y, Cr, Cb */
144 unsigned char* bitmap
[1]; /* Y only */
152 /************************* Globals ***************************/
154 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
155 static struct t_disp disp
[9];
157 /* my memory pool (from the mp3 buffer) */
158 static char print
[32]; /* use a common snprintf() buffer */
159 /* the remaining free part of the buffer for compressed+uncompressed images */
160 static unsigned char* buf
;
161 static ssize_t buf_size
;
163 /* the root of the images, hereafter are decompresed ones */
164 static unsigned char* buf_root
;
165 static int root_size
;
167 /* up to here currently used by image(s) */
168 static unsigned char* buf_images
;
169 static ssize_t buf_images_size
;
171 static int ds
, ds_min
, ds_max
; /* downscaling and limits */
172 static struct jpeg jpg
; /* too large for stack */
174 static struct tree_context
*tree
;
176 /* the current full file name */
177 static char np_file
[MAX_PATH
];
178 static int curfile
= 0, direction
= DIR_NONE
, entries
= 0;
180 /* list of the jpeg files */
181 static char **file_pt
;
182 /* are we using the plugin buffer or the audio buffer? */
183 bool plug_buf
= false;
186 /************************* Implementation ***************************/
188 bool jpg_ext(const char ext
[])
192 if(!rb
->strcasecmp(ext
,".jpg") ||
193 !rb
->strcasecmp(ext
,".jpe") ||
194 !rb
->strcasecmp(ext
,".jpeg"))
200 /*Read directory contents for scrolling. */
201 void get_pic_list(void)
204 struct entry
*dircache
;
206 tree
= rb
->tree_get_context();
207 dircache
= tree
->dircache
;
209 file_pt
= (char **) buf
;
211 /* Remove path and leave only the name.*/
212 pname
= rb
->strrchr(np_file
,'/');
215 for (i
= 0; i
< tree
->filesindir
; i
++)
217 if (!(dircache
[i
].attr
& ATTR_DIRECTORY
)
218 && jpg_ext(rb
->strrchr(dircache
[i
].name
,'.')))
220 file_pt
[entries
] = dircache
[i
].name
;
221 /* Set Selected File. */
222 if (!rb
->strcmp(file_pt
[entries
], pname
))
228 buf
+= (entries
* sizeof(char**));
229 buf_size
-= (entries
* sizeof(char**));
232 int change_filename(int direct
)
234 bool file_erased
= (file_pt
[curfile
] == NULL
);
237 curfile
+= (direct
== DIR_PREV
? entries
- 1: 1);
238 if (curfile
>= entries
)
243 /* remove 'erased' file names from list. */
245 for (count
= i
= 0; i
< entries
; i
++)
249 if (file_pt
[i
] != NULL
)
250 file_pt
[count
++] = file_pt
[i
];
257 rb
->splash(HZ
, "No supported files");
261 if (rb
->strlen(tree
->currdir
) > 1)
263 rb
->strcpy(np_file
, tree
->currdir
);
264 rb
->strcat(np_file
, "/");
267 rb
->strcpy(np_file
, tree
->currdir
);
269 rb
->strcat(np_file
, file_pt
[curfile
]);
274 /* switch off overlay, for handling SYS_ events */
275 void cleanup(void *parameter
)
283 #define VSCROLL (LCD_HEIGHT/8)
284 #define HSCROLL (LCD_WIDTH/10)
286 #define ZOOM_IN 100 /* return codes for below function */
289 #ifdef HAVE_LCD_COLOR
290 bool set_option_grayscale(void)
292 bool gray
= jpeg_settings
.colour_mode
== COLOURMODE_GRAY
;
293 rb
->set_bool("Grayscale", &gray
);
294 jpeg_settings
.colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
298 bool set_option_dithering(void)
300 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
301 [DITHER_NONE
] = { "Off", -1 },
302 [DITHER_ORDERED
] = { "Ordered", -1 },
303 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
306 rb
->set_option("Dithering", &jpeg_settings
.dither_mode
, INT
,
307 dithering
, DITHER_NUM_MODES
, NULL
);
311 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
312 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
313 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
314 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
315 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
316 &grayscale_item
, &dithering_item
);
318 static void display_options(void)
320 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
322 #endif /* HAVE_LCD_COLOR */
324 int show_menu(void) /* return 1 to quit */
327 rb
->lcd_set_backdrop(old_backdrop
);
328 #ifdef HAVE_LCD_COLOR
329 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
330 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
332 rb
->lcd_set_foreground(LCD_BLACK
);
333 rb
->lcd_set_background(LCD_WHITE
);
343 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
344 MIID_SHOW_PLAYBACK_MENU
,
346 #ifdef HAVE_LCD_COLOR
347 MIID_DISPLAY_OPTIONS
,
352 MENUITEM_STRINGLIST(menu
, "Jpeg Menu", NULL
,
353 "Return", "Toggle Slideshow Mode",
354 "Change Slideshow Time",
355 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
356 "Show Playback Menu",
358 #ifdef HAVE_LCD_COLOR
363 static const struct opt_items slideshow
[2] = {
368 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
374 case MIID_TOGGLE_SS_MODE
:
375 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
376 slideshow
, 2, NULL
);
378 case MIID_CHANGE_SS_MODE
:
379 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
380 &jpeg_settings
.ss_timeout
, NULL
, 1,
381 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
384 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
385 case MIID_SHOW_PLAYBACK_MENU
:
388 playback_control(NULL
);
392 rb
->splash(HZ
, "Cannot restart playback");
396 #ifdef HAVE_LCD_COLOR
397 case MIID_DISPLAY_OPTIONS
:
406 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
407 /* change ata spindown time based on slideshow time setting */
408 immediate_ata_off
= false;
409 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
411 if (slideshow_enabled
)
413 if(jpeg_settings
.ss_timeout
< 10)
415 /* slideshow times < 10s keep disk spinning */
416 rb
->storage_spindown(0);
418 else if (!rb
->mp3_is_playing())
420 /* slideshow times > 10s and not playing: ata_off after load */
421 immediate_ata_off
= true;
426 rb
->lcd_set_backdrop(NULL
);
427 rb
->lcd_set_foreground(LCD_WHITE
);
428 rb
->lcd_set_background(LCD_BLACK
);
430 rb
->lcd_clear_display();
434 void draw_image_rect(struct t_disp
* pdisp
, int x
, int y
, int width
, int height
)
436 #ifdef HAVE_LCD_COLOR
438 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
439 pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
440 x
+ MAX(0, (LCD_WIDTH
- pdisp
->width
) / 2),
441 y
+ MAX(0, (LCD_HEIGHT
- pdisp
->height
) / 2),
443 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
445 MYXLCD(gray_bitmap_part
)(
446 pdisp
->bitmap
[0], pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
447 x
+ MAX(0, (LCD_WIDTH
-pdisp
->width
)/2),
448 y
+ MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2),
453 /* Pan the viewing window right - move image to the left and fill in
454 the right-hand side */
455 static void pan_view_right(struct t_disp
* pdisp
)
459 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
462 MYXLCD(scroll_left
)(move
); /* scroll left */
464 draw_image_rect(pdisp
, LCD_WIDTH
- move
, 0, move
, pdisp
->height
-pdisp
->y
);
469 /* Pan the viewing window left - move image to the right and fill in
470 the left-hand side */
471 static void pan_view_left(struct t_disp
* pdisp
)
475 move
= MIN(HSCROLL
, pdisp
->x
);
478 MYXLCD(scroll_right
)(move
); /* scroll right */
480 draw_image_rect(pdisp
, 0, 0, move
, pdisp
->height
-pdisp
->y
);
485 /* Pan the viewing window up - move image down and fill in
487 static void pan_view_up(struct t_disp
* pdisp
)
491 move
= MIN(VSCROLL
, pdisp
->y
);
494 MYXLCD(scroll_down
)(move
); /* scroll down */
496 #ifdef HAVE_LCD_COLOR
497 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
499 /* Draw over the band at the top of the last update
500 caused by lack of error history on line zero. */
501 move
= MIN(move
+ 1, pdisp
->y
+ pdisp
->height
);
504 draw_image_rect(pdisp
, 0, 0, pdisp
->width
-pdisp
->x
, move
);
509 /* Pan the viewing window down - move image up and fill in
511 static void pan_view_down(struct t_disp
* pdisp
)
515 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
518 MYXLCD(scroll_up
)(move
); /* scroll up */
520 #ifdef HAVE_LCD_COLOR
521 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
523 /* Save the line that was on the last line of the display
524 and draw one extra line above then recover the line with
525 image data that had an error history when it was drawn.
528 rb
->memcpy(rgb_linebuf
,
529 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
530 LCD_WIDTH
*sizeof (fb_data
));
534 draw_image_rect(pdisp
, 0, LCD_HEIGHT
- move
, pdisp
->width
-pdisp
->x
, move
);
536 #ifdef HAVE_LCD_COLOR
537 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
539 /* Cover the first row drawn with previous image data. */
540 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
542 LCD_WIDTH
*sizeof (fb_data
));
550 /* interactively scroll around the image */
551 int scroll_bmp(struct t_disp
* pdisp
)
558 if (slideshow_enabled
)
559 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
560 else button
= rb
->button_get(true);
562 running_slideshow
= false;
567 if (entries
> 1 && pdisp
->width
<= LCD_WIDTH
568 && pdisp
->height
<= LCD_HEIGHT
)
569 return change_filename(DIR_PREV
);
570 case JPEG_LEFT
| BUTTON_REPEAT
:
571 pan_view_left(pdisp
);
575 if (entries
> 1 && pdisp
->width
<= LCD_WIDTH
576 && pdisp
->height
<= LCD_HEIGHT
)
577 return change_filename(DIR_NEXT
);
578 case JPEG_RIGHT
| BUTTON_REPEAT
:
579 pan_view_right(pdisp
);
583 case JPEG_UP
| BUTTON_REPEAT
:
588 case JPEG_DOWN
| BUTTON_REPEAT
:
589 pan_view_down(pdisp
);
593 if (!slideshow_enabled
)
595 running_slideshow
= true;
597 return change_filename(DIR_NEXT
);
600 #ifdef JPEG_SLIDE_SHOW
601 case JPEG_SLIDE_SHOW
:
602 slideshow_enabled
= !slideshow_enabled
;
603 running_slideshow
= slideshow_enabled
;
607 #ifdef JPEG_NEXT_REPEAT
608 case JPEG_NEXT_REPEAT
:
612 return change_filename(DIR_NEXT
);
615 #ifdef JPEG_PREVIOUS_REPEAT
616 case JPEG_PREVIOUS_REPEAT
:
620 return change_filename(DIR_PREV
);
625 if (lastbutton
!= JPEG_ZOOM_PRE
)
633 if (lastbutton
!= JPEG_ZOOM_PRE
)
643 grey_show(false); /* switch off greyscale overlay */
645 if (show_menu() == 1)
649 grey_show(true); /* switch on greyscale overlay */
651 draw_image_rect(pdisp
, 0, 0,
652 pdisp
->width
-pdisp
->x
, pdisp
->height
-pdisp
->y
);
657 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
658 == SYS_USB_CONNECTED
)
659 return PLUGIN_USB_CONNECTED
;
664 if (button
!= BUTTON_NONE
)
669 /********************* main function *************************/
671 /* callback updating a progress meter while JPEG decoding */
672 void cb_progress(int current
, int total
)
674 rb
->yield(); /* be nice to the other threads */
675 if(!running_slideshow
)
677 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
678 0, LCD_HEIGHT
-8, LCD_WIDTH
, 8,
679 total
, 0, current
, HORIZONTAL
);
680 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
685 /* in slideshow mode, keep gui interference to a minimum */
686 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
687 0, LCD_HEIGHT
-4, LCD_WIDTH
, 4,
688 total
, 0, current
, HORIZONTAL
);
689 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
694 int jpegmem(struct jpeg
*p_jpg
, int ds
)
698 size
= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[0])
699 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[0]);
700 #ifdef HAVE_LCD_COLOR
701 if (p_jpg
->blocks
> 1) /* colour, add requirements for chroma */
703 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[1])
704 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[1]);
705 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[2])
706 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[2]);
712 /* how far can we zoom in without running out of memory */
713 int min_downscale(struct jpeg
*p_jpg
, int bufsize
)
717 if (jpegmem(p_jpg
, 8) > bufsize
)
718 return 0; /* error, too large, even 1:8 doesn't fit */
720 while (downscale
> 1 && jpegmem(p_jpg
, downscale
/2) <= bufsize
)
726 /* how far can we zoom out, to fit image into the LCD */
727 int max_downscale(struct jpeg
*p_jpg
)
731 while (downscale
< 8 && (p_jpg
->x_size
/downscale
> LCD_WIDTH
732 || p_jpg
->y_size
/downscale
> LCD_HEIGHT
))
741 /* return decoded or cached image */
742 struct t_disp
* get_image(struct jpeg
* p_jpg
, int ds
)
744 int w
, h
; /* used to center output */
745 int size
; /* decompressed image size */
746 long time
; /* measured ticks */
749 struct t_disp
* p_disp
= &disp
[ds
]; /* short cut */
751 if (p_disp
->bitmap
[0] != NULL
)
753 return p_disp
; /* we still have it */
756 /* assign image buffer */
758 /* physical size needed for decoding */
759 size
= jpegmem(p_jpg
, ds
);
760 if (buf_images_size
<= size
)
761 { /* have to discard the current */
764 disp
[i
].bitmap
[0] = NULL
; /* invalidate all bitmaps */
765 buf_images
= buf_root
; /* start again from the beginning of the buffer */
766 buf_images_size
= root_size
;
769 #ifdef HAVE_LCD_COLOR
770 if (p_jpg
->blocks
> 1) /* colour jpeg */
774 for (i
= 1; i
< 3; i
++)
776 size
= (p_jpg
->x_phys
/ ds
/ p_jpg
->subsample_x
[i
])
777 * (p_jpg
->y_phys
/ ds
/ p_jpg
->subsample_y
[i
]);
778 p_disp
->bitmap
[i
] = buf_images
;
780 buf_images_size
-= size
;
782 p_disp
->csub_x
= p_jpg
->subsample_x
[1];
783 p_disp
->csub_y
= p_jpg
->subsample_y
[1];
787 p_disp
->csub_x
= p_disp
->csub_y
= 0;
788 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf_images
;
791 /* size may be less when decoded (if height is not block aligned) */
792 size
= (p_jpg
->x_phys
/ds
) * (p_jpg
->y_size
/ ds
);
793 p_disp
->bitmap
[0] = buf_images
;
795 buf_images_size
-= size
;
797 if(!running_slideshow
)
799 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
800 p_jpg
->x_size
/ds
, p_jpg
->y_size
/ds
);
801 rb
->lcd_puts(0, 3, print
);
805 /* update image properties */
806 p_disp
->width
= p_jpg
->x_size
/ ds
;
807 p_disp
->stride
= p_jpg
->x_phys
/ ds
; /* use physical size for stride */
808 p_disp
->height
= p_jpg
->y_size
/ ds
;
810 /* the actual decoding */
811 time
= *rb
->current_tick
;
812 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
814 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progress
);
815 rb
->cpu_boost(false);
817 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progress
);
821 rb
->splashf(HZ
, "decode error %d", status
);
824 time
= *rb
->current_tick
- time
;
826 if(!running_slideshow
)
828 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
829 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
830 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
838 /* set the view to the given center point, limit if necessary */
839 void set_view (struct t_disp
* p_disp
, int cx
, int cy
)
843 /* plain center to available width/height */
844 x
= cx
- MIN(LCD_WIDTH
, p_disp
->width
) / 2;
845 y
= cy
- MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
847 /* limit against upper image size */
848 x
= MIN(p_disp
->width
- LCD_WIDTH
, x
);
849 y
= MIN(p_disp
->height
- LCD_HEIGHT
, y
);
851 /* limit against negative side */
855 p_disp
->x
= x
; /* set the values */
860 /* calculate the view center based on the bitmap position */
861 void get_view(struct t_disp
* p_disp
, int* p_cx
, int* p_cy
)
863 *p_cx
= p_disp
->x
+ MIN(LCD_WIDTH
, p_disp
->width
) / 2;
864 *p_cy
= p_disp
->y
+ MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
868 /* load, decode, display the image */
869 int load_and_show(char* filename
)
873 unsigned char* buf_jpeg
; /* compressed JPEG image */
875 struct t_disp
* p_disp
; /* currenly displayed image */
876 int cx
, cy
; /* view center */
878 fd
= rb
->open(filename
, O_RDONLY
);
881 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
882 rb
->splash(HZ
, print
);
885 filesize
= rb
->filesize(fd
);
886 rb
->memset(&disp
, 0, sizeof(disp
));
888 /* allocate JPEG buffer */
891 /* we can start the decompressed images behind it */
892 buf_images
= buf_root
= buf
+ filesize
;
893 buf_images_size
= root_size
= buf_size
- filesize
;
895 if (buf_images_size
<= 0)
898 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
901 rb
->lcd_setfont(FONT_SYSFIXED
);
902 rb
->lcd_clear_display();
903 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
904 rb
->lcd_puts(0,0,print
);
905 rb
->lcd_puts(0,1,"Not enough plugin memory!");
906 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
908 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
909 rb
->lcd_puts(0,4,"Off: Quit.");
911 rb
->lcd_setfont(FONT_UI
);
913 rb
->button_clear_queue();
917 int button
= rb
->button_get(true);
922 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
923 /*try again this file, now using the audio buffer */
934 rb
->lcd_clear_display();
935 return change_filename(DIR_PREV
);
942 rb
->lcd_clear_display();
943 return change_filename(DIR_NEXT
);
947 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
948 == SYS_USB_CONNECTED
)
949 return PLUGIN_USB_CONNECTED
;
957 rb
->splash(HZ
, "Out of Memory");
962 if(!running_slideshow
)
965 rb
->lcd_set_foreground(LCD_WHITE
);
966 rb
->lcd_set_background(LCD_BLACK
);
967 rb
->lcd_set_backdrop(NULL
);
970 rb
->lcd_clear_display();
971 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
972 rb
->lcd_puts(0, 0, print
);
975 rb
->snprintf(print
, sizeof(print
), "loading %d bytes", filesize
);
976 rb
->lcd_puts(0, 1, print
);
980 rb
->read(fd
, buf_jpeg
, filesize
);
983 if(!running_slideshow
)
985 rb
->snprintf(print
, sizeof(print
), "decoding markers");
986 rb
->lcd_puts(0, 2, print
);
990 else if(immediate_ata_off
)
992 /* running slideshow and time is long enough: power down disk */
997 rb
->memset(&jpg
, 0, sizeof(jpg
)); /* clear info struct */
998 /* process markers, unstuffing */
999 status
= process_markers(buf_jpeg
, filesize
, &jpg
);
1001 if (status
< 0 || (status
& (DQT
| SOF0
)) != (DQT
| SOF0
))
1002 { /* bad format or minimum components not contained */
1003 rb
->splashf(HZ
, "unsupported %d", status
);
1004 file_pt
[curfile
] = NULL
;
1005 return change_filename(direction
);
1008 if (!(status
& DHT
)) /* if no Huffman table present: */
1009 default_huff_tbl(&jpg
); /* use default */
1010 build_lut(&jpg
); /* derive Huffman and other lookup-tables */
1012 if(!running_slideshow
)
1014 rb
->snprintf(print
, sizeof(print
), "image %dx%d", jpg
.x_size
, jpg
.y_size
);
1015 rb
->lcd_puts(0, 2, print
);
1018 ds_max
= max_downscale(&jpg
); /* check display constraint */
1019 ds_min
= min_downscale(&jpg
, buf_images_size
); /* check memory constraint */
1022 rb
->splash(HZ
, "too large");
1023 file_pt
[curfile
] = NULL
;
1024 return change_filename(direction
);
1027 ds
= ds_max
; /* initialize setting */
1028 cx
= jpg
.x_size
/ds
/2; /* center the view */
1029 cy
= jpg
.y_size
/ds
/2;
1031 do /* loop the image prepare and decoding when zoomed */
1033 p_disp
= get_image(&jpg
, ds
); /* decode or fetch from cache */
1036 file_pt
[curfile
] = NULL
;
1037 return change_filename(direction
);
1040 set_view(p_disp
, cx
, cy
);
1042 if(!running_slideshow
)
1044 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
1045 p_disp
->width
, p_disp
->height
);
1046 rb
->lcd_puts(0, 3, print
);
1050 MYLCD(clear_display
)();
1051 draw_image_rect(p_disp
, 0, 0,
1052 p_disp
->width
-p_disp
->x
, p_disp
->height
-p_disp
->y
);
1056 grey_show(true); /* switch on greyscale overlay */
1059 /* drawing is now finished, play around with scrolling
1060 * until you press OFF or connect USB
1064 status
= scroll_bmp(p_disp
);
1065 if (status
== ZOOM_IN
)
1069 ds
/= 2; /* reduce downscaling to zoom in */
1070 get_view(p_disp
, &cx
, &cy
);
1071 cx
*= 2; /* prepare the position in the new image */
1078 if (status
== ZOOM_OUT
)
1082 ds
*= 2; /* increase downscaling to zoom out */
1083 get_view(p_disp
, &cx
, &cy
);
1084 cx
/= 2; /* prepare the position in the new image */
1094 grey_show(false); /* switch off overlay */
1096 rb
->lcd_clear_display();
1098 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
1099 && status
!= PLUGIN_OTHER
);
1106 /******************** Plugin entry point *********************/
1108 enum plugin_status
plugin_start(const void* parameter
)
1112 long greysize
; /* helper */
1115 old_backdrop
= rb
->lcd_get_backdrop();
1118 if(!parameter
) return PLUGIN_ERROR
;
1120 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1121 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
);
1123 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1126 rb
->strcpy(np_file
, parameter
);
1129 if(!entries
) return PLUGIN_ERROR
;
1131 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1132 if(rb
->audio_status())
1135 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1139 if (!grey_init(buf
, buf_size
, GREY_ON_COP
,
1140 LCD_WIDTH
, LCD_HEIGHT
, &greysize
))
1142 rb
->splash(HZ
, "grey buf error");
1143 return PLUGIN_ERROR
;
1146 buf_size
-= greysize
;
1149 /* should be ok to just load settings since the plugin itself has
1150 just been loaded from disk and the drive should be spinning */
1151 configfile_load(JPEG_CONFIGFILE
, jpeg_config
,
1152 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_MINVERSION
);
1153 old_settings
= jpeg_settings
;
1155 /* Turn off backlight timeout */
1156 backlight_force_on(); /* backlight control in lib/helper.c */
1160 condition
= load_and_show(np_file
);
1161 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
1162 && condition
!= PLUGIN_ERROR
);
1164 if (rb
->memcmp(&jpeg_settings
, &old_settings
, sizeof (jpeg_settings
)))
1166 /* Just in case drive has to spin, keep it from looking locked */
1167 rb
->splash(0, "Saving Settings");
1168 configfile_save(JPEG_CONFIGFILE
, jpeg_config
,
1169 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_VERSION
);
1172 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1173 /* set back ata spindown time in case we changed it */
1174 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1177 /* Turn on backlight timeout (revert to settings) */
1178 backlight_use_settings(); /* backlight control in lib/helper.c */
1181 grey_release(); /* deinitialize */