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 /* support function for qsort() */
189 static int compare(const void* p1
, const void* p2
)
191 return rb
->strcasecmp(*((char **)p1
), *((char **)p2
));
194 bool jpg_ext(const char ext
[])
198 if(!rb
->strcasecmp(ext
,".jpg") ||
199 !rb
->strcasecmp(ext
,".jpe") ||
200 !rb
->strcasecmp(ext
,".jpeg"))
206 /*Read directory contents for scrolling. */
207 void get_pic_list(void)
210 long int str_len
= 0;
212 tree
= rb
->tree_get_context();
214 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
215 file_pt
= rb
->plugin_get_buffer((size_t *)&buf_size
);
217 file_pt
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
220 for(i
= 0; i
< tree
->filesindir
; i
++)
222 if(jpg_ext(rb
->strrchr(&tree
->name_buffer
[str_len
],'.')))
223 file_pt
[entries
++] = &tree
->name_buffer
[str_len
];
225 str_len
+= rb
->strlen(&tree
->name_buffer
[str_len
]) + 1;
228 rb
->qsort(file_pt
, entries
, sizeof(char**), compare
);
230 /* Remove path and leave only the name.*/
231 pname
= rb
->strrchr(np_file
,'/');
234 /* Find Selected File. */
235 for(i
= 0; i
< entries
; i
++)
236 if(!rb
->strcmp(file_pt
[i
], pname
))
240 int change_filename(int direct
)
245 if(direct
== DIR_PREV
)
251 curfile
= entries
- 1;
254 }while(file_pt
[curfile
] == '\0' && count
< entries
);
255 /* we "erase" the file name if we encounter
256 * a non-supported file, so skip it now */
258 else /* DIR_NEXT/DIR_NONE */
263 if(curfile
== entries
- 1)
267 }while(file_pt
[curfile
] == '\0' && count
< entries
);
270 if(count
== entries
&& file_pt
[curfile
] == '\0')
272 rb
->splash(HZ
, "No supported files");
275 if(rb
->strlen(tree
->currdir
) > 1)
277 rb
->strcpy(np_file
, tree
->currdir
);
278 rb
->strcat(np_file
, "/");
281 rb
->strcpy(np_file
, tree
->currdir
);
283 rb
->strcat(np_file
, file_pt
[curfile
]);
288 /* switch off overlay, for handling SYS_ events */
289 void cleanup(void *parameter
)
297 #define VSCROLL (LCD_HEIGHT/8)
298 #define HSCROLL (LCD_WIDTH/10)
300 #define ZOOM_IN 100 /* return codes for below function */
303 #ifdef HAVE_LCD_COLOR
304 bool set_option_grayscale(void)
306 bool gray
= jpeg_settings
.colour_mode
== COLOURMODE_GRAY
;
307 rb
->set_bool("Grayscale", &gray
);
308 jpeg_settings
.colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
312 bool set_option_dithering(void)
314 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
315 [DITHER_NONE
] = { "Off", -1 },
316 [DITHER_ORDERED
] = { "Ordered", -1 },
317 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
320 rb
->set_option("Dithering", &jpeg_settings
.dither_mode
, INT
,
321 dithering
, DITHER_NUM_MODES
, NULL
);
325 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
326 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
327 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
328 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
329 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
330 &grayscale_item
, &dithering_item
);
332 static void display_options(void)
334 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
336 #endif /* HAVE_LCD_COLOR */
338 int show_menu(void) /* return 1 to quit */
341 rb
->lcd_set_backdrop(old_backdrop
);
342 #ifdef HAVE_LCD_COLOR
343 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
344 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
346 rb
->lcd_set_foreground(LCD_BLACK
);
347 rb
->lcd_set_background(LCD_WHITE
);
357 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
358 MIID_SHOW_PLAYBACK_MENU
,
360 #ifdef HAVE_LCD_COLOR
361 MIID_DISPLAY_OPTIONS
,
366 MENUITEM_STRINGLIST(menu
, "Jpeg Menu", NULL
,
367 "Return", "Toggle Slideshow Mode",
368 "Change Slideshow Time",
369 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
370 "Show Playback Menu",
372 #ifdef HAVE_LCD_COLOR
377 static const struct opt_items slideshow
[2] = {
382 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
388 case MIID_TOGGLE_SS_MODE
:
389 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
390 slideshow
, 2, NULL
);
392 case MIID_CHANGE_SS_MODE
:
393 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
394 &jpeg_settings
.ss_timeout
, NULL
, 1,
395 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
398 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
399 case MIID_SHOW_PLAYBACK_MENU
:
402 playback_control(NULL
);
406 rb
->splash(HZ
, "Cannot restart playback");
410 #ifdef HAVE_LCD_COLOR
411 case MIID_DISPLAY_OPTIONS
:
420 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
421 /* change ata spindown time based on slideshow time setting */
422 immediate_ata_off
= false;
423 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
425 if (slideshow_enabled
)
427 if(jpeg_settings
.ss_timeout
< 10)
429 /* slideshow times < 10s keep disk spinning */
430 rb
->storage_spindown(0);
432 else if (!rb
->mp3_is_playing())
434 /* slideshow times > 10s and not playing: ata_off after load */
435 immediate_ata_off
= true;
440 rb
->lcd_set_backdrop(NULL
);
441 rb
->lcd_set_foreground(LCD_WHITE
);
442 rb
->lcd_set_background(LCD_BLACK
);
444 rb
->lcd_clear_display();
448 /* Pan the viewing window right - move image to the left and fill in
449 the right-hand side */
450 static void pan_view_right(struct t_disp
* pdisp
)
454 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
457 MYXLCD(scroll_left
)(move
); /* scroll left */
459 #ifdef HAVE_LCD_COLOR
461 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
462 pdisp
->x
+ LCD_WIDTH
- move
, pdisp
->y
, pdisp
->stride
,
463 LCD_WIDTH
- move
, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
464 move
, MIN(LCD_HEIGHT
, pdisp
->height
), /* w, h */
465 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
467 MYXLCD(gray_bitmap_part
)(
468 pdisp
->bitmap
[0], pdisp
->x
+ LCD_WIDTH
- move
,
469 pdisp
->y
, pdisp
->stride
,
470 LCD_WIDTH
- move
, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
471 move
, MIN(LCD_HEIGHT
, pdisp
->height
)); /* w, h */
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 #ifdef HAVE_LCD_COLOR
490 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
491 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
492 0, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
493 move
, MIN(LCD_HEIGHT
, pdisp
->height
), /* w, h */
494 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
496 MYXLCD(gray_bitmap_part
)(
497 pdisp
->bitmap
[0], pdisp
->x
, pdisp
->y
, pdisp
->stride
,
498 0, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
499 move
, MIN(LCD_HEIGHT
, pdisp
->height
)); /* w, h */
506 /* Pan the viewing window up - move image down and fill in
508 static void pan_view_up(struct t_disp
* pdisp
)
512 move
= MIN(VSCROLL
, pdisp
->y
);
515 MYXLCD(scroll_down
)(move
); /* scroll down */
517 #ifdef HAVE_LCD_COLOR
518 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
520 /* Draw over the band at the top of the last update
521 caused by lack of error history on line zero. */
522 move
= MIN(move
+ 1, pdisp
->y
+ pdisp
->height
);
526 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
527 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
528 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), 0, /* x, y */
529 MIN(LCD_WIDTH
, pdisp
->width
), move
, /* w, h */
530 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
532 MYXLCD(gray_bitmap_part
)(
533 pdisp
->bitmap
[0], pdisp
->x
, pdisp
->y
, pdisp
->stride
,
534 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), 0, /* x, y */
535 MIN(LCD_WIDTH
, pdisp
->width
), move
); /* w, h */
541 /* Pan the viewing window down - move image up and fill in
543 static void pan_view_down(struct t_disp
* pdisp
)
547 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
550 MYXLCD(scroll_up
)(move
); /* scroll up */
552 #ifdef HAVE_LCD_COLOR
553 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
555 /* Save the line that was on the last line of the display
556 and draw one extra line above then recover the line with
557 image data that had an error history when it was drawn.
560 rb
->memcpy(rgb_linebuf
,
561 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
562 LCD_WIDTH
*sizeof (fb_data
));
566 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
, pdisp
->x
,
567 pdisp
->y
+ LCD_HEIGHT
- move
, pdisp
->stride
,
568 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), LCD_HEIGHT
- move
, /* x, y */
569 MIN(LCD_WIDTH
, pdisp
->width
), move
, /* w, h */
570 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
572 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
574 /* Cover the first row drawn with previous image data. */
575 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
577 LCD_WIDTH
*sizeof (fb_data
));
581 MYXLCD(gray_bitmap_part
)(
582 pdisp
->bitmap
[0], pdisp
->x
,
583 pdisp
->y
+ LCD_HEIGHT
- move
, pdisp
->stride
,
584 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), LCD_HEIGHT
- move
, /* x, y */
585 MIN(LCD_WIDTH
, pdisp
->width
), move
); /* w, h */
591 /* interactively scroll around the image */
592 int scroll_bmp(struct t_disp
* pdisp
)
599 if (slideshow_enabled
)
600 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
601 else button
= rb
->button_get(true);
603 running_slideshow
= false;
608 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
609 return change_filename(DIR_PREV
);
610 case JPEG_LEFT
| BUTTON_REPEAT
:
611 pan_view_left(pdisp
);
615 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
616 return change_filename(DIR_NEXT
);
617 case JPEG_RIGHT
| BUTTON_REPEAT
:
618 pan_view_right(pdisp
);
622 case JPEG_UP
| BUTTON_REPEAT
:
627 case JPEG_DOWN
| BUTTON_REPEAT
:
628 pan_view_down(pdisp
);
632 if (!slideshow_enabled
)
634 running_slideshow
= true;
636 return change_filename(DIR_NEXT
);
639 #ifdef JPEG_SLIDE_SHOW
640 case JPEG_SLIDE_SHOW
:
641 slideshow_enabled
= !slideshow_enabled
;
642 running_slideshow
= slideshow_enabled
;
646 #ifdef JPEG_NEXT_REPEAT
647 case JPEG_NEXT_REPEAT
:
651 return change_filename(DIR_NEXT
);
654 #ifdef JPEG_PREVIOUS_REPEAT
655 case JPEG_PREVIOUS_REPEAT
:
659 return change_filename(DIR_PREV
);
664 if (lastbutton
!= JPEG_ZOOM_PRE
)
672 if (lastbutton
!= JPEG_ZOOM_PRE
)
682 grey_show(false); /* switch off greyscale overlay */
684 if (show_menu() == 1)
688 grey_show(true); /* switch on greyscale overlay */
691 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
692 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
693 MAX(0, (LCD_WIDTH
- pdisp
->width
) / 2),
694 MAX(0, (LCD_HEIGHT
- pdisp
->height
) / 2),
695 MIN(LCD_WIDTH
, pdisp
->width
),
696 MIN(LCD_HEIGHT
, pdisp
->height
),
697 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
702 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
703 == SYS_USB_CONNECTED
)
704 return PLUGIN_USB_CONNECTED
;
709 if (button
!= BUTTON_NONE
)
714 /********************* main function *************************/
716 /* callback updating a progress meter while JPEG decoding */
717 void cb_progess(int current
, int total
)
719 rb
->yield(); /* be nice to the other threads */
720 if(!running_slideshow
)
722 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-8, LCD_WIDTH
, 8, total
, 0,
723 current
, HORIZONTAL
);
724 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
729 /* in slideshow mode, keep gui interference to a minimum */
730 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-4, LCD_WIDTH
, 4, total
, 0,
731 current
, HORIZONTAL
);
732 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
737 int jpegmem(struct jpeg
*p_jpg
, int ds
)
741 size
= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[0])
742 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[0]);
743 #ifdef HAVE_LCD_COLOR
744 if (p_jpg
->blocks
> 1) /* colour, add requirements for chroma */
746 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[1])
747 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[1]);
748 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[2])
749 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[2]);
755 /* how far can we zoom in without running out of memory */
756 int min_downscale(struct jpeg
*p_jpg
, int bufsize
)
760 if (jpegmem(p_jpg
, 8) > bufsize
)
761 return 0; /* error, too large, even 1:8 doesn't fit */
763 while (downscale
> 1 && jpegmem(p_jpg
, downscale
/2) <= bufsize
)
770 /* how far can we zoom out, to fit image into the LCD */
771 int max_downscale(struct jpeg
*p_jpg
)
775 while (downscale
< 8 && (p_jpg
->x_size
> LCD_WIDTH
*downscale
776 || p_jpg
->y_size
> LCD_HEIGHT
*downscale
))
785 /* return decoded or cached image */
786 struct t_disp
* get_image(struct jpeg
* p_jpg
, int ds
)
788 int w
, h
; /* used to center output */
789 int size
; /* decompressed image size */
790 long time
; /* measured ticks */
793 struct t_disp
* p_disp
= &disp
[ds
]; /* short cut */
795 if (p_disp
->bitmap
[0] != NULL
)
797 return p_disp
; /* we still have it */
800 /* assign image buffer */
802 /* physical size needed for decoding */
803 size
= jpegmem(p_jpg
, ds
);
804 if (buf_size
<= size
)
805 { /* have to discard the current */
808 disp
[i
].bitmap
[0] = NULL
; /* invalidate all bitmaps */
809 buf
= buf_root
; /* start again from the beginning of the buffer */
810 buf_size
= root_size
;
813 #ifdef HAVE_LCD_COLOR
814 if (p_jpg
->blocks
> 1) /* colour jpeg */
818 for (i
= 1; i
< 3; i
++)
820 size
= (p_jpg
->x_phys
/ ds
/ p_jpg
->subsample_x
[i
])
821 * (p_jpg
->y_phys
/ ds
/ p_jpg
->subsample_y
[i
]);
822 p_disp
->bitmap
[i
] = buf
;
826 p_disp
->csub_x
= p_jpg
->subsample_x
[1];
827 p_disp
->csub_y
= p_jpg
->subsample_y
[1];
831 p_disp
->csub_x
= p_disp
->csub_y
= 0;
832 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf
;
835 /* size may be less when decoded (if height is not block aligned) */
836 size
= (p_jpg
->x_phys
/ds
) * (p_jpg
->y_size
/ ds
);
837 p_disp
->bitmap
[0] = buf
;
841 if(!running_slideshow
)
843 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
844 p_jpg
->x_size
/ds
, p_jpg
->y_size
/ds
);
845 rb
->lcd_puts(0, 3, print
);
849 /* update image properties */
850 p_disp
->width
= p_jpg
->x_size
/ ds
;
851 p_disp
->stride
= p_jpg
->x_phys
/ ds
; /* use physical size for stride */
852 p_disp
->height
= p_jpg
->y_size
/ ds
;
854 /* the actual decoding */
855 time
= *rb
->current_tick
;
856 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
858 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
859 rb
->cpu_boost(false);
861 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
865 rb
->splashf(HZ
, "decode error %d", status
);
866 file_pt
[curfile
] = '\0';
869 time
= *rb
->current_tick
- time
;
871 if(!running_slideshow
)
873 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
874 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
875 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
883 /* set the view to the given center point, limit if necessary */
884 void set_view (struct t_disp
* p_disp
, int cx
, int cy
)
888 /* plain center to available width/height */
889 x
= cx
- MIN(LCD_WIDTH
, p_disp
->width
) / 2;
890 y
= cy
- MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
892 /* limit against upper image size */
893 x
= MIN(p_disp
->width
- LCD_WIDTH
, x
);
894 y
= MIN(p_disp
->height
- LCD_HEIGHT
, y
);
896 /* limit against negative side */
900 p_disp
->x
= x
; /* set the values */
905 /* calculate the view center based on the bitmap position */
906 void get_view(struct t_disp
* p_disp
, int* p_cx
, int* p_cy
)
908 *p_cx
= p_disp
->x
+ MIN(LCD_WIDTH
, p_disp
->width
) / 2;
909 *p_cy
= p_disp
->y
+ MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
913 /* load, decode, display the image */
914 int load_and_show(char* filename
)
918 unsigned char* buf_jpeg
; /* compressed JPEG image */
920 struct t_disp
* p_disp
; /* currenly displayed image */
921 int cx
, cy
; /* view center */
923 fd
= rb
->open(filename
, O_RDONLY
);
926 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
927 rb
->splash(HZ
, print
);
930 filesize
= rb
->filesize(fd
);
931 rb
->memset(&disp
, 0, sizeof(disp
));
933 buf
= buf_images
+ filesize
;
934 buf_size
= buf_images_size
- filesize
;
935 /* allocate JPEG buffer */
936 buf_jpeg
= buf_images
;
938 buf_root
= buf
; /* we can start the decompressed images behind it */
939 root_size
= buf_size
;
943 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
947 rb
->lcd_setfont(FONT_SYSFIXED
);
948 rb
->lcd_clear_display();
949 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
950 rb
->lcd_puts(0,0,print
);
951 rb
->lcd_puts(0,1,"Not enough plugin memory!");
952 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
954 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
955 rb
->lcd_puts(0,4,"Off: Quit.");
957 rb
->lcd_setfont(FONT_UI
);
959 rb
->button_clear_queue();
963 int button
= rb
->button_get(true);
968 buf_images
= rb
->plugin_get_audio_buffer(
969 (size_t *)&buf_images_size
);
970 /*try again this file, now using the audio buffer */
981 rb
->lcd_clear_display();
982 return change_filename(DIR_PREV
);
989 rb
->lcd_clear_display();
990 return change_filename(DIR_NEXT
);
994 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
995 == SYS_USB_CONNECTED
)
996 return PLUGIN_USB_CONNECTED
;
1004 rb
->splash(HZ
, "Out of Memory");
1006 return PLUGIN_ERROR
;
1010 if(!running_slideshow
)
1013 rb
->lcd_set_foreground(LCD_WHITE
);
1014 rb
->lcd_set_background(LCD_BLACK
);
1015 rb
->lcd_set_backdrop(NULL
);
1018 rb
->lcd_clear_display();
1019 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
1020 rb
->lcd_puts(0, 0, print
);
1023 rb
->snprintf(print
, sizeof(print
), "loading %d bytes", filesize
);
1024 rb
->lcd_puts(0, 1, print
);
1028 rb
->read(fd
, buf_jpeg
, filesize
);
1031 if(!running_slideshow
)
1033 rb
->snprintf(print
, sizeof(print
), "decoding markers");
1034 rb
->lcd_puts(0, 2, print
);
1038 else if(immediate_ata_off
)
1040 /* running slideshow and time is long enough: power down disk */
1041 rb
->storage_sleep();
1045 rb
->memset(&jpg
, 0, sizeof(jpg
)); /* clear info struct */
1046 /* process markers, unstuffing */
1047 status
= process_markers(buf_jpeg
, filesize
, &jpg
);
1049 if (status
< 0 || (status
& (DQT
| SOF0
)) != (DQT
| SOF0
))
1050 { /* bad format or minimum components not contained */
1051 rb
->splashf(HZ
, "unsupported %d", status
);
1052 file_pt
[curfile
] = '\0';
1053 return change_filename(direction
);
1056 if (!(status
& DHT
)) /* if no Huffman table present: */
1057 default_huff_tbl(&jpg
); /* use default */
1058 build_lut(&jpg
); /* derive Huffman and other lookup-tables */
1060 if(!running_slideshow
)
1062 rb
->snprintf(print
, sizeof(print
), "image %dx%d", jpg
.x_size
, jpg
.y_size
);
1063 rb
->lcd_puts(0, 2, print
);
1066 ds_max
= max_downscale(&jpg
); /* check display constraint */
1067 ds_min
= min_downscale(&jpg
, buf_size
); /* check memory constraint */
1070 rb
->splash(HZ
, "too large");
1071 file_pt
[curfile
] = '\0';
1072 return change_filename(direction
);
1075 ds
= ds_max
; /* initials setting */
1076 cx
= jpg
.x_size
/ds
/2; /* center the view */
1077 cy
= jpg
.y_size
/ds
/2;
1079 do /* loop the image prepare and decoding when zoomed */
1081 p_disp
= get_image(&jpg
, ds
); /* decode or fetch from cache */
1083 return change_filename(direction
);
1085 set_view(p_disp
, cx
, cy
);
1087 if(!running_slideshow
)
1089 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
1090 p_disp
->width
, p_disp
->height
);
1091 rb
->lcd_puts(0, 3, print
);
1094 MYLCD(clear_display
)();
1095 #ifdef HAVE_LCD_COLOR
1097 p_disp
->bitmap
, p_disp
->csub_x
, p_disp
->csub_y
,
1098 p_disp
->x
, p_disp
->y
, p_disp
->stride
,
1099 MAX(0, (LCD_WIDTH
- p_disp
->width
) / 2),
1100 MAX(0, (LCD_HEIGHT
- p_disp
->height
) / 2),
1101 MIN(LCD_WIDTH
, p_disp
->width
),
1102 MIN(LCD_HEIGHT
, p_disp
->height
),
1103 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
1105 MYXLCD(gray_bitmap_part
)(
1106 p_disp
->bitmap
[0], p_disp
->x
, p_disp
->y
, p_disp
->stride
,
1107 MAX(0, (LCD_WIDTH
- p_disp
->width
) / 2),
1108 MAX(0, (LCD_HEIGHT
- p_disp
->height
) / 2),
1109 MIN(LCD_WIDTH
, p_disp
->width
),
1110 MIN(LCD_HEIGHT
, p_disp
->height
));
1115 grey_show(true); /* switch on greyscale overlay */
1118 /* drawing is now finished, play around with scrolling
1119 * until you press OFF or connect USB
1123 status
= scroll_bmp(p_disp
);
1124 if (status
== ZOOM_IN
)
1128 ds
/= 2; /* reduce downscaling to zoom in */
1129 get_view(p_disp
, &cx
, &cy
);
1130 cx
*= 2; /* prepare the position in the new image */
1137 if (status
== ZOOM_OUT
)
1141 ds
*= 2; /* increase downscaling to zoom out */
1142 get_view(p_disp
, &cx
, &cy
);
1143 cx
/= 2; /* prepare the position in the new image */
1153 grey_show(false); /* switch off overlay */
1155 rb
->lcd_clear_display();
1157 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
1158 && status
!= PLUGIN_OTHER
);
1165 /******************** Plugin entry point *********************/
1167 enum plugin_status
plugin_start(const void* parameter
)
1171 long greysize
; /* helper */
1174 old_backdrop
= rb
->lcd_get_backdrop();
1177 if(!parameter
) return PLUGIN_ERROR
;
1179 rb
->strcpy(np_file
, parameter
);
1182 if(!entries
) return PLUGIN_ERROR
;
1184 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1185 if(rb
->audio_status())
1187 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
) +
1188 (entries
* sizeof(char**));
1189 buf_size
-= (entries
* sizeof(char**));
1193 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1195 buf
= rb
->plugin_get_audio_buffer(&buf_size
) +
1196 (entries
* sizeof(char**));
1197 buf_size
-= (entries
* sizeof(char**));
1201 if (!grey_init(buf
, buf_size
, GREY_ON_COP
,
1202 LCD_WIDTH
, LCD_HEIGHT
, &greysize
))
1204 rb
->splash(HZ
, "grey buf error");
1205 return PLUGIN_ERROR
;
1208 buf_size
-= greysize
;
1211 /* should be ok to just load settings since the plugin itself has
1212 just been loaded from disk and the drive should be spinning */
1213 configfile_load(JPEG_CONFIGFILE
, jpeg_config
,
1214 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_MINVERSION
);
1215 old_settings
= jpeg_settings
;
1217 buf_images
= buf
; buf_images_size
= buf_size
;
1219 /* Turn off backlight timeout */
1220 backlight_force_on(); /* backlight control in lib/helper.c */
1224 condition
= load_and_show(np_file
);
1225 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
1226 && condition
!= PLUGIN_ERROR
);
1228 if (rb
->memcmp(&jpeg_settings
, &old_settings
, sizeof (jpeg_settings
)))
1230 /* Just in case drive has to spin, keep it from looking locked */
1231 rb
->splash(0, "Saving Settings");
1232 configfile_save(JPEG_CONFIGFILE
, jpeg_config
,
1233 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_VERSION
);
1236 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1237 /* set back ata spindown time in case we changed it */
1238 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1241 /* Turn on backlight timeout (revert to settings) */
1242 backlight_use_settings(); /* backlight control in lib/helper.c */
1245 grey_release(); /* deinitialize */