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 static void cleanup(UNUSED_ATTR
void *parameter
)
296 #define VSCROLL (LCD_HEIGHT/8)
297 #define HSCROLL (LCD_WIDTH/10)
299 #define ZOOM_IN 100 /* return codes for below function */
302 #ifdef HAVE_LCD_COLOR
303 bool set_option_grayscale(void)
305 bool gray
= jpeg_settings
.colour_mode
== COLOURMODE_GRAY
;
306 rb
->set_bool("Grayscale", &gray
);
307 jpeg_settings
.colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
311 bool set_option_dithering(void)
313 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
314 [DITHER_NONE
] = { "Off", -1 },
315 [DITHER_ORDERED
] = { "Ordered", -1 },
316 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
319 rb
->set_option("Dithering", &jpeg_settings
.dither_mode
, INT
,
320 dithering
, DITHER_NUM_MODES
, NULL
);
324 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
325 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
326 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
327 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
328 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
329 &grayscale_item
, &dithering_item
);
331 static void display_options(void)
333 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
335 #endif /* HAVE_LCD_COLOR */
337 int show_menu(void) /* return 1 to quit */
340 rb
->lcd_set_backdrop(old_backdrop
);
341 #ifdef HAVE_LCD_COLOR
342 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
343 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
345 rb
->lcd_set_foreground(LCD_BLACK
);
346 rb
->lcd_set_background(LCD_WHITE
);
356 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
357 MIID_SHOW_PLAYBACK_MENU
,
359 #ifdef HAVE_LCD_COLOR
360 MIID_DISPLAY_OPTIONS
,
365 MENUITEM_STRINGLIST(menu
, "Jpeg Menu", NULL
,
366 "Return", "Toggle Slideshow Mode",
367 "Change Slideshow Time",
368 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
369 "Show Playback Menu",
371 #ifdef HAVE_LCD_COLOR
376 static const struct opt_items slideshow
[2] = {
381 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
387 case MIID_TOGGLE_SS_MODE
:
388 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
389 slideshow
, 2, NULL
);
391 case MIID_CHANGE_SS_MODE
:
392 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
393 &jpeg_settings
.ss_timeout
, NULL
, 1,
394 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
397 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
398 case MIID_SHOW_PLAYBACK_MENU
:
401 playback_control(NULL
);
405 rb
->splash(HZ
, "Cannot restart playback");
409 #ifdef HAVE_LCD_COLOR
410 case MIID_DISPLAY_OPTIONS
:
419 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
420 /* change ata spindown time based on slideshow time setting */
421 immediate_ata_off
= false;
422 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
424 if (slideshow_enabled
)
426 if(jpeg_settings
.ss_timeout
< 10)
428 /* slideshow times < 10s keep disk spinning */
429 rb
->storage_spindown(0);
431 else if (!rb
->mp3_is_playing())
433 /* slideshow times > 10s and not playing: ata_off after load */
434 immediate_ata_off
= true;
439 rb
->lcd_set_backdrop(NULL
);
440 rb
->lcd_set_foreground(LCD_WHITE
);
441 rb
->lcd_set_background(LCD_BLACK
);
443 rb
->lcd_clear_display();
447 /* Pan the viewing window right - move image to the left and fill in
448 the right-hand side */
449 static void pan_view_right(struct t_disp
* pdisp
)
453 move
= MIN(HSCROLL
, pdisp
->width
- pdisp
->x
- LCD_WIDTH
);
456 MYXLCD(scroll_left
)(move
); /* scroll left */
458 #ifdef HAVE_LCD_COLOR
460 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
461 pdisp
->x
+ LCD_WIDTH
- move
, pdisp
->y
, pdisp
->stride
,
462 LCD_WIDTH
- move
, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
463 move
, MIN(LCD_HEIGHT
, pdisp
->height
), /* w, h */
464 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
466 MYXLCD(gray_bitmap_part
)(
467 pdisp
->bitmap
[0], pdisp
->x
+ LCD_WIDTH
- move
,
468 pdisp
->y
, pdisp
->stride
,
469 LCD_WIDTH
- move
, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
470 move
, MIN(LCD_HEIGHT
, pdisp
->height
)); /* w, h */
476 /* Pan the viewing window left - move image to the right and fill in
477 the left-hand side */
478 static void pan_view_left(struct t_disp
* pdisp
)
482 move
= MIN(HSCROLL
, pdisp
->x
);
485 MYXLCD(scroll_right
)(move
); /* scroll right */
487 #ifdef HAVE_LCD_COLOR
489 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
490 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
491 0, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
492 move
, MIN(LCD_HEIGHT
, pdisp
->height
), /* w, h */
493 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
495 MYXLCD(gray_bitmap_part
)(
496 pdisp
->bitmap
[0], pdisp
->x
, pdisp
->y
, pdisp
->stride
,
497 0, MAX(0, (LCD_HEIGHT
-pdisp
->height
)/2), /* x, y */
498 move
, MIN(LCD_HEIGHT
, pdisp
->height
)); /* w, h */
505 /* Pan the viewing window up - move image down and fill in
507 static void pan_view_up(struct t_disp
* pdisp
)
511 move
= MIN(VSCROLL
, pdisp
->y
);
514 MYXLCD(scroll_down
)(move
); /* scroll down */
516 #ifdef HAVE_LCD_COLOR
517 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
519 /* Draw over the band at the top of the last update
520 caused by lack of error history on line zero. */
521 move
= MIN(move
+ 1, pdisp
->y
+ pdisp
->height
);
525 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
526 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
527 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), 0, /* x, y */
528 MIN(LCD_WIDTH
, pdisp
->width
), move
, /* w, h */
529 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
531 MYXLCD(gray_bitmap_part
)(
532 pdisp
->bitmap
[0], pdisp
->x
, pdisp
->y
, pdisp
->stride
,
533 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), 0, /* x, y */
534 MIN(LCD_WIDTH
, pdisp
->width
), move
); /* w, h */
540 /* Pan the viewing window down - move image up and fill in
542 static void pan_view_down(struct t_disp
* pdisp
)
546 move
= MIN(VSCROLL
, pdisp
->height
- pdisp
->y
- LCD_HEIGHT
);
549 MYXLCD(scroll_up
)(move
); /* scroll up */
551 #ifdef HAVE_LCD_COLOR
552 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
554 /* Save the line that was on the last line of the display
555 and draw one extra line above then recover the line with
556 image data that had an error history when it was drawn.
559 rb
->memcpy(rgb_linebuf
,
560 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
561 LCD_WIDTH
*sizeof (fb_data
));
565 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
, pdisp
->x
,
566 pdisp
->y
+ LCD_HEIGHT
- move
, pdisp
->stride
,
567 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), LCD_HEIGHT
- move
, /* x, y */
568 MIN(LCD_WIDTH
, pdisp
->width
), move
, /* w, h */
569 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
571 if (jpeg_settings
.dither_mode
== DITHER_DIFFUSION
)
573 /* Cover the first row drawn with previous image data. */
574 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
576 LCD_WIDTH
*sizeof (fb_data
));
580 MYXLCD(gray_bitmap_part
)(
581 pdisp
->bitmap
[0], pdisp
->x
,
582 pdisp
->y
+ LCD_HEIGHT
- move
, pdisp
->stride
,
583 MAX(0, (LCD_WIDTH
-pdisp
->width
)/2), LCD_HEIGHT
- move
, /* x, y */
584 MIN(LCD_WIDTH
, pdisp
->width
), move
); /* w, h */
590 /* interactively scroll around the image */
591 int scroll_bmp(struct t_disp
* pdisp
)
598 if (slideshow_enabled
)
599 button
= rb
->button_get_w_tmo(jpeg_settings
.ss_timeout
* HZ
);
600 else button
= rb
->button_get(true);
602 running_slideshow
= false;
607 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
608 return change_filename(DIR_PREV
);
609 case JPEG_LEFT
| BUTTON_REPEAT
:
610 pan_view_left(pdisp
);
614 if (!(ds
< ds_max
) && entries
> 0 && jpg
.x_size
<= MAX_X_SIZE
)
615 return change_filename(DIR_NEXT
);
616 case JPEG_RIGHT
| BUTTON_REPEAT
:
617 pan_view_right(pdisp
);
621 case JPEG_UP
| BUTTON_REPEAT
:
626 case JPEG_DOWN
| BUTTON_REPEAT
:
627 pan_view_down(pdisp
);
631 if (!slideshow_enabled
)
633 running_slideshow
= true;
635 return change_filename(DIR_NEXT
);
638 #ifdef JPEG_SLIDE_SHOW
639 case JPEG_SLIDE_SHOW
:
640 slideshow_enabled
= !slideshow_enabled
;
641 running_slideshow
= slideshow_enabled
;
645 #ifdef JPEG_NEXT_REPEAT
646 case JPEG_NEXT_REPEAT
:
650 return change_filename(DIR_NEXT
);
653 #ifdef JPEG_PREVIOUS_REPEAT
654 case JPEG_PREVIOUS_REPEAT
:
658 return change_filename(DIR_PREV
);
663 if (lastbutton
!= JPEG_ZOOM_PRE
)
671 if (lastbutton
!= JPEG_ZOOM_PRE
)
681 grey_show(false); /* switch off greyscale overlay */
683 if (show_menu() == 1)
687 grey_show(true); /* switch on greyscale overlay */
690 pdisp
->bitmap
, pdisp
->csub_x
, pdisp
->csub_y
,
691 pdisp
->x
, pdisp
->y
, pdisp
->stride
,
692 MAX(0, (LCD_WIDTH
- pdisp
->width
) / 2),
693 MAX(0, (LCD_HEIGHT
- pdisp
->height
) / 2),
694 MIN(LCD_WIDTH
, pdisp
->width
),
695 MIN(LCD_HEIGHT
, pdisp
->height
),
696 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
701 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
702 == SYS_USB_CONNECTED
)
703 return PLUGIN_USB_CONNECTED
;
708 if (button
!= BUTTON_NONE
)
713 /********************* main function *************************/
715 /* callback updating a progress meter while JPEG decoding */
716 void cb_progess(int current
, int total
)
718 rb
->yield(); /* be nice to the other threads */
719 if(!running_slideshow
)
721 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-8, LCD_WIDTH
, 8, total
, 0,
722 current
, HORIZONTAL
);
723 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
728 /* in slideshow mode, keep gui interference to a minimum */
729 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, LCD_HEIGHT
-4, LCD_WIDTH
, 4, total
, 0,
730 current
, HORIZONTAL
);
731 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
736 int jpegmem(struct jpeg
*p_jpg
, int ds
)
740 size
= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[0])
741 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[0]);
742 #ifdef HAVE_LCD_COLOR
743 if (p_jpg
->blocks
> 1) /* colour, add requirements for chroma */
745 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[1])
746 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[1]);
747 size
+= (p_jpg
->x_phys
/ds
/p_jpg
->subsample_x
[2])
748 * (p_jpg
->y_phys
/ds
/p_jpg
->subsample_y
[2]);
754 /* how far can we zoom in without running out of memory */
755 int min_downscale(struct jpeg
*p_jpg
, int bufsize
)
759 if (jpegmem(p_jpg
, 8) > bufsize
)
760 return 0; /* error, too large, even 1:8 doesn't fit */
762 while (downscale
> 1 && jpegmem(p_jpg
, downscale
/2) <= bufsize
)
769 /* how far can we zoom out, to fit image into the LCD */
770 int max_downscale(struct jpeg
*p_jpg
)
774 while (downscale
< 8 && (p_jpg
->x_size
> LCD_WIDTH
*downscale
775 || p_jpg
->y_size
> LCD_HEIGHT
*downscale
))
784 /* return decoded or cached image */
785 struct t_disp
* get_image(struct jpeg
* p_jpg
, int ds
)
787 int w
, h
; /* used to center output */
788 int size
; /* decompressed image size */
789 long time
; /* measured ticks */
792 struct t_disp
* p_disp
= &disp
[ds
]; /* short cut */
794 if (p_disp
->bitmap
[0] != NULL
)
796 return p_disp
; /* we still have it */
799 /* assign image buffer */
801 /* physical size needed for decoding */
802 size
= jpegmem(p_jpg
, ds
);
803 if (buf_size
<= size
)
804 { /* have to discard the current */
807 disp
[i
].bitmap
[0] = NULL
; /* invalidate all bitmaps */
808 buf
= buf_root
; /* start again from the beginning of the buffer */
809 buf_size
= root_size
;
812 #ifdef HAVE_LCD_COLOR
813 if (p_jpg
->blocks
> 1) /* colour jpeg */
817 for (i
= 1; i
< 3; i
++)
819 size
= (p_jpg
->x_phys
/ ds
/ p_jpg
->subsample_x
[i
])
820 * (p_jpg
->y_phys
/ ds
/ p_jpg
->subsample_y
[i
]);
821 p_disp
->bitmap
[i
] = buf
;
825 p_disp
->csub_x
= p_jpg
->subsample_x
[1];
826 p_disp
->csub_y
= p_jpg
->subsample_y
[1];
830 p_disp
->csub_x
= p_disp
->csub_y
= 0;
831 p_disp
->bitmap
[1] = p_disp
->bitmap
[2] = buf
;
834 /* size may be less when decoded (if height is not block aligned) */
835 size
= (p_jpg
->x_phys
/ds
) * (p_jpg
->y_size
/ ds
);
836 p_disp
->bitmap
[0] = buf
;
840 if(!running_slideshow
)
842 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
843 p_jpg
->x_size
/ds
, p_jpg
->y_size
/ds
);
844 rb
->lcd_puts(0, 3, print
);
848 /* update image properties */
849 p_disp
->width
= p_jpg
->x_size
/ ds
;
850 p_disp
->stride
= p_jpg
->x_phys
/ ds
; /* use physical size for stride */
851 p_disp
->height
= p_jpg
->y_size
/ ds
;
853 /* the actual decoding */
854 time
= *rb
->current_tick
;
855 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
857 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
858 rb
->cpu_boost(false);
860 status
= jpeg_decode(p_jpg
, p_disp
->bitmap
, ds
, cb_progess
);
864 rb
->splashf(HZ
, "decode error %d", status
);
865 file_pt
[curfile
] = '\0';
868 time
= *rb
->current_tick
- time
;
870 if(!running_slideshow
)
872 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
873 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
874 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
882 /* set the view to the given center point, limit if necessary */
883 void set_view (struct t_disp
* p_disp
, int cx
, int cy
)
887 /* plain center to available width/height */
888 x
= cx
- MIN(LCD_WIDTH
, p_disp
->width
) / 2;
889 y
= cy
- MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
891 /* limit against upper image size */
892 x
= MIN(p_disp
->width
- LCD_WIDTH
, x
);
893 y
= MIN(p_disp
->height
- LCD_HEIGHT
, y
);
895 /* limit against negative side */
899 p_disp
->x
= x
; /* set the values */
904 /* calculate the view center based on the bitmap position */
905 void get_view(struct t_disp
* p_disp
, int* p_cx
, int* p_cy
)
907 *p_cx
= p_disp
->x
+ MIN(LCD_WIDTH
, p_disp
->width
) / 2;
908 *p_cy
= p_disp
->y
+ MIN(LCD_HEIGHT
, p_disp
->height
) / 2;
912 /* load, decode, display the image */
913 int load_and_show(char* filename
)
917 unsigned char* buf_jpeg
; /* compressed JPEG image */
919 struct t_disp
* p_disp
; /* currenly displayed image */
920 int cx
, cy
; /* view center */
922 fd
= rb
->open(filename
, O_RDONLY
);
925 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
926 rb
->splash(HZ
, print
);
929 filesize
= rb
->filesize(fd
);
930 rb
->memset(&disp
, 0, sizeof(disp
));
932 buf
= buf_images
+ filesize
;
933 buf_size
= buf_images_size
- filesize
;
934 /* allocate JPEG buffer */
935 buf_jpeg
= buf_images
;
937 buf_root
= buf
; /* we can start the decompressed images behind it */
938 root_size
= buf_size
;
942 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
946 rb
->lcd_setfont(FONT_SYSFIXED
);
947 rb
->lcd_clear_display();
948 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
949 rb
->lcd_puts(0,0,print
);
950 rb
->lcd_puts(0,1,"Not enough plugin memory!");
951 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
953 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
954 rb
->lcd_puts(0,4,"Off: Quit.");
956 rb
->lcd_setfont(FONT_UI
);
958 rb
->button_clear_queue();
962 int button
= rb
->button_get(true);
967 buf_images
= rb
->plugin_get_audio_buffer(
968 (size_t *)&buf_images_size
);
969 /*try again this file, now using the audio buffer */
980 rb
->lcd_clear_display();
981 return change_filename(DIR_PREV
);
988 rb
->lcd_clear_display();
989 return change_filename(DIR_NEXT
);
993 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
994 == SYS_USB_CONNECTED
)
995 return PLUGIN_USB_CONNECTED
;
1003 rb
->splash(HZ
, "Out of Memory");
1005 return PLUGIN_ERROR
;
1009 if(!running_slideshow
)
1012 rb
->lcd_set_foreground(LCD_WHITE
);
1013 rb
->lcd_set_background(LCD_BLACK
);
1014 rb
->lcd_set_backdrop(NULL
);
1017 rb
->lcd_clear_display();
1018 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
1019 rb
->lcd_puts(0, 0, print
);
1022 rb
->snprintf(print
, sizeof(print
), "loading %d bytes", filesize
);
1023 rb
->lcd_puts(0, 1, print
);
1027 rb
->read(fd
, buf_jpeg
, filesize
);
1030 if(!running_slideshow
)
1032 rb
->snprintf(print
, sizeof(print
), "decoding markers");
1033 rb
->lcd_puts(0, 2, print
);
1037 else if(immediate_ata_off
)
1039 /* running slideshow and time is long enough: power down disk */
1040 rb
->storage_sleep();
1044 rb
->memset(&jpg
, 0, sizeof(jpg
)); /* clear info struct */
1045 /* process markers, unstuffing */
1046 status
= process_markers(buf_jpeg
, filesize
, &jpg
);
1048 if (status
< 0 || (status
& (DQT
| SOF0
)) != (DQT
| SOF0
))
1049 { /* bad format or minimum components not contained */
1050 rb
->splashf(HZ
, "unsupported %d", status
);
1051 file_pt
[curfile
] = '\0';
1052 return change_filename(direction
);
1055 if (!(status
& DHT
)) /* if no Huffman table present: */
1056 default_huff_tbl(&jpg
); /* use default */
1057 build_lut(&jpg
); /* derive Huffman and other lookup-tables */
1059 if(!running_slideshow
)
1061 rb
->snprintf(print
, sizeof(print
), "image %dx%d", jpg
.x_size
, jpg
.y_size
);
1062 rb
->lcd_puts(0, 2, print
);
1065 ds_max
= max_downscale(&jpg
); /* check display constraint */
1066 ds_min
= min_downscale(&jpg
, buf_size
); /* check memory constraint */
1069 rb
->splash(HZ
, "too large");
1070 file_pt
[curfile
] = '\0';
1071 return change_filename(direction
);
1074 ds
= ds_max
; /* initials setting */
1075 cx
= jpg
.x_size
/ds
/2; /* center the view */
1076 cy
= jpg
.y_size
/ds
/2;
1078 do /* loop the image prepare and decoding when zoomed */
1080 p_disp
= get_image(&jpg
, ds
); /* decode or fetch from cache */
1082 return change_filename(direction
);
1084 set_view(p_disp
, cx
, cy
);
1086 if(!running_slideshow
)
1088 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
1089 p_disp
->width
, p_disp
->height
);
1090 rb
->lcd_puts(0, 3, print
);
1093 MYLCD(clear_display
)();
1094 #ifdef HAVE_LCD_COLOR
1096 p_disp
->bitmap
, p_disp
->csub_x
, p_disp
->csub_y
,
1097 p_disp
->x
, p_disp
->y
, p_disp
->stride
,
1098 MAX(0, (LCD_WIDTH
- p_disp
->width
) / 2),
1099 MAX(0, (LCD_HEIGHT
- p_disp
->height
) / 2),
1100 MIN(LCD_WIDTH
, p_disp
->width
),
1101 MIN(LCD_HEIGHT
, p_disp
->height
),
1102 jpeg_settings
.colour_mode
, jpeg_settings
.dither_mode
);
1104 MYXLCD(gray_bitmap_part
)(
1105 p_disp
->bitmap
[0], p_disp
->x
, p_disp
->y
, p_disp
->stride
,
1106 MAX(0, (LCD_WIDTH
- p_disp
->width
) / 2),
1107 MAX(0, (LCD_HEIGHT
- p_disp
->height
) / 2),
1108 MIN(LCD_WIDTH
, p_disp
->width
),
1109 MIN(LCD_HEIGHT
, p_disp
->height
));
1114 grey_show(true); /* switch on greyscale overlay */
1117 /* drawing is now finished, play around with scrolling
1118 * until you press OFF or connect USB
1122 status
= scroll_bmp(p_disp
);
1123 if (status
== ZOOM_IN
)
1127 ds
/= 2; /* reduce downscaling to zoom in */
1128 get_view(p_disp
, &cx
, &cy
);
1129 cx
*= 2; /* prepare the position in the new image */
1136 if (status
== ZOOM_OUT
)
1140 ds
*= 2; /* increase downscaling to zoom out */
1141 get_view(p_disp
, &cx
, &cy
);
1142 cx
/= 2; /* prepare the position in the new image */
1152 grey_show(false); /* switch off overlay */
1154 rb
->lcd_clear_display();
1156 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
1157 && status
!= PLUGIN_OTHER
);
1164 /******************** Plugin entry point *********************/
1166 enum plugin_status
plugin_start(UNUSED_ATTR
const void* parameter
)
1170 long greysize
; /* helper */
1173 old_backdrop
= rb
->lcd_get_backdrop();
1176 if(!parameter
) return PLUGIN_ERROR
;
1178 rb
->strcpy(np_file
, parameter
);
1181 if(!entries
) return PLUGIN_ERROR
;
1183 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1184 if(rb
->audio_status())
1186 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
) +
1187 (entries
* sizeof(char**));
1188 buf_size
-= (entries
* sizeof(char**));
1192 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
1194 buf
= rb
->plugin_get_audio_buffer(&buf_size
) +
1195 (entries
* sizeof(char**));
1196 buf_size
-= (entries
* sizeof(char**));
1200 if (!grey_init(buf
, buf_size
, GREY_ON_COP
,
1201 LCD_WIDTH
, LCD_HEIGHT
, &greysize
))
1203 rb
->splash(HZ
, "grey buf error");
1204 return PLUGIN_ERROR
;
1207 buf_size
-= greysize
;
1210 /* should be ok to just load settings since the plugin itself has
1211 just been loaded from disk and the drive should be spinning */
1212 configfile_load(JPEG_CONFIGFILE
, jpeg_config
,
1213 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_MINVERSION
);
1214 old_settings
= jpeg_settings
;
1216 buf_images
= buf
; buf_images_size
= buf_size
;
1218 /* Turn off backlight timeout */
1219 backlight_force_on(); /* backlight control in lib/helper.c */
1223 condition
= load_and_show(np_file
);
1224 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
1225 && condition
!= PLUGIN_ERROR
);
1227 if (rb
->memcmp(&jpeg_settings
, &old_settings
, sizeof (jpeg_settings
)))
1229 /* Just in case drive has to spin, keep it from looking locked */
1230 rb
->splash(0, "Saving Settings");
1231 configfile_save(JPEG_CONFIGFILE
, jpeg_config
,
1232 ARRAYLEN(jpeg_config
), JPEG_SETTINGS_VERSION
);
1235 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1236 /* set back ata spindown time in case we changed it */
1237 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1240 /* Turn on backlight timeout (revert to settings) */
1241 backlight_use_settings(); /* backlight control in lib/helper.c */
1244 grey_release(); /* deinitialize */