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 static unsigned char* buf
; /* up to here currently used by image(s) */
163 /* the remaining free part of the buffer for compressed+uncompressed images */
164 static unsigned char* buf_images
;
166 static ssize_t buf_size
, buf_images_size
;
167 /* the root of the images, hereafter are decompresed ones */
168 static unsigned char* buf_root
;
169 static int root_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
)
237 if(direct
== DIR_PREV
)
243 curfile
= entries
- 1;
246 }while(file_pt
[curfile
] == NULL
&& count
< entries
);
247 /* we "erase" the file name if we encounter
248 * a non-supported file, so skip it now */
250 else /* DIR_NEXT/DIR_NONE */
255 if(curfile
== entries
- 1)
259 }while(file_pt
[curfile
] == NULL
&& count
< entries
);
264 rb
->splash(HZ
, "No supported files");
267 if(rb
->strlen(tree
->currdir
) > 1)
269 rb
->strcpy(np_file
, tree
->currdir
);
270 rb
->strcat(np_file
, "/");
273 rb
->strcpy(np_file
, tree
->currdir
);
275 rb
->strcat(np_file
, file_pt
[curfile
]);
280 /* switch off overlay, for handling SYS_ events */
281 void cleanup(void *parameter
)
289 #define VSCROLL (LCD_HEIGHT/8)
290 #define HSCROLL (LCD_WIDTH/10)
292 #define ZOOM_IN 100 /* return codes for below function */
295 #ifdef HAVE_LCD_COLOR
296 bool set_option_grayscale(void)
298 bool gray
= jpeg_settings
.colour_mode
== COLOURMODE_GRAY
;
299 rb
->set_bool("Grayscale", &gray
);
300 jpeg_settings
.colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
304 bool set_option_dithering(void)
306 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
307 [DITHER_NONE
] = { "Off", -1 },
308 [DITHER_ORDERED
] = { "Ordered", -1 },
309 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
312 rb
->set_option("Dithering", &jpeg_settings
.dither_mode
, INT
,
313 dithering
, DITHER_NUM_MODES
, NULL
);
317 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
318 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
319 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
320 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
321 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
322 &grayscale_item
, &dithering_item
);
324 static void display_options(void)
326 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
328 #endif /* HAVE_LCD_COLOR */
330 int show_menu(void) /* return 1 to quit */
333 rb
->lcd_set_backdrop(old_backdrop
);
334 #ifdef HAVE_LCD_COLOR
335 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
336 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
338 rb
->lcd_set_foreground(LCD_BLACK
);
339 rb
->lcd_set_background(LCD_WHITE
);
349 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
350 MIID_SHOW_PLAYBACK_MENU
,
352 #ifdef HAVE_LCD_COLOR
353 MIID_DISPLAY_OPTIONS
,
358 MENUITEM_STRINGLIST(menu
, "Jpeg Menu", NULL
,
359 "Return", "Toggle Slideshow Mode",
360 "Change Slideshow Time",
361 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
362 "Show Playback Menu",
364 #ifdef HAVE_LCD_COLOR
369 static const struct opt_items slideshow
[2] = {
374 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
380 case MIID_TOGGLE_SS_MODE
:
381 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
382 slideshow
, 2, NULL
);
384 case MIID_CHANGE_SS_MODE
:
385 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
386 &jpeg_settings
.ss_timeout
, NULL
, 1,
387 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
390 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
391 case MIID_SHOW_PLAYBACK_MENU
:
394 playback_control(NULL
);
398 rb
->splash(HZ
, "Cannot restart playback");
402 #ifdef HAVE_LCD_COLOR
403 case MIID_DISPLAY_OPTIONS
:
412 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
413 /* change ata spindown time based on slideshow time setting */
414 immediate_ata_off
= false;
415 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
417 if (slideshow_enabled
)
419 if(jpeg_settings
.ss_timeout
< 10)
421 /* slideshow times < 10s keep disk spinning */
422 rb
->storage_spindown(0);
424 else if (!rb
->mp3_is_playing())
426 /* slideshow times > 10s and not playing: ata_off after load */
427 immediate_ata_off
= true;
432 rb
->lcd_set_backdrop(NULL
);
433 rb
->lcd_set_foreground(LCD_WHITE
);
434 rb
->lcd_set_background(LCD_BLACK
);
436 rb
->lcd_clear_display();
440 void draw_image_rect(struct t_disp
* pdisp
, int x
, int y
, int width
, int height
)
442 #ifdef HAVE_LCD_COLOR
444 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
445 pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
446 x
+ MAX(0, (LCD_WIDTH
- pdisp
->width
) / 2),
447 y
+ MAX(0, (LCD_HEIGHT
- pdisp
->height
) / 2),
449 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
451 MYXLCD(gray_bitmap_part
)(
452 pdisp
->bitmap
[0], pdisp
->x
+ x
, pdisp
->y
+ y
, pdisp
->stride
,
453 x
+ MAX(0, (LCD_WIDTH
-pdisp
->width
)/2),
454 y
+ MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2),
459 /* Pan the viewing window right - move image to the left and fill in
460 the right-hand side */
461 static void pan_view_right(struct t_disp
* pdisp
)
465 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
468 MYXLCD(scroll_left
)(move
); /* scroll left */
470 draw_image_rect(pdisp
, LCD_WIDTH
- move
, 0, move
, pdisp
->height
-pdisp
->y
);
475 /* Pan the viewing window left - move image to the right and fill in
476 the left-hand side */
477 static void pan_view_left(struct t_disp
* pdisp
)
481 move
= MIN(HSCROLL
, pdisp
->x
);
484 MYXLCD(scroll_right
)(move
); /* scroll right */
486 draw_image_rect(pdisp
, 0, 0, move
, pdisp
->height
-pdisp
->y
);
491 /* Pan the viewing window up - move image down and fill in
493 static void pan_view_up(struct t_disp
* pdisp
)
497 move
= MIN(VSCROLL
, pdisp
->y
);
500 MYXLCD(scroll_down
)(move
); /* scroll down */
502 #ifdef HAVE_LCD_COLOR
503 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
505 /* Draw over the band at the top of the last update
506 caused by lack of error history on line zero. */
507 move
= MIN(move
+ 1, pdisp
->y
+ pdisp
->height
);
510 draw_image_rect(pdisp
, 0, 0, pdisp
->width
-pdisp
->x
, move
);
515 /* Pan the viewing window down - move image up and fill in
517 static void pan_view_down(struct t_disp
* pdisp
)
521 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
524 MYXLCD(scroll_up
)(move
); /* scroll up */
526 #ifdef HAVE_LCD_COLOR
527 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
529 /* Save the line that was on the last line of the display
530 and draw one extra line above then recover the line with
531 image data that had an error history when it was drawn.
534 rb
->memcpy(rgb_linebuf
,
535 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
536 LCD_WIDTH
*sizeof (fb_data
));
540 draw_image_rect(pdisp
, 0, LCD_HEIGHT
- move
, pdisp
->width
-pdisp
->x
, move
);
542 #ifdef HAVE_LCD_COLOR
543 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
545 /* Cover the first row drawn with previous image data. */
546 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
548 LCD_WIDTH
*sizeof (fb_data
));
556 /* interactively scroll around the image */
557 int scroll_bmp(struct t_disp
* pdisp
)
564 if (slideshow_enabled
)
565 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
566 else button
= rb
->button_get(true);
568 running_slideshow
= false;
573 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
574 return change_filename(DIR_PREV
);
575 case JPEG_LEFT
| BUTTON_REPEAT
:
576 pan_view_left(pdisp
);
580 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
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_progess(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
],0, LCD_HEIGHT
-8, LCD_WIDTH
, 8, total
, 0,
682 current
, HORIZONTAL
);
683 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
688 /* in slideshow mode, keep gui interference to a minimum */
689 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-4, LCD_WIDTH
, 4, total
, 0,
690 current
, HORIZONTAL
);
691 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
696 int jpegmem(struct jpeg
*p_jpg
, int ds
)
700 size
= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[0])
701 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[0]);
702 #ifdef HAVE_LCD_COLOR
703 if (p_jpg
->blocks
> 1) /* colour, add requirements for chroma */
705 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[1])
706 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[1]);
707 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[2])
708 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[2]);
714 /* how far can we zoom in without running out of memory */
715 int min_downscale(struct jpeg
*p_jpg
, int bufsize
)
719 if (jpegmem(p_jpg
, 8) > bufsize
)
720 return 0; /* error, too large, even 1:8 doesn't fit */
722 while (downscale
> 1 && jpegmem(p_jpg
, downscale
/2) <= bufsize
)
729 /* how far can we zoom out, to fit image into the LCD */
730 int max_downscale(struct jpeg
*p_jpg
)
734 while (downscale
< 8 && (p_jpg
->x_size
> LCD_WIDTH
*downscale
735 || p_jpg
->y_size
> LCD_HEIGHT
*downscale
))
744 /* return decoded or cached image */
745 struct t_disp
* get_image(struct jpeg
* p_jpg
, int ds
)
747 int w
, h
; /* used to center output */
748 int size
; /* decompressed image size */
749 long time
; /* measured ticks */
752 struct t_disp
* p_disp
= &disp
[ds
]; /* short cut */
754 if (p_disp
->bitmap
[0] != NULL
)
756 return p_disp
; /* we still have it */
759 /* assign image buffer */
761 /* physical size needed for decoding */
762 size
= jpegmem(p_jpg
, ds
);
763 if (buf_size
<= size
)
764 { /* have to discard the current */
767 disp
[i
].bitmap
[0] = NULL
; /* invalidate all bitmaps */
768 buf
= buf_root
; /* start again from the beginning of the buffer */
769 buf_size
= root_size
;
772 #ifdef HAVE_LCD_COLOR
773 if (p_jpg
->blocks
> 1) /* colour jpeg */
777 for (i
= 1; i
< 3; i
++)
779 size
= (p_jpg
->x_phys
/ ds
/ p_jpg
->subsample_x
[i
])
780 * (p_jpg
->y_phys
/ ds
/ p_jpg
->subsample_y
[i
]);
781 p_disp
->bitmap
[i
] = buf
;
785 p_disp
->csub_x
= p_jpg
->subsample_x
[1];
786 p_disp
->csub_y
= p_jpg
->subsample_y
[1];
790 p_disp
->csub_x
= p_disp
->csub_y
= 0;
791 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf
;
794 /* size may be less when decoded (if height is not block aligned) */
795 size
= (p_jpg
->x_phys
/ds
) * (p_jpg
->y_size
/ ds
);
796 p_disp
->bitmap
[0] = buf
;
800 if(!running_slideshow
)
802 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
803 p_jpg
->x_size
/ds
, p_jpg
->y_size
/ds
);
804 rb
->lcd_puts(0, 3, print
);
808 /* update image properties */
809 p_disp
->width
= p_jpg
->x_size
/ ds
;
810 p_disp
->stride
= p_jpg
->x_phys
/ ds
; /* use physical size for stride */
811 p_disp
->height
= p_jpg
->y_size
/ ds
;
813 /* the actual decoding */
814 time
= *rb
->current_tick
;
815 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
817 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
818 rb
->cpu_boost(false);
820 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
824 rb
->splashf(HZ
, "decode error %d", status
);
825 file_pt
[curfile
] = NULL
;
828 time
= *rb
->current_tick
- time
;
830 if(!running_slideshow
)
832 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
833 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
834 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
842 /* set the view to the given center point, limit if necessary */
843 void set_view (struct t_disp
* p_disp
, int cx
, int cy
)
847 /* plain center to available width/height */
848 x
= cx
- MIN(LCD_WIDTH
, p_disp
->width
) / 2;
849 y
= cy
- MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
851 /* limit against upper image size */
852 x
= MIN(p_disp
->width
- LCD_WIDTH
, x
);
853 y
= MIN(p_disp
->height
- LCD_HEIGHT
, y
);
855 /* limit against negative side */
859 p_disp
->x
= x
; /* set the values */
864 /* calculate the view center based on the bitmap position */
865 void get_view(struct t_disp
* p_disp
, int* p_cx
, int* p_cy
)
867 *p_cx
= p_disp
->x
+ MIN(LCD_WIDTH
, p_disp
->width
) / 2;
868 *p_cy
= p_disp
->y
+ MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
872 /* load, decode, display the image */
873 int load_and_show(char* filename
)
877 unsigned char* buf_jpeg
; /* compressed JPEG image */
879 struct t_disp
* p_disp
; /* currenly displayed image */
880 int cx
, cy
; /* view center */
882 fd
= rb
->open(filename
, O_RDONLY
);
885 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
886 rb
->splash(HZ
, print
);
889 filesize
= rb
->filesize(fd
);
890 rb
->memset(&disp
, 0, sizeof(disp
));
892 buf
= buf_images
+ filesize
;
893 buf_size
= buf_images_size
- filesize
;
894 /* allocate JPEG buffer */
895 buf_jpeg
= buf_images
;
897 buf_root
= buf
; /* we can start the decompressed images behind it */
898 root_size
= buf_size
;
903 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
906 rb
->lcd_setfont(FONT_SYSFIXED
);
907 rb
->lcd_clear_display();
908 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
909 rb
->lcd_puts(0,0,print
);
910 rb
->lcd_puts(0,1,"Not enough plugin memory!");
911 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
913 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
914 rb
->lcd_puts(0,4,"Off: Quit.");
916 rb
->lcd_setfont(FONT_UI
);
918 rb
->button_clear_queue();
922 int button
= rb
->button_get(true);
927 buf_images
= rb
->plugin_get_audio_buffer(
928 (size_t *)&buf_images_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_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 buf_images
= buf
; buf_images_size
= buf_size
;
1160 /* Turn off backlight timeout */
1161 backlight_force_on(); /* backlight control in lib/helper.c */
1165 condition
= load_and_show(np_file
);
1166 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
1167 && condition
!= PLUGIN_ERROR
);
1169 if (rb
->memcmp(&jpeg_settings
, &old_settings
, sizeof (jpeg_settings
)))
1171 /* Just in case drive has to spin, keep it from looking locked */
1172 rb
->splash(0, "Saving Settings");
1173 configfile_save(JPEG_CONFIGFILE
, jpeg_config
,
1174 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_VERSION
);
1177 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1178 /* set back ata spindown time in case we changed it */
1179 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1182 /* Turn on backlight timeout (revert to settings) */
1183 backlight_use_settings(); /* backlight control in lib/helper.c */
1186 grey_release(); /* deinitialize */