summaryrefslogtreecommitdiff
path: root/apps/plugins/jpeg/jpeg.c
diff options
context:
space:
mode:
authorTeruaki Kawashima <teru@rockbox.org>2010-01-18 12:46:19 +0000
committerTeruaki Kawashima <teru@rockbox.org>2010-01-18 12:46:19 +0000
commit5bd08237499dfc66309ba2a5a4dac75018e794ac (patch)
treefe742342707b8789ce0dbf1a18e5a8346ae2601d /apps/plugins/jpeg/jpeg.c
parent135d983433e741cf9658ff5d7457bdf37ef48ce0 (diff)
downloadrockbox-5bd08237499dfc66309ba2a5a4dac75018e794ac.tar.gz
rockbox-5bd08237499dfc66309ba2a5a4dac75018e794ac.zip
jpeg,png: Merge user interface code and plugin entry point of the two plugins (part of FS#6321).
* Created new directory, imageviewer/ and moved both jpeg/ and png/ under it. - this still doesn't merge the two plugins. i.e. both jpeg.rock and png.rock will be made for color targets. - I'm thinking to merge the two plugins to single image viewer later. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24272 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/jpeg/jpeg.c')
-rw-r--r--apps/plugins/jpeg/jpeg.c1218
1 files changed, 0 insertions, 1218 deletions
diff --git a/apps/plugins/jpeg/jpeg.c b/apps/plugins/jpeg/jpeg.c
deleted file mode 100644
index 4a61f13e51..0000000000
--- a/apps/plugins/jpeg/jpeg.c
+++ /dev/null
@@ -1,1218 +0,0 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* JPEG image viewer
11* (This is a real mess if it has to be coded in one single C file)
12*
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
17*
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.
22*
23* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24* KIND, either express or implied.
25*
26****************************************************************************/
27
28#include "plugin.h"
29#include <lib/playback_control.h>
30#include <lib/helper.h>
31#include <lib/configfile.h>
32
33#include <lib/grey.h>
34#include <lib/xlcd.h>
35
36#include "jpeg.h"
37#include "jpeg_decoder.h"
38
39PLUGIN_HEADER
40
41#ifdef HAVE_LCD_COLOR
42#include "yuv2rgb.h"
43#endif
44
45/* different graphics libraries */
46#if LCD_DEPTH < 8
47#define USEGSLIB
48GREY_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
57
58/* Min memory allowing us to use the plugin buffer
59 * and thus not stopping the music
60 * *Very* rough estimation:
61 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
62 * + 20k code size = 60 000
63 * + 50k min for jpeg = 120 000
64 */
65#define MIN_MEM 120000
66
67/* Headings */
68#define DIR_PREV 1
69#define DIR_NEXT -1
70#define DIR_NONE 0
71
72#define PLUGIN_OTHER 10 /* State code for output with return. */
73#define PLUGIN_ABORT 11
74#define PLUGIN_OUTOFMEM 12
75
76/******************************* Globals ***********************************/
77
78static int slideshow_enabled = false; /* run slideshow */
79static int running_slideshow = false; /* loading image because of slideshw */
80#ifndef SIMULATOR
81static int immediate_ata_off = false; /* power down disk after loading */
82#endif
83
84#ifdef HAVE_LCD_COLOR
85fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when
86 DITHER_DIFFUSION is set */
87#endif
88
89
90/* Persistent configuration */
91#define JPEG_CONFIGFILE "jpeg.cfg"
92#define JPEG_SETTINGS_MINVERSION 1
93#define JPEG_SETTINGS_VERSION 2
94
95/* Slideshow times */
96#define SS_MIN_TIMEOUT 1
97#define SS_MAX_TIMEOUT 20
98#define SS_DEFAULT_TIMEOUT 5
99
100struct jpeg_settings
101{
102#ifdef HAVE_LCD_COLOR
103 int colour_mode;
104 int dither_mode;
105#endif
106 int ss_timeout;
107};
108
109static struct jpeg_settings jpeg_settings =
110{
111#ifdef HAVE_LCD_COLOR
112 COLOURMODE_COLOUR,
113 DITHER_NONE,
114#endif
115 SS_DEFAULT_TIMEOUT
116};
117static struct jpeg_settings old_settings;
118
119static struct configdata jpeg_config[] =
120{
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 },
129};
130
131#if LCD_DEPTH > 1
132static fb_data* old_backdrop;
133#endif
134
135/**************** begin Application ********************/
136
137
138/************************* Types ***************************/
139
140struct t_disp
141{
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;
152};
153
154/************************* Globals ***************************/
155
156/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
157static struct t_disp disp[9];
158
159/* my memory pool (from the mp3 buffer) */
160static char print[32]; /* use a common snprintf() buffer */
161/* the remaining free part of the buffer for compressed+uncompressed images */
162static unsigned char* buf;
163static ssize_t buf_size;
164
165/* the root of the images, hereafter are decompresed ones */
166static unsigned char* buf_root;
167static int root_size;
168
169/* up to here currently used by image(s) */
170static unsigned char* buf_images;
171static ssize_t buf_images_size;
172
173static int ds, ds_min, ds_max; /* downscaling and limits */
174static struct jpeg jpg; /* too large for stack */
175
176static struct tree_context *tree;
177
178/* the current full file name */
179static char np_file[MAX_PATH];
180static int curfile = 0, direction = DIR_NONE, entries = 0;
181
182/* list of the jpeg files */
183static char **file_pt;
184#if PLUGIN_BUFFER_SIZE >= MIN_MEM
185/* are we using the plugin buffer or the audio buffer? */
186static bool plug_buf = true;
187#endif
188
189
190/************************* Implementation ***************************/
191
192bool jpg_ext(const char ext[])
193{
194 if(!ext)
195 return false;
196 if(!rb->strcasecmp(ext,".jpg") ||
197 !rb->strcasecmp(ext,".jpe") ||
198 !rb->strcasecmp(ext,".jpeg"))
199 return true;
200 else
201 return false;
202}
203
204/*Read directory contents for scrolling. */
205void get_pic_list(void)
206{
207 int i;
208 struct entry *dircache;
209 char *pname;
210 tree = rb->tree_get_context();
211 dircache = tree->dircache;
212
213 file_pt = (char **) buf;
214
215 /* Remove path and leave only the name.*/
216 pname = rb->strrchr(np_file,'/');
217 pname++;
218
219 for (i = 0; i < tree->filesindir; i++)
220 {
221 if (!(dircache[i].attr & ATTR_DIRECTORY)
222 && jpg_ext(rb->strrchr(dircache[i].name,'.')))
223 {
224 file_pt[entries] = dircache[i].name;
225 /* Set Selected File. */
226 if (!rb->strcmp(file_pt[entries], pname))
227 curfile = entries;
228 entries++;
229 }
230 }
231
232 buf += (entries * sizeof(char**));
233 buf_size -= (entries * sizeof(char**));
234}
235
236int change_filename(int direct)
237{
238 bool file_erased = (file_pt[curfile] == NULL);
239 direction = direct;
240
241 curfile += (direct == DIR_PREV? entries - 1: 1);
242 if (curfile >= entries)
243 curfile -= entries;
244
245 if (file_erased)
246 {
247 /* remove 'erased' file names from list. */
248 int count, i;
249 for (count = i = 0; i < entries; i++)
250 {
251 if (curfile == i)
252 curfile = count;
253 if (file_pt[i] != NULL)
254 file_pt[count++] = file_pt[i];
255 }
256 entries = count;
257 }
258
259 if (entries == 0)
260 {
261 rb->splash(HZ, "No supported files");
262 return PLUGIN_ERROR;
263 }
264
265 if (rb->strlen(tree->currdir) > 1)
266 {
267 rb->strcpy(np_file, tree->currdir);
268 rb->strcat(np_file, "/");
269 }
270 else
271 rb->strcpy(np_file, tree->currdir);
272
273 rb->strcat(np_file, file_pt[curfile]);
274
275 return PLUGIN_OTHER;
276}
277
278/* switch off overlay, for handling SYS_ events */
279void cleanup(void *parameter)
280{
281 (void)parameter;
282#ifdef USEGSLIB
283 grey_show(false);
284#endif
285}
286
287#define VSCROLL (LCD_HEIGHT/8)
288#define HSCROLL (LCD_WIDTH/10)
289
290#define ZOOM_IN 100 /* return codes for below function */
291#define ZOOM_OUT 101
292
293#ifdef HAVE_LCD_COLOR
294bool set_option_grayscale(void)
295{
296 bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY;
297 rb->set_bool("Grayscale", &gray);
298 jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR;
299 return false;
300}
301
302bool set_option_dithering(void)
303{
304 static const struct opt_items dithering[DITHER_NUM_MODES] = {
305 [DITHER_NONE] = { "Off", -1 },
306 [DITHER_ORDERED] = { "Ordered", -1 },
307 [DITHER_DIFFUSION] = { "Diffusion", -1 },
308 };
309
310 rb->set_option("Dithering", &jpeg_settings.dither_mode, INT,
311 dithering, DITHER_NUM_MODES, NULL);
312 return false;
313}
314
315MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale",
316 set_option_grayscale, NULL, NULL, Icon_NOICON);
317MENUITEM_FUNCTION(dithering_item, 0, "Dithering",
318 set_option_dithering, NULL, NULL, Icon_NOICON);
319MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
320 &grayscale_item, &dithering_item);
321
322static void display_options(void)
323{
324 rb->do_menu(&display_menu, NULL, NULL, false);
325}
326#endif /* HAVE_LCD_COLOR */
327
328int show_menu(void) /* return 1 to quit */
329{
330#if LCD_DEPTH > 1
331 rb->lcd_set_backdrop(old_backdrop);
332#ifdef HAVE_LCD_COLOR
333 rb->lcd_set_foreground(rb->global_settings->fg_color);
334 rb->lcd_set_background(rb->global_settings->bg_color);
335#else
336 rb->lcd_set_foreground(LCD_BLACK);
337 rb->lcd_set_background(LCD_WHITE);
338#endif
339#endif
340 int result;
341
342 enum menu_id
343 {
344 MIID_RETURN = 0,
345 MIID_TOGGLE_SS_MODE,
346 MIID_CHANGE_SS_MODE,
347#if PLUGIN_BUFFER_SIZE >= MIN_MEM
348 MIID_SHOW_PLAYBACK_MENU,
349#endif
350#ifdef HAVE_LCD_COLOR
351 MIID_DISPLAY_OPTIONS,
352#endif
353 MIID_QUIT,
354 };
355
356 MENUITEM_STRINGLIST(menu, "Jpeg Menu", NULL,
357 "Return", "Toggle Slideshow Mode",
358 "Change Slideshow Time",
359#if PLUGIN_BUFFER_SIZE >= MIN_MEM
360 "Show Playback Menu",
361#endif
362#ifdef HAVE_LCD_COLOR
363 "Display Options",
364#endif
365 "Quit");
366
367 static const struct opt_items slideshow[2] = {
368 { "Disable", -1 },
369 { "Enable", -1 },
370 };
371
372 result=rb->do_menu(&menu, NULL, NULL, false);
373
374 switch (result)
375 {
376 case MIID_RETURN:
377 break;
378 case MIID_TOGGLE_SS_MODE:
379 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
380 slideshow , 2, NULL);
381 break;
382 case MIID_CHANGE_SS_MODE:
383 rb->set_int("Slideshow Time", "s", UNIT_SEC,
384 &jpeg_settings.ss_timeout, NULL, 1,
385 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
386 break;
387
388#if PLUGIN_BUFFER_SIZE >= MIN_MEM
389 case MIID_SHOW_PLAYBACK_MENU:
390 if (plug_buf)
391 {
392 playback_control(NULL);
393 }
394 else
395 {
396 rb->splash(HZ, "Cannot restart playback");
397 }
398 break;
399#endif
400#ifdef HAVE_LCD_COLOR
401 case MIID_DISPLAY_OPTIONS:
402 display_options();
403 break;
404#endif
405 case MIID_QUIT:
406 return 1;
407 break;
408 }
409
410#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
411 /* change ata spindown time based on slideshow time setting */
412 immediate_ata_off = false;
413 rb->storage_spindown(rb->global_settings->disk_spindown);
414
415 if (slideshow_enabled)
416 {
417 if(jpeg_settings.ss_timeout < 10)
418 {
419 /* slideshow times < 10s keep disk spinning */
420 rb->storage_spindown(0);
421 }
422 else if (!rb->mp3_is_playing())
423 {
424 /* slideshow times > 10s and not playing: ata_off after load */
425 immediate_ata_off = true;
426 }
427 }
428#endif
429#if LCD_DEPTH > 1
430 rb->lcd_set_backdrop(NULL);
431 rb->lcd_set_foreground(LCD_WHITE);
432 rb->lcd_set_background(LCD_BLACK);
433#endif
434 rb->lcd_clear_display();
435 return 0;
436}
437
438void draw_image_rect(struct t_disp* pdisp, int x, int y, int width, int height)
439{
440#ifdef HAVE_LCD_COLOR
441 yuv_bitmap_part(
442 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
443 pdisp->x + x, pdisp->y + y, pdisp->stride,
444 x + MAX(0, (LCD_WIDTH - pdisp->width) / 2),
445 y + MAX(0, (LCD_HEIGHT - pdisp->height) / 2),
446 width, height,
447 jpeg_settings.colour_mode, jpeg_settings.dither_mode);
448#else
449 MYXLCD(gray_bitmap_part)(
450 pdisp->bitmap[0], pdisp->x + x, pdisp->y + y, pdisp->stride,
451 x + MAX(0, (LCD_WIDTH-pdisp->width)/2),
452 y + MAX(0, (LCD_HEIGHT-pdisp->height)/2),
453 width, height);
454#endif
455}
456
457/* Pan the viewing window right - move image to the left and fill in
458 the right-hand side */
459static void pan_view_right(struct t_disp* pdisp)
460{
461 int move;
462
463 move = MIN(HSCROLL, pdisp->width - pdisp->x - LCD_WIDTH);
464 if (move > 0)
465 {
466 MYXLCD(scroll_left)(move); /* scroll left */
467 pdisp->x += move;
468 draw_image_rect(pdisp, LCD_WIDTH - move, 0, move, pdisp->height-pdisp->y);
469 MYLCD_UPDATE();
470 }
471}
472
473/* Pan the viewing window left - move image to the right and fill in
474 the left-hand side */
475static void pan_view_left(struct t_disp* pdisp)
476{
477 int move;
478
479 move = MIN(HSCROLL, pdisp->x);
480 if (move > 0)
481 {
482 MYXLCD(scroll_right)(move); /* scroll right */
483 pdisp->x -= move;
484 draw_image_rect(pdisp, 0, 0, move, pdisp->height-pdisp->y);
485 MYLCD_UPDATE();
486 }
487}
488
489/* Pan the viewing window up - move image down and fill in
490 the top */
491static void pan_view_up(struct t_disp* pdisp)
492{
493 int move;
494
495 move = MIN(VSCROLL, pdisp->y);
496 if (move > 0)
497 {
498 MYXLCD(scroll_down)(move); /* scroll down */
499 pdisp->y -= move;
500#ifdef HAVE_LCD_COLOR
501 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
502 {
503 /* Draw over the band at the top of the last update
504 caused by lack of error history on line zero. */
505 move = MIN(move + 1, pdisp->y + pdisp->height);
506 }
507#endif
508 draw_image_rect(pdisp, 0, 0, pdisp->width-pdisp->x, move);
509 MYLCD_UPDATE();
510 }
511}
512
513/* Pan the viewing window down - move image up and fill in
514 the bottom */
515static void pan_view_down(struct t_disp* pdisp)
516{
517 int move;
518
519 move = MIN(VSCROLL, pdisp->height - pdisp->y - LCD_HEIGHT);
520 if (move > 0)
521 {
522 MYXLCD(scroll_up)(move); /* scroll up */
523 pdisp->y += move;
524#ifdef HAVE_LCD_COLOR
525 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
526 {
527 /* Save the line that was on the last line of the display
528 and draw one extra line above then recover the line with
529 image data that had an error history when it was drawn.
530 */
531 move++, pdisp->y--;
532 rb->memcpy(rgb_linebuf,
533 rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
534 LCD_WIDTH*sizeof (fb_data));
535 }
536#endif
537
538 draw_image_rect(pdisp, 0, LCD_HEIGHT - move, pdisp->width-pdisp->x, move);
539
540#ifdef HAVE_LCD_COLOR
541 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
542 {
543 /* Cover the first row drawn with previous image data. */
544 rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
545 rgb_linebuf, LCD_WIDTH*sizeof (fb_data));
546 pdisp->y++;
547 }
548#endif
549 MYLCD_UPDATE();
550 }
551}
552
553/* interactively scroll around the image */
554int scroll_bmp(struct t_disp* pdisp)
555{
556 int button;
557 int lastbutton = 0;
558
559 while (true)
560 {
561 if (slideshow_enabled)
562 button = rb->button_get_w_tmo(jpeg_settings.ss_timeout * HZ);
563 else
564 button = rb->button_get(true);
565
566 running_slideshow = false;
567
568 switch(button)
569 {
570 case JPEG_LEFT:
571 if (entries > 1 && pdisp->width <= LCD_WIDTH
572 && pdisp->height <= LCD_HEIGHT)
573 return change_filename(DIR_PREV);
574 case JPEG_LEFT | BUTTON_REPEAT:
575 pan_view_left(pdisp);
576 break;
577
578 case JPEG_RIGHT:
579 if (entries > 1 && pdisp->width <= LCD_WIDTH
580 && pdisp->height <= LCD_HEIGHT)
581 return change_filename(DIR_NEXT);
582 case JPEG_RIGHT | BUTTON_REPEAT:
583 pan_view_right(pdisp);
584 break;
585
586 case JPEG_UP:
587 case JPEG_UP | BUTTON_REPEAT:
588 pan_view_up(pdisp);
589 break;
590
591 case JPEG_DOWN:
592 case JPEG_DOWN | BUTTON_REPEAT:
593 pan_view_down(pdisp);
594 break;
595
596 case BUTTON_NONE:
597 if (!slideshow_enabled)
598 break;
599 running_slideshow = true;
600 if (entries > 1)
601 return change_filename(DIR_NEXT);
602 break;
603
604#ifdef JPEG_SLIDE_SHOW
605 case JPEG_SLIDE_SHOW:
606 slideshow_enabled = !slideshow_enabled;
607 running_slideshow = slideshow_enabled;
608 break;
609#endif
610
611#ifdef JPEG_NEXT_REPEAT
612 case JPEG_NEXT_REPEAT:
613#endif
614 case JPEG_NEXT:
615 if (entries > 1)
616 return change_filename(DIR_NEXT);
617 break;
618
619#ifdef JPEG_PREVIOUS_REPEAT
620 case JPEG_PREVIOUS_REPEAT:
621#endif
622 case JPEG_PREVIOUS:
623 if (entries > 1)
624 return change_filename(DIR_PREV);
625 break;
626
627 case JPEG_ZOOM_IN:
628#ifdef JPEG_ZOOM_PRE
629 if (lastbutton != JPEG_ZOOM_PRE)
630 break;
631#endif
632 return ZOOM_IN;
633 break;
634
635 case JPEG_ZOOM_OUT:
636#ifdef JPEG_ZOOM_PRE
637 if (lastbutton != JPEG_ZOOM_PRE)
638 break;
639#endif
640 return ZOOM_OUT;
641 break;
642#ifdef JPEG_RC_MENU
643 case JPEG_RC_MENU:
644#endif
645 case JPEG_MENU:
646#ifdef USEGSLIB
647 grey_show(false); /* switch off greyscale overlay */
648#endif
649 if (show_menu() == 1)
650 return PLUGIN_OK;
651
652#ifdef USEGSLIB
653 grey_show(true); /* switch on greyscale overlay */
654#else
655 draw_image_rect(pdisp, 0, 0,
656 pdisp->width-pdisp->x, pdisp->height-pdisp->y);
657 MYLCD_UPDATE();
658#endif
659 break;
660 default:
661 if (rb->default_event_handler_ex(button, cleanup, NULL)
662 == SYS_USB_CONNECTED)
663 return PLUGIN_USB_CONNECTED;
664 break;
665
666 } /* switch */
667
668 if (button != BUTTON_NONE)
669 lastbutton = button;
670 } /* while (true) */
671}
672
673/********************* main function *************************/
674
675/* callback updating a progress meter while JPEG decoding */
676void cb_progress(int current, int total)
677{
678 rb->yield(); /* be nice to the other threads */
679 if(!running_slideshow)
680 {
681 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
682 0, LCD_HEIGHT-8, LCD_WIDTH, 8,
683 total, 0, current, HORIZONTAL);
684 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
685 }
686#ifndef USEGSLIB
687 else
688 {
689 /* in slideshow mode, keep gui interference to a minimum */
690 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
691 0, LCD_HEIGHT-4, LCD_WIDTH, 4,
692 total, 0, current, HORIZONTAL);
693 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
694 }
695#endif
696}
697
698int jpegmem(struct jpeg *p_jpg, int ds)
699{
700 int size;
701
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 */
706 {
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]);
711 }
712#endif
713 return size;
714}
715
716/* how far can we zoom in without running out of memory */
717int min_downscale(struct jpeg *p_jpg, int bufsize)
718{
719 int downscale = 8;
720
721 if (jpegmem(p_jpg, 8) > bufsize)
722 return 0; /* error, too large, even 1:8 doesn't fit */
723
724 while (downscale > 1 && jpegmem(p_jpg, downscale/2) <= bufsize)
725 downscale /= 2;
726
727 return downscale;
728}
729
730/* how far can we zoom out, to fit image into the LCD */
731int max_downscale(struct jpeg *p_jpg)
732{
733 int downscale = 1;
734
735 while (downscale < 8 && (p_jpg->x_size/downscale > LCD_WIDTH
736 || p_jpg->y_size/downscale > LCD_HEIGHT))
737 {
738 downscale *= 2;
739 }
740
741 return downscale;
742}
743
744/* load image from filename. */
745int load_image(char* filename, struct jpeg *p_jpg)
746{
747 int fd;
748 int filesize;
749 unsigned char* buf_jpeg; /* compressed JPEG image */
750 int status;
751
752 fd = rb->open(filename, O_RDONLY);
753 if (fd < 0)
754 {
755 rb->splashf(HZ, "err opening %s:%d", filename, fd);
756 return PLUGIN_ERROR;
757 }
758 filesize = rb->filesize(fd);
759
760 /* allocate JPEG buffer */
761 buf_jpeg = buf;
762
763 /* we can start the decompressed images behind it */
764 buf_images = buf_root = buf + filesize;
765 buf_images_size = root_size = buf_size - filesize;
766
767 if (buf_images_size <= 0)
768 {
769 rb->close(fd);
770 return PLUGIN_OUTOFMEM;
771 }
772
773 if(!running_slideshow)
774 {
775 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
776 rb->lcd_puts(0, 0, print);
777 rb->lcd_update();
778
779 rb->snprintf(print, sizeof(print), "loading %d bytes", filesize);
780 rb->lcd_puts(0, 1, print);
781 rb->lcd_update();
782 }
783
784 rb->read(fd, buf_jpeg, filesize);
785 rb->close(fd);
786
787 if(!running_slideshow)
788 {
789 rb->snprintf(print, sizeof(print), "decoding markers");
790 rb->lcd_puts(0, 2, print);
791 rb->lcd_update();
792 }
793#ifndef SIMULATOR
794 else if(immediate_ata_off)
795 {
796 /* running slideshow and time is long enough: power down disk */
797 rb->storage_sleep();
798 }
799#endif
800
801 /* process markers, unstuffing */
802 status = process_markers(buf_jpeg, filesize, p_jpg);
803
804 if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0))
805 { /* bad format or minimum components not contained */
806 rb->splashf(HZ, "unsupported %d", status);
807 return PLUGIN_ERROR;
808 }
809
810 if (!(status & DHT)) /* if no Huffman table present: */
811 default_huff_tbl(p_jpg); /* use default */
812 build_lut(p_jpg); /* derive Huffman and other lookup-tables */
813
814 if(!running_slideshow)
815 {
816 rb->snprintf(print, sizeof(print), "image %dx%d",
817 p_jpg->x_size, p_jpg->y_size);
818 rb->lcd_puts(0, 2, print);
819 rb->lcd_update();
820 }
821
822 return PLUGIN_OK;
823}
824
825/* return decoded or cached image */
826struct t_disp* get_image(struct jpeg* p_jpg, int ds)
827{
828 int w, h; /* used to center output */
829 int size; /* decompressed image size */
830 long time; /* measured ticks */
831 int status;
832
833 struct t_disp* p_disp = &disp[ds]; /* short cut */
834
835 if (p_disp->bitmap[0] != NULL)
836 {
837 return p_disp; /* we still have it */
838 }
839
840 /* assign image buffer */
841
842 /* physical size needed for decoding */
843 size = jpegmem(p_jpg, ds);
844 if (buf_images_size <= size)
845 { /* have to discard the current */
846 int i;
847 for (i=1; i<=8; i++)
848 disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */
849 buf_images = buf_root; /* start again from the beginning of the buffer */
850 buf_images_size = root_size;
851 }
852
853#ifdef HAVE_LCD_COLOR
854 if (p_jpg->blocks > 1) /* colour jpeg */
855 {
856 int i;
857
858 for (i = 1; i < 3; i++)
859 {
860 size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i])
861 * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]);
862 p_disp->bitmap[i] = buf_images;
863 buf_images += size;
864 buf_images_size -= size;
865 }
866 p_disp->csub_x = p_jpg->subsample_x[1];
867 p_disp->csub_y = p_jpg->subsample_y[1];
868 }
869 else
870 {
871 p_disp->csub_x = p_disp->csub_y = 0;
872 p_disp->bitmap[1] = p_disp->bitmap[2] = buf_images;
873 }
874#endif
875 /* size may be less when decoded (if height is not block aligned) */
876 size = (p_jpg->x_phys/ds) * (p_jpg->y_size / ds);
877 p_disp->bitmap[0] = buf_images;
878 buf_images += size;
879 buf_images_size -= size;
880
881 if(!running_slideshow)
882 {
883 rb->snprintf(print, sizeof(print), "decoding %d*%d",
884 p_jpg->x_size/ds, p_jpg->y_size/ds);
885 rb->lcd_puts(0, 3, print);
886 rb->lcd_update();
887 }
888
889 /* update image properties */
890 p_disp->width = p_jpg->x_size / ds;
891 p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */
892 p_disp->height = p_jpg->y_size / ds;
893
894 /* the actual decoding */
895 time = *rb->current_tick;
896#ifdef HAVE_ADJUSTABLE_CPU_FREQ
897 rb->cpu_boost(true);
898 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress);
899 rb->cpu_boost(false);
900#else
901 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress);
902#endif
903 if (status)
904 {
905 rb->splashf(HZ, "decode error %d", status);
906 return NULL;
907 }
908 time = *rb->current_tick - time;
909
910 if(!running_slideshow)
911 {
912 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
913 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
914 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
915 rb->lcd_update();
916 }
917
918 return p_disp;
919}
920
921
922/* set the view to the given center point, limit if necessary */
923void set_view (struct t_disp* p_disp, int cx, int cy)
924{
925 int x, y;
926
927 /* plain center to available width/height */
928 x = cx - MIN(LCD_WIDTH, p_disp->width) / 2;
929 y = cy - MIN(LCD_HEIGHT, p_disp->height) / 2;
930
931 /* limit against upper image size */
932 x = MIN(p_disp->width - LCD_WIDTH, x);
933 y = MIN(p_disp->height - LCD_HEIGHT, y);
934
935 /* limit against negative side */
936 x = MAX(0, x);
937 y = MAX(0, y);
938
939 p_disp->x = x; /* set the values */
940 p_disp->y = y;
941}
942
943/* calculate the view center based on the bitmap position */
944void get_view(struct t_disp* p_disp, int* p_cx, int* p_cy)
945{
946 *p_cx = p_disp->x + MIN(LCD_WIDTH, p_disp->width) / 2;
947 *p_cy = p_disp->y + MIN(LCD_HEIGHT, p_disp->height) / 2;
948}
949
950/* load, decode, display the image */
951int load_and_show(char* filename)
952{
953 int status;
954 struct t_disp* p_disp; /* currenly displayed image */
955 int cx, cy; /* view center */
956
957#if LCD_DEPTH > 1
958 rb->lcd_set_foreground(LCD_WHITE);
959 rb->lcd_set_background(LCD_BLACK);
960 rb->lcd_set_backdrop(NULL);
961#endif
962 rb->lcd_clear_display();
963
964 rb->memset(&disp, 0, sizeof(disp));
965 rb->memset(&jpg, 0, sizeof(jpg)); /* clear info struct */
966
967 if (rb->button_get(false) == JPEG_MENU)
968 status = PLUGIN_ABORT;
969 else
970 status = load_image(filename, &jpg);
971
972 if (status == PLUGIN_OUTOFMEM)
973 {
974#if PLUGIN_BUFFER_SIZE >= MIN_MEM
975 if(plug_buf)
976 {
977 rb->lcd_setfont(FONT_SYSFIXED);
978 rb->lcd_clear_display();
979 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
980 rb->lcd_puts(0,0,print);
981 rb->lcd_puts(0,1,"Not enough plugin memory!");
982 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
983 if(entries>1)
984 rb->lcd_puts(0,3,"Left/Right: Skip File.");
985 rb->lcd_puts(0,4,"Show Menu: Quit.");
986 rb->lcd_update();
987 rb->lcd_setfont(FONT_UI);
988
989 rb->button_clear_queue();
990
991 while (1)
992 {
993 int button = rb->button_get(true);
994 switch(button)
995 {
996 case JPEG_ZOOM_IN:
997 plug_buf = false;
998 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
999 /*try again this file, now using the audio buffer */
1000 return PLUGIN_OTHER;
1001#ifdef JPEG_RC_MENU
1002 case JPEG_RC_MENU:
1003#endif
1004 case JPEG_MENU:
1005 return PLUGIN_OK;
1006
1007 case JPEG_LEFT:
1008 if(entries>1)
1009 {
1010 rb->lcd_clear_display();
1011 return change_filename(DIR_PREV);
1012 }
1013 break;
1014
1015 case JPEG_RIGHT:
1016 if(entries>1)
1017 {
1018 rb->lcd_clear_display();
1019 return change_filename(DIR_NEXT);
1020 }
1021 break;
1022 default:
1023 if(rb->default_event_handler_ex(button, cleanup, NULL)
1024 == SYS_USB_CONNECTED)
1025 return PLUGIN_USB_CONNECTED;
1026
1027 }
1028 }
1029 }
1030 else
1031#endif
1032 {
1033 rb->splash(HZ, "Out of Memory");
1034 file_pt[curfile] = NULL;
1035 return change_filename(direction);
1036 }
1037 }
1038 else if (status == PLUGIN_ERROR)
1039 {
1040 file_pt[curfile] = NULL;
1041 return change_filename(direction);
1042 }
1043 else if (status == PLUGIN_ABORT) {
1044 rb->splash(HZ, "aborted");
1045 return PLUGIN_OK;
1046 }
1047
1048 ds_max = max_downscale(&jpg); /* check display constraint */
1049 ds_min = min_downscale(&jpg, buf_images_size); /* check memory constraint */
1050 if (ds_min == 0)
1051 {
1052 rb->splash(HZ, "too large");
1053 file_pt[curfile] = NULL;
1054 return change_filename(direction);
1055 }
1056 else if (ds_max < ds_min)
1057 ds_max = ds_min;
1058
1059 ds = ds_max; /* initialize setting */
1060 cx = jpg.x_size/ds/2; /* center the view */
1061 cy = jpg.y_size/ds/2;
1062
1063 do /* loop the image prepare and decoding when zoomed */
1064 {
1065 p_disp = get_image(&jpg, ds); /* decode or fetch from cache */
1066 if (p_disp == NULL)
1067 {
1068 file_pt[curfile] = NULL;
1069 return change_filename(direction);
1070 }
1071
1072 set_view(p_disp, cx, cy);
1073
1074 if(!running_slideshow)
1075 {
1076 rb->snprintf(print, sizeof(print), "showing %dx%d",
1077 p_disp->width, p_disp->height);
1078 rb->lcd_puts(0, 3, print);
1079 rb->lcd_update();
1080 }
1081
1082 MYLCD(clear_display)();
1083 draw_image_rect(p_disp, 0, 0,
1084 p_disp->width-p_disp->x, p_disp->height-p_disp->y);
1085 MYLCD_UPDATE();
1086
1087#ifdef USEGSLIB
1088 grey_show(true); /* switch on greyscale overlay */
1089#endif
1090
1091 /* drawing is now finished, play around with scrolling
1092 * until you press OFF or connect USB
1093 */
1094 while (1)
1095 {
1096 status = scroll_bmp(p_disp);
1097 if (status == ZOOM_IN)
1098 {
1099 if (ds > ds_min)
1100 {
1101 ds /= 2; /* reduce downscaling to zoom in */
1102 get_view(p_disp, &cx, &cy);
1103 cx *= 2; /* prepare the position in the new image */
1104 cy *= 2;
1105 }
1106 else
1107 continue;
1108 }
1109
1110 if (status == ZOOM_OUT)
1111 {
1112 if (ds < ds_max)
1113 {
1114 ds *= 2; /* increase downscaling to zoom out */
1115 get_view(p_disp, &cx, &cy);
1116 cx /= 2; /* prepare the position in the new image */
1117 cy /= 2;
1118 }
1119 else
1120 continue;
1121 }
1122 break;
1123 }
1124
1125#ifdef USEGSLIB
1126 grey_show(false); /* switch off overlay */
1127#endif
1128 rb->lcd_clear_display();
1129 }
1130 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
1131 && status != PLUGIN_OTHER);
1132#ifdef USEGSLIB
1133 rb->lcd_update();
1134#endif
1135 return status;
1136}
1137
1138/******************** Plugin entry point *********************/
1139
1140enum plugin_status plugin_start(const void* parameter)
1141{
1142 int condition;
1143#ifdef USEGSLIB
1144 long greysize; /* helper */
1145#endif
1146#if LCD_DEPTH > 1
1147 old_backdrop = rb->lcd_get_backdrop();
1148#endif
1149
1150 if(!parameter) return PLUGIN_ERROR;
1151
1152#if PLUGIN_BUFFER_SIZE >= MIN_MEM
1153 buf = rb->plugin_get_buffer((size_t *)&buf_size);
1154#else
1155 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1156#endif
1157
1158 rb->strcpy(np_file, parameter);
1159 get_pic_list();
1160
1161 if(!entries) return PLUGIN_ERROR;
1162
1163#if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
1164 if(!rb->audio_status())
1165 {
1166 plug_buf = false;
1167 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
1168 }
1169#endif
1170
1171#ifdef USEGSLIB
1172 if (!grey_init(buf, buf_size, GREY_ON_COP,
1173 LCD_WIDTH, LCD_HEIGHT, &greysize))
1174 {
1175 rb->splash(HZ, "grey buf error");
1176 return PLUGIN_ERROR;
1177 }
1178 buf += greysize;
1179 buf_size -= greysize;
1180#endif
1181
1182 /* should be ok to just load settings since the plugin itself has
1183 just been loaded from disk and the drive should be spinning */
1184 configfile_load(JPEG_CONFIGFILE, jpeg_config,
1185 ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION);
1186 old_settings = jpeg_settings;
1187
1188 /* Turn off backlight timeout */
1189 backlight_force_on(); /* backlight control in lib/helper.c */
1190
1191 do
1192 {
1193 condition = load_and_show(np_file);
1194 } while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
1195 && condition != PLUGIN_ERROR);
1196
1197 if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings)))
1198 {
1199 /* Just in case drive has to spin, keep it from looking locked */
1200 rb->splash(0, "Saving Settings");
1201 configfile_save(JPEG_CONFIGFILE, jpeg_config,
1202 ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION);
1203 }
1204
1205#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1206 /* set back ata spindown time in case we changed it */
1207 rb->storage_spindown(rb->global_settings->disk_spindown);
1208#endif
1209
1210 /* Turn on backlight timeout (revert to settings) */
1211 backlight_use_settings(); /* backlight control in lib/helper.c */
1212
1213#ifdef USEGSLIB
1214 grey_release(); /* deinitialize */
1215#endif
1216
1217 return condition;
1218}