jpeg/png: refactor use of buf.
[kugel-rb.git] / apps / plugins / jpeg / jpeg.c
blobe3c210b4a5c23b59b1c4edea54e8d1b47b648390
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * JPEG image viewer
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 ****************************************************************************/
28 #include "plugin.h"
29 #include <lib/playback_control.h>
30 #include <lib/helper.h>
31 #include <lib/configfile.h>
33 #include <lib/grey.h>
34 #include <lib/xlcd.h>
36 #include "jpeg.h"
37 #include "jpeg_decoder.h"
39 PLUGIN_HEADER
41 #ifdef HAVE_LCD_COLOR
42 #include "yuv2rgb.h"
43 #endif
45 /* different graphics libraries */
46 #if LCD_DEPTH < 8
47 #define USEGSLIB
48 GREY_INFO_STRUCT
49 #define MYLCD(fn) grey_ub_ ## fn
50 #define MYLCD_UPDATE()
51 #define MYXLCD(fn) grey_ub_ ## fn
52 #else
53 #define MYLCD(fn) rb->lcd_ ## fn
54 #define MYLCD_UPDATE() rb->lcd_update();
55 #define MYXLCD(fn) xlcd_ ## fn
56 #endif
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
69 /* Headings */
70 #define DIR_PREV 1
71 #define DIR_NEXT -1
72 #define DIR_NONE 0
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 */
80 #ifndef SIMULATOR
81 static int immediate_ata_off = false; /* power down disk after loading */
82 #endif
84 #ifdef HAVE_LCD_COLOR
85 fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when
86 DITHER_DIFFUSION is set */
87 #endif
90 /* Persistent configuration */
91 #define JPEG_CONFIGFILE "jpeg.cfg"
92 #define JPEG_SETTINGS_MINVERSION 1
93 #define JPEG_SETTINGS_VERSION 2
95 /* Slideshow times */
96 #define SS_MIN_TIMEOUT 1
97 #define SS_MAX_TIMEOUT 20
98 #define SS_DEFAULT_TIMEOUT 5
100 struct jpeg_settings
102 #ifdef HAVE_LCD_COLOR
103 int colour_mode;
104 int dither_mode;
105 #endif
106 int ss_timeout;
109 static struct jpeg_settings jpeg_settings =
111 #ifdef HAVE_LCD_COLOR
112 COLOURMODE_COLOUR,
113 DITHER_NONE,
114 #endif
115 SS_DEFAULT_TIMEOUT
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" } },
126 #endif
127 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
128 { .int_p = &jpeg_settings.ss_timeout }, "Slideshow Time", NULL },
131 #if LCD_DEPTH > 1
132 static fb_data* old_backdrop;
133 #endif
135 /**************** begin Application ********************/
138 /************************* Types ***************************/
140 struct t_disp
142 #ifdef HAVE_LCD_COLOR
143 unsigned char* bitmap[3]; /* Y, Cr, Cb */
144 int csub_x, csub_y;
145 #else
146 unsigned char* bitmap[1]; /* Y only */
147 #endif
148 int width;
149 int height;
150 int stride;
151 int x, y;
154 /************************* Globals ***************************/
156 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
157 static struct t_disp disp[9];
159 /* my memory pool (from the mp3 buffer) */
160 static char print[32]; /* use a common snprintf() buffer */
161 /* the remaining free part of the buffer for compressed+uncompressed images */
162 static unsigned char* buf;
163 static ssize_t buf_size;
165 /* the root of the images, hereafter are decompresed ones */
166 static unsigned char* buf_root;
167 static int root_size;
169 /* up to here currently used by image(s) */
170 static unsigned char* buf_images;
171 static ssize_t buf_images_size;
173 static int ds, ds_min, ds_max; /* downscaling and limits */
174 static struct jpeg jpg; /* too large for stack */
176 static struct tree_context *tree;
178 /* the current full file name */
179 static char np_file[MAX_PATH];
180 static int curfile = 0, direction = DIR_NONE, entries = 0;
182 /* list of the jpeg files */
183 static char **file_pt;
184 /* are we using the plugin buffer or the audio buffer? */
185 bool plug_buf = false;
188 /************************* Implementation ***************************/
190 bool jpg_ext(const char ext[])
192 if(!ext)
193 return false;
194 if(!rb->strcasecmp(ext,".jpg") ||
195 !rb->strcasecmp(ext,".jpe") ||
196 !rb->strcasecmp(ext,".jpeg"))
197 return true;
198 else
199 return false;
202 /*Read directory contents for scrolling. */
203 void get_pic_list(void)
205 int i;
206 struct entry *dircache;
207 char *pname;
208 tree = rb->tree_get_context();
209 dircache = tree->dircache;
211 file_pt = (char **) buf;
213 /* Remove path and leave only the name.*/
214 pname = rb->strrchr(np_file,'/');
215 pname++;
217 for (i = 0; i < tree->filesindir; i++)
219 if (!(dircache[i].attr & ATTR_DIRECTORY)
220 && jpg_ext(rb->strrchr(dircache[i].name,'.')))
222 file_pt[entries] = dircache[i].name;
223 /* Set Selected File. */
224 if (!rb->strcmp(file_pt[entries], pname))
225 curfile = entries;
226 entries++;
230 buf += (entries * sizeof(char**));
231 buf_size -= (entries * sizeof(char**));
234 int change_filename(int direct)
236 int count = 0;
237 direction = direct;
239 if(direct == DIR_PREV)
243 count++;
244 if(curfile == 0)
245 curfile = entries - 1;
246 else
247 curfile--;
248 }while(file_pt[curfile] == NULL && count < entries);
249 /* we "erase" the file name if we encounter
250 * a non-supported file, so skip it now */
252 else /* DIR_NEXT/DIR_NONE */
256 count++;
257 if(curfile == entries - 1)
258 curfile = 0;
259 else
260 curfile++;
261 }while(file_pt[curfile] == NULL && count < entries);
264 if(count == entries)
266 rb->splash(HZ, "No supported files");
267 return PLUGIN_ERROR;
269 if(rb->strlen(tree->currdir) > 1)
271 rb->strcpy(np_file, tree->currdir);
272 rb->strcat(np_file, "/");
274 else
275 rb->strcpy(np_file, tree->currdir);
277 rb->strcat(np_file, file_pt[curfile]);
279 return PLUGIN_OTHER;
282 /* switch off overlay, for handling SYS_ events */
283 void cleanup(void *parameter)
285 (void)parameter;
286 #ifdef USEGSLIB
287 grey_show(false);
288 #endif
291 #define VSCROLL (LCD_HEIGHT/8)
292 #define HSCROLL (LCD_WIDTH/10)
294 #define ZOOM_IN 100 /* return codes for below function */
295 #define ZOOM_OUT 101
297 #ifdef HAVE_LCD_COLOR
298 bool set_option_grayscale(void)
300 bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY;
301 rb->set_bool("Grayscale", &gray);
302 jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR;
303 return false;
306 bool set_option_dithering(void)
308 static const struct opt_items dithering[DITHER_NUM_MODES] = {
309 [DITHER_NONE] = { "Off", -1 },
310 [DITHER_ORDERED] = { "Ordered", -1 },
311 [DITHER_DIFFUSION] = { "Diffusion", -1 },
314 rb->set_option("Dithering", &jpeg_settings.dither_mode, INT,
315 dithering, DITHER_NUM_MODES, NULL);
316 return false;
319 MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale",
320 set_option_grayscale, NULL, NULL, Icon_NOICON);
321 MENUITEM_FUNCTION(dithering_item, 0, "Dithering",
322 set_option_dithering, NULL, NULL, Icon_NOICON);
323 MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
324 &grayscale_item, &dithering_item);
326 static void display_options(void)
328 rb->do_menu(&display_menu, NULL, NULL, false);
330 #endif /* HAVE_LCD_COLOR */
332 int show_menu(void) /* return 1 to quit */
334 #if LCD_DEPTH > 1
335 rb->lcd_set_backdrop(old_backdrop);
336 #ifdef HAVE_LCD_COLOR
337 rb->lcd_set_foreground(rb->global_settings->fg_color);
338 rb->lcd_set_background(rb->global_settings->bg_color);
339 #else
340 rb->lcd_set_foreground(LCD_BLACK);
341 rb->lcd_set_background(LCD_WHITE);
342 #endif
343 #endif
344 int result;
346 enum menu_id
348 MIID_RETURN = 0,
349 MIID_TOGGLE_SS_MODE,
350 MIID_CHANGE_SS_MODE,
351 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
352 MIID_SHOW_PLAYBACK_MENU,
353 #endif
354 #ifdef HAVE_LCD_COLOR
355 MIID_DISPLAY_OPTIONS,
356 #endif
357 MIID_QUIT,
360 MENUITEM_STRINGLIST(menu, "Jpeg Menu", NULL,
361 "Return", "Toggle Slideshow Mode",
362 "Change Slideshow Time",
363 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
364 "Show Playback Menu",
365 #endif
366 #ifdef HAVE_LCD_COLOR
367 "Display Options",
368 #endif
369 "Quit");
371 static const struct opt_items slideshow[2] = {
372 { "Disable", -1 },
373 { "Enable", -1 },
376 result=rb->do_menu(&menu, NULL, NULL, false);
378 switch (result)
380 case MIID_RETURN:
381 break;
382 case MIID_TOGGLE_SS_MODE:
383 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
384 slideshow , 2, NULL);
385 break;
386 case MIID_CHANGE_SS_MODE:
387 rb->set_int("Slideshow Time", "s", UNIT_SEC,
388 &jpeg_settings.ss_timeout, NULL, 1,
389 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
390 break;
392 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
393 case MIID_SHOW_PLAYBACK_MENU:
394 if (plug_buf)
396 playback_control(NULL);
398 else
400 rb->splash(HZ, "Cannot restart playback");
402 break;
403 #endif
404 #ifdef HAVE_LCD_COLOR
405 case MIID_DISPLAY_OPTIONS:
406 display_options();
407 break;
408 #endif
409 case MIID_QUIT:
410 return 1;
411 break;
414 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
415 /* change ata spindown time based on slideshow time setting */
416 immediate_ata_off = false;
417 rb->storage_spindown(rb->global_settings->disk_spindown);
419 if (slideshow_enabled)
421 if(jpeg_settings.ss_timeout < 10)
423 /* slideshow times < 10s keep disk spinning */
424 rb->storage_spindown(0);
426 else if (!rb->mp3_is_playing())
428 /* slideshow times > 10s and not playing: ata_off after load */
429 immediate_ata_off = true;
432 #endif
433 #if LCD_DEPTH > 1
434 rb->lcd_set_backdrop(NULL);
435 rb->lcd_set_foreground(LCD_WHITE);
436 rb->lcd_set_background(LCD_BLACK);
437 #endif
438 rb->lcd_clear_display();
439 return 0;
442 void draw_image_rect(struct t_disp* pdisp, int x, int y, int width, int height)
444 #ifdef HAVE_LCD_COLOR
445 yuv_bitmap_part(
446 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
447 pdisp->x + x, pdisp->y + y, pdisp->stride,
448 x + MAX(0, (LCD_WIDTH - pdisp->width) / 2),
449 y + MAX(0, (LCD_HEIGHT - pdisp->height) / 2),
450 width, height,
451 jpeg_settings.colour_mode, jpeg_settings.dither_mode);
452 #else
453 MYXLCD(gray_bitmap_part)(
454 pdisp->bitmap[0], pdisp->x + x, pdisp->y + y, pdisp->stride,
455 x + MAX(0, (LCD_WIDTH-pdisp->width)/2),
456 y + MAX(0, (LCD_HEIGHT-pdisp->height)/2),
457 width, height);
458 #endif
461 /* Pan the viewing window right - move image to the left and fill in
462 the right-hand side */
463 static void pan_view_right(struct t_disp* pdisp)
465 int move;
467 move = MIN(HSCROLL, pdisp->width - pdisp->x - LCD_WIDTH);
468 if (move > 0)
470 MYXLCD(scroll_left)(move); /* scroll left */
471 pdisp->x += move;
472 draw_image_rect(pdisp, LCD_WIDTH - move, 0, move, pdisp->height-pdisp->y);
473 MYLCD_UPDATE();
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)
481 int move;
483 move = MIN(HSCROLL, pdisp->x);
484 if (move > 0)
486 MYXLCD(scroll_right)(move); /* scroll right */
487 pdisp->x -= move;
488 draw_image_rect(pdisp, 0, 0, move, pdisp->height-pdisp->y);
489 MYLCD_UPDATE();
493 /* Pan the viewing window up - move image down and fill in
494 the top */
495 static void pan_view_up(struct t_disp* pdisp)
497 int move;
499 move = MIN(VSCROLL, pdisp->y);
500 if (move > 0)
502 MYXLCD(scroll_down)(move); /* scroll down */
503 pdisp->y -= move;
504 #ifdef HAVE_LCD_COLOR
505 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
507 /* Draw over the band at the top of the last update
508 caused by lack of error history on line zero. */
509 move = MIN(move + 1, pdisp->y + pdisp->height);
511 #endif
512 draw_image_rect(pdisp, 0, 0, pdisp->width-pdisp->x, move);
513 MYLCD_UPDATE();
517 /* Pan the viewing window down - move image up and fill in
518 the bottom */
519 static void pan_view_down(struct t_disp* pdisp)
521 int move;
523 move = MIN(VSCROLL, pdisp->height - pdisp->y - LCD_HEIGHT);
524 if (move > 0)
526 MYXLCD(scroll_up)(move); /* scroll up */
527 pdisp->y += move;
528 #ifdef HAVE_LCD_COLOR
529 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
531 /* Save the line that was on the last line of the display
532 and draw one extra line above then recover the line with
533 image data that had an error history when it was drawn.
535 move++, pdisp->y--;
536 rb->memcpy(rgb_linebuf,
537 rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
538 LCD_WIDTH*sizeof (fb_data));
540 #endif
542 draw_image_rect(pdisp, 0, LCD_HEIGHT - move, pdisp->width-pdisp->x, move);
544 #ifdef HAVE_LCD_COLOR
545 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
547 /* Cover the first row drawn with previous image data. */
548 rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
549 rgb_linebuf,
550 LCD_WIDTH*sizeof (fb_data));
551 pdisp->y++;
553 #endif
554 MYLCD_UPDATE();
558 /* interactively scroll around the image */
559 int scroll_bmp(struct t_disp* pdisp)
561 int button;
562 int lastbutton = 0;
564 while (true)
566 if (slideshow_enabled)
567 button = rb->button_get_w_tmo(jpeg_settings.ss_timeout * HZ);
568 else button = rb->button_get(true);
570 running_slideshow = false;
572 switch(button)
574 case JPEG_LEFT:
575 if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE)
576 return change_filename(DIR_PREV);
577 case JPEG_LEFT | BUTTON_REPEAT:
578 pan_view_left(pdisp);
579 break;
581 case JPEG_RIGHT:
582 if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE)
583 return change_filename(DIR_NEXT);
584 case JPEG_RIGHT | BUTTON_REPEAT:
585 pan_view_right(pdisp);
586 break;
588 case JPEG_UP:
589 case JPEG_UP | BUTTON_REPEAT:
590 pan_view_up(pdisp);
591 break;
593 case JPEG_DOWN:
594 case JPEG_DOWN | BUTTON_REPEAT:
595 pan_view_down(pdisp);
596 break;
598 case BUTTON_NONE:
599 if (!slideshow_enabled)
600 break;
601 running_slideshow = true;
602 if (entries > 0)
603 return change_filename(DIR_NEXT);
604 break;
606 #ifdef JPEG_SLIDE_SHOW
607 case JPEG_SLIDE_SHOW:
608 slideshow_enabled = !slideshow_enabled;
609 running_slideshow = slideshow_enabled;
610 break;
611 #endif
613 #ifdef JPEG_NEXT_REPEAT
614 case JPEG_NEXT_REPEAT:
615 #endif
616 case JPEG_NEXT:
617 if (entries > 0)
618 return change_filename(DIR_NEXT);
619 break;
621 #ifdef JPEG_PREVIOUS_REPEAT
622 case JPEG_PREVIOUS_REPEAT:
623 #endif
624 case JPEG_PREVIOUS:
625 if (entries > 0)
626 return change_filename(DIR_PREV);
627 break;
629 case JPEG_ZOOM_IN:
630 #ifdef JPEG_ZOOM_PRE
631 if (lastbutton != JPEG_ZOOM_PRE)
632 break;
633 #endif
634 return ZOOM_IN;
635 break;
637 case JPEG_ZOOM_OUT:
638 #ifdef JPEG_ZOOM_PRE
639 if (lastbutton != JPEG_ZOOM_PRE)
640 break;
641 #endif
642 return ZOOM_OUT;
643 break;
644 #ifdef JPEG_RC_MENU
645 case JPEG_RC_MENU:
646 #endif
647 case JPEG_MENU:
648 #ifdef USEGSLIB
649 grey_show(false); /* switch off greyscale overlay */
650 #endif
651 if (show_menu() == 1)
652 return PLUGIN_OK;
654 #ifdef USEGSLIB
655 grey_show(true); /* switch on greyscale overlay */
656 #else
657 draw_image_rect(pdisp, 0, 0,
658 pdisp->width-pdisp->x, pdisp->height-pdisp->y);
659 MYLCD_UPDATE();
660 #endif
661 break;
662 default:
663 if (rb->default_event_handler_ex(button, cleanup, NULL)
664 == SYS_USB_CONNECTED)
665 return PLUGIN_USB_CONNECTED;
666 break;
668 } /* switch */
670 if (button != BUTTON_NONE)
671 lastbutton = button;
672 } /* while (true) */
675 /********************* main function *************************/
677 /* callback updating a progress meter while JPEG decoding */
678 void cb_progess(int current, int total)
680 rb->yield(); /* be nice to the other threads */
681 if(!running_slideshow)
683 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0,
684 current, HORIZONTAL);
685 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
687 #ifndef USEGSLIB
688 else
690 /* in slideshow mode, keep gui interference to a minimum */
691 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0,
692 current, HORIZONTAL);
693 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
695 #endif
698 int jpegmem(struct jpeg *p_jpg, int ds)
700 int size;
702 size = (p_jpg->x_phys/ds/p_jpg->subsample_x[0])
703 * (p_jpg->y_phys/ds/p_jpg->subsample_y[0]);
704 #ifdef HAVE_LCD_COLOR
705 if (p_jpg->blocks > 1) /* colour, add requirements for chroma */
707 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[1])
708 * (p_jpg->y_phys/ds/p_jpg->subsample_y[1]);
709 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[2])
710 * (p_jpg->y_phys/ds/p_jpg->subsample_y[2]);
712 #endif
713 return size;
716 /* how far can we zoom in without running out of memory */
717 int min_downscale(struct jpeg *p_jpg, int bufsize)
719 int downscale = 8;
721 if (jpegmem(p_jpg, 8) > bufsize)
722 return 0; /* error, too large, even 1:8 doesn't fit */
724 while (downscale > 1 && jpegmem(p_jpg, downscale/2) <= bufsize)
725 downscale /= 2;
727 return downscale;
731 /* how far can we zoom out, to fit image into the LCD */
732 int max_downscale(struct jpeg *p_jpg)
734 int downscale = 1;
736 while (downscale < 8 && (p_jpg->x_size > LCD_WIDTH*downscale
737 || p_jpg->y_size > LCD_HEIGHT*downscale))
739 downscale *= 2;
742 return downscale;
746 /* return decoded or cached image */
747 struct t_disp* get_image(struct jpeg* p_jpg, int ds)
749 int w, h; /* used to center output */
750 int size; /* decompressed image size */
751 long time; /* measured ticks */
752 int status;
754 struct t_disp* p_disp = &disp[ds]; /* short cut */
756 if (p_disp->bitmap[0] != NULL)
758 return p_disp; /* we still have it */
761 /* assign image buffer */
763 /* physical size needed for decoding */
764 size = jpegmem(p_jpg, ds);
765 if (buf_images_size <= size)
766 { /* have to discard the current */
767 int i;
768 for (i=1; i<=8; i++)
769 disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */
770 buf_images = buf_root; /* start again from the beginning of the buffer */
771 buf_images_size = root_size;
774 #ifdef HAVE_LCD_COLOR
775 if (p_jpg->blocks > 1) /* colour jpeg */
777 int i;
779 for (i = 1; i < 3; i++)
781 size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i])
782 * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]);
783 p_disp->bitmap[i] = buf_images;
784 buf_images += size;
785 buf_images_size -= size;
787 p_disp->csub_x = p_jpg->subsample_x[1];
788 p_disp->csub_y = p_jpg->subsample_y[1];
790 else
792 p_disp->csub_x = p_disp->csub_y = 0;
793 p_disp->bitmap[1] = p_disp->bitmap[2] = buf_images;
795 #endif
796 /* size may be less when decoded (if height is not block aligned) */
797 size = (p_jpg->x_phys/ds) * (p_jpg->y_size / ds);
798 p_disp->bitmap[0] = buf_images;
799 buf_images += size;
800 buf_images_size -= size;
802 if(!running_slideshow)
804 rb->snprintf(print, sizeof(print), "decoding %d*%d",
805 p_jpg->x_size/ds, p_jpg->y_size/ds);
806 rb->lcd_puts(0, 3, print);
807 rb->lcd_update();
810 /* update image properties */
811 p_disp->width = p_jpg->x_size / ds;
812 p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */
813 p_disp->height = p_jpg->y_size / ds;
815 /* the actual decoding */
816 time = *rb->current_tick;
817 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
818 rb->cpu_boost(true);
819 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess);
820 rb->cpu_boost(false);
821 #else
822 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess);
823 #endif
824 if (status)
826 rb->splashf(HZ, "decode error %d", status);
827 file_pt[curfile] = NULL;
828 return NULL;
830 time = *rb->current_tick - time;
832 if(!running_slideshow)
834 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
835 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
836 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
837 rb->lcd_update();
840 return p_disp;
844 /* set the view to the given center point, limit if necessary */
845 void set_view (struct t_disp* p_disp, int cx, int cy)
847 int x, y;
849 /* plain center to available width/height */
850 x = cx - MIN(LCD_WIDTH, p_disp->width) / 2;
851 y = cy - MIN(LCD_HEIGHT, p_disp->height) / 2;
853 /* limit against upper image size */
854 x = MIN(p_disp->width - LCD_WIDTH, x);
855 y = MIN(p_disp->height - LCD_HEIGHT, y);
857 /* limit against negative side */
858 x = MAX(0, x);
859 y = MAX(0, y);
861 p_disp->x = x; /* set the values */
862 p_disp->y = y;
866 /* calculate the view center based on the bitmap position */
867 void get_view(struct t_disp* p_disp, int* p_cx, int* p_cy)
869 *p_cx = p_disp->x + MIN(LCD_WIDTH, p_disp->width) / 2;
870 *p_cy = p_disp->y + MIN(LCD_HEIGHT, p_disp->height) / 2;
874 /* load, decode, display the image */
875 int load_and_show(char* filename)
877 int fd;
878 int filesize;
879 unsigned char* buf_jpeg; /* compressed JPEG image */
880 int status;
881 struct t_disp* p_disp; /* currenly displayed image */
882 int cx, cy; /* view center */
884 fd = rb->open(filename, O_RDONLY);
885 if (fd < 0)
887 rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd);
888 rb->splash(HZ, print);
889 return PLUGIN_ERROR;
891 filesize = rb->filesize(fd);
892 rb->memset(&disp, 0, sizeof(disp));
894 /* allocate JPEG buffer */
895 buf_jpeg = buf;
897 /* we can start the decompressed images behind it */
898 buf_images = buf_root = buf + filesize;
899 buf_images_size = root_size = buf_size - filesize;
901 if (buf_images_size <= 0)
903 rb->close(fd);
904 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
905 if(plug_buf)
907 rb->lcd_setfont(FONT_SYSFIXED);
908 rb->lcd_clear_display();
909 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
910 rb->lcd_puts(0,0,print);
911 rb->lcd_puts(0,1,"Not enough plugin memory!");
912 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
913 if(entries>1)
914 rb->lcd_puts(0,3,"Left/Right: Skip File.");
915 rb->lcd_puts(0,4,"Off: Quit.");
916 rb->lcd_update();
917 rb->lcd_setfont(FONT_UI);
919 rb->button_clear_queue();
921 while (1)
923 int button = rb->button_get(true);
924 switch(button)
926 case JPEG_ZOOM_IN:
927 plug_buf = false;
928 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
929 /*try again this file, now using the audio buffer */
930 return PLUGIN_OTHER;
931 #ifdef JPEG_RC_MENU
932 case JPEG_RC_MENU:
933 #endif
934 case JPEG_MENU:
935 return PLUGIN_OK;
937 case JPEG_LEFT:
938 if(entries>1)
940 rb->lcd_clear_display();
941 return change_filename(DIR_PREV);
943 break;
945 case JPEG_RIGHT:
946 if(entries>1)
948 rb->lcd_clear_display();
949 return change_filename(DIR_NEXT);
951 break;
952 default:
953 if(rb->default_event_handler_ex(button, cleanup, NULL)
954 == SYS_USB_CONNECTED)
955 return PLUGIN_USB_CONNECTED;
960 else
961 #endif
963 rb->splash(HZ, "Out of Memory");
964 return PLUGIN_ERROR;
968 if(!running_slideshow)
970 #if LCD_DEPTH > 1
971 rb->lcd_set_foreground(LCD_WHITE);
972 rb->lcd_set_background(LCD_BLACK);
973 rb->lcd_set_backdrop(NULL);
974 #endif
976 rb->lcd_clear_display();
977 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
978 rb->lcd_puts(0, 0, print);
979 rb->lcd_update();
981 rb->snprintf(print, sizeof(print), "loading %d bytes", filesize);
982 rb->lcd_puts(0, 1, print);
983 rb->lcd_update();
986 rb->read(fd, buf_jpeg, filesize);
987 rb->close(fd);
989 if(!running_slideshow)
991 rb->snprintf(print, sizeof(print), "decoding markers");
992 rb->lcd_puts(0, 2, print);
993 rb->lcd_update();
995 #ifndef SIMULATOR
996 else if(immediate_ata_off)
998 /* running slideshow and time is long enough: power down disk */
999 rb->storage_sleep();
1001 #endif
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);
1022 rb->lcd_update();
1024 ds_max = max_downscale(&jpg); /* check display constraint */
1025 ds_min = min_downscale(&jpg, buf_images_size); /* check memory constraint */
1026 if (ds_min == 0)
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 */
1040 if (p_disp == NULL)
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);
1050 rb->lcd_update();
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);
1056 MYLCD_UPDATE();
1058 #ifdef USEGSLIB
1059 grey_show(true); /* switch on greyscale overlay */
1060 #endif
1062 /* drawing is now finished, play around with scrolling
1063 * until you press OFF or connect USB
1065 while (1)
1067 status = scroll_bmp(p_disp);
1068 if (status == ZOOM_IN)
1070 if (ds > ds_min)
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 */
1075 cy *= 2;
1077 else
1078 continue;
1081 if (status == ZOOM_OUT)
1083 if (ds < ds_max)
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 */
1088 cy /= 2;
1090 else
1091 continue;
1093 break;
1096 #ifdef USEGSLIB
1097 grey_show(false); /* switch off overlay */
1098 #endif
1099 rb->lcd_clear_display();
1101 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
1102 && status != PLUGIN_OTHER);
1103 #ifdef USEGSLIB
1104 rb->lcd_update();
1105 #endif
1106 return status;
1109 /******************** Plugin entry point *********************/
1111 enum plugin_status plugin_start(const void* parameter)
1113 int condition;
1114 #ifdef USEGSLIB
1115 long greysize; /* helper */
1116 #endif
1117 #if LCD_DEPTH > 1
1118 old_backdrop = rb->lcd_get_backdrop();
1119 #endif
1121 if(!parameter) return PLUGIN_ERROR;
1123 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1124 buf = rb->plugin_get_buffer((size_t *)&buf_size);
1125 #else
1126 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1127 #endif
1129 rb->strcpy(np_file, parameter);
1130 get_pic_list();
1132 if(!entries) return PLUGIN_ERROR;
1134 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1135 if(rb->audio_status())
1136 plug_buf = true;
1137 else
1138 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1139 #endif
1141 #ifdef USEGSLIB
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;
1148 buf += greysize;
1149 buf_size -= greysize;
1150 #endif
1152 /* should be ok to just load settings since the plugin itself has
1153 just been loaded from disk and the drive should be spinning */
1154 configfile_load(JPEG_CONFIGFILE, jpeg_config,
1155 ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION);
1156 old_settings = jpeg_settings;
1158 /* Turn off backlight timeout */
1159 backlight_force_on(); /* backlight control in lib/helper.c */
1163 condition = load_and_show(np_file);
1164 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
1165 && condition != PLUGIN_ERROR);
1167 if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings)))
1169 /* Just in case drive has to spin, keep it from looking locked */
1170 rb->splash(0, "Saving Settings");
1171 configfile_save(JPEG_CONFIGFILE, jpeg_config,
1172 ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION);
1175 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1176 /* set back ata spindown time in case we changed it */
1177 rb->storage_spindown(rb->global_settings->disk_spindown);
1178 #endif
1180 /* Turn on backlight timeout (revert to settings) */
1181 backlight_use_settings(); /* backlight control in lib/helper.c */
1183 #ifdef USEGSLIB
1184 grey_release(); /* deinitialize */
1185 #endif
1187 return condition;