# change to bmp.c is to load bitmap larger than LCD correctly. Index: apps/recorder/bmp.c =================================================================== --- apps/recorder/bmp.c (revision 24284) +++ apps/recorder/bmp.c (working copy) @@ -621,6 +621,8 @@ defined(HAVE_BMP_SCALING) || defined(PLUGIN) if(resize) totalsize += BM_SCALED_SIZE(bm->width, 0, 0, 0); + else if (bm->width > BM_MAX_WIDTH) + totalsize += bm->width*4; #endif return totalsize; } @@ -717,9 +719,7 @@ #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ defined(HAVE_BMP_SCALING) || defined(PLUGIN) -#if LCD_DEPTH > 1 && defined(HAVE_BMP_SCALING) if (resize) -#endif { if (resize_on_load(bm, dither, &src_dim, &rset, bitmap + totalsize, maxsize - totalsize, @@ -749,17 +749,51 @@ #endif #endif + unsigned char *buf = ba.buf; +#if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ + defined(PLUGIN) + if (bm->width > BM_MAX_WIDTH) + { +#if defined(HAVE_BMP_SCALING) || defined(PLUGIN) + buf = bitmap + totalsize; + unsigned int len = maxsize - totalsize; + ALIGN_BUFFER(buf, len, sizeof(uint32_t)); + if (bm->width*4 > (int)len) +#endif + return -6; + } +#endif + int row; /* loop to read rows and put them to buffer */ for (row = rset.rowstart; row != rset.rowstop; row += rset.rowstep) { +#if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ + defined(HAVE_BMP_SCALING) || defined(PLUGIN) + if (bm->width > BM_MAX_WIDTH) + { +#if defined(HAVE_LCD_COLOR) + struct uint8_rgb *p = (struct uint8_rgb *)buf; +#else + uint8_t* p = buf; +#endif + do { + int len = read_part_line(&ba); + if (!len) + return -9; + memcpy(p, ba.buf, len*sizeof(*p)); + p += len; + } while (ba.cur_col); + } + else +#endif if (!read_part_line(&ba)) return -9; #ifndef PLUGIN #if !defined(HAVE_LCD_COLOR) && \ (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) - uint8_t* qp = ba.buf; + uint8_t* qp = buf; #else - struct uint8_rgb *qp = (struct uint8_rgb *)ba.buf; + struct uint8_rgb *qp = (struct uint8_rgb *)buf; #endif #endif /* Convert to destination format */ @@ -798,9 +832,9 @@ #if LCD_DEPTH > 1 || defined(PLUGIN) { #if !defined(PLUGIN) && !defined(HAVE_JPEG) && !defined(HAVE_BMP_SCALING) - output_row_8_native(row, ba.buf, &ctx); + output_row_8_native(row, buf, &ctx); #else - output_row_8(row, ba.buf, &ctx); + output_row_8(row, buf, &ctx); #endif } #endif Index: apps/plugins/plugin.lds =================================================================== --- apps/plugins/plugin.lds (revision 24284) +++ apps/plugins/plugin.lds (working copy) @@ -176,6 +176,9 @@ #elif defined OVERLAY_OFFSET #define THIS_LENGTH (DRAMSIZE - OVERLAY_OFFSET) #define THIS_ORIGIN (DRAMORIG + OVERLAY_OFFSET) +#elif defined IMGVDECODER_OFFSET +#define THIS_LENGTH (PLUGIN_LENGTH - IMGVDECODER_OFFSET) +#define THIS_ORIGIN (PLUGIN_ORIGIN + IMGVDECODER_OFFSET) #else /* plugin */ #define THIS_LENGTH PLUGIN_LENGTH #define THIS_ORIGIN PLUGIN_ORIGIN Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 24284) +++ apps/plugins/CATEGORIES (working copy) @@ -32,12 +32,15 @@ goban,games greyscale,demos helloworld,demos +imageviewer,viewers +imageviewer_bmp,viewers +imageviewer_jpeg,viewers +imageviewer_png,viewers invadrox,games iriver_flash,apps iriverify,viewers jackpot,games jewels,games -jpeg,viewers keybox,apps lamp,apps logo,demos @@ -63,7 +66,6 @@ pictureflow,demos pitch_detector,apps plasma,demos -png,viewers pong,games ppmviewer,viewers properties,viewers Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 24284) +++ apps/plugins/viewers.config (working copy) @@ -2,6 +2,12 @@ txt,viewers/viewer,1 nfo,viewers/viewer,1 txt,apps/text_editor,2 +bmp,viewers/imageviewer,2 +jpg,viewers/imageviewer,2 +jpe,viewers/imageviewer,2 +jpeg,viewers/imageviewer,2 +png,viewers/imageviewer,2 +bmp,viewers/bmp,2 jpg,viewers/jpeg,2 jpe,viewers/jpeg,2 jpeg,viewers/jpeg,2 Index: apps/plugins/imageviewer/imageviewer.make =================================================================== --- apps/plugins/imageviewer/imageviewer.make (revision 24284) +++ apps/plugins/imageviewer/imageviewer.make (working copy) @@ -10,7 +10,29 @@ IMGVSRCDIR := $(APPSDIR)/plugins/imageviewer IMGVBUILDDIR := $(BUILDDIR)/apps/plugins/imageviewer -# include actual viewer's make file +IMGDECFLAGS = $(PLUGINFLAGS) -DIMGDEC + +ROCKS += $(IMGVBUILDDIR)/imageviewer.rock + +IMGV_SRC := $(call preprocess, $(IMGVSRCDIR)/SOURCES) + +# compile all decoder in one binary for simulator. +# so include decoder make file first. IMGVSUBDIRS := $(call preprocess, $(IMGVSRCDIR)/SUBDIRS) $(foreach dir,$(IMGVSUBDIRS),$(eval include $(dir)/$(notdir $(dir)).make)) +IMGV_OBJ := $(call c2obj, $(IMGV_SRC)) + +# add source files to OTHER_SRC to get automatic dependencies +OTHER_SRC += $(IMGV_SRC) + +$(IMGVBUILDDIR)/imageviewer.rock: $(IMGV_OBJ) + +IMGDECLDFLAGS = -T$(PLUGINLINK_LDS) -Wl,--gc-sections -Wl,-Map,$(IMGVBUILDDIR)/$*.refmap + +# rule to create reference map for image decoder +$(IMGVBUILDDIR)/%.refmap: $(APPSDIR)/plugin.h $(IMGVSRCDIR)/imageviewer.h $(PLUGINLINK_LDS) $(PLUGINLIB) $(PLUGINBITMAPLIB) + $(call PRINTS,LD $(@F))$(CC) $(IMGDECFLAGS) -o /dev/null \ + $(filter %.o, $^) \ + $(filter %.a, $+) \ + -lgcc $(IMGDECLDFLAGS) Index: apps/plugins/imageviewer/SOURCES =================================================================== --- apps/plugins/imageviewer/SOURCES (revision 0) +++ apps/plugins/imageviewer/SOURCES (revision 0) @@ -0,0 +1,2 @@ +imageviewer.c +load_decoder.c Index: apps/plugins/imageviewer/png/SOURCES =================================================================== --- apps/plugins/imageviewer/png/SOURCES (revision 24284) +++ apps/plugins/imageviewer/png/SOURCES (working copy) @@ -4,4 +4,3 @@ inflate.c inftrees.c png.c -png_ui.c Index: apps/plugins/imageviewer/png/png.make =================================================================== --- apps/plugins/imageviewer/png/png.make (revision 24284) +++ apps/plugins/imageviewer/png/png.make (working copy) @@ -10,19 +10,37 @@ PNGSRCDIR := $(IMGVSRCDIR)/png PNGBUILDDIR := $(IMGVBUILDDIR)/png -ROCKS += $(PNGBUILDDIR)/png.rock - PNG_SRC := $(call preprocess, $(PNGSRCDIR)/SOURCES) PNG_OBJ := $(call c2obj, $(PNG_SRC)) -# add source files to OTHER_SRC to get automatic dependencies -OTHER_SRC += $(PNG_SRC) +ifndef SIMVER + # add source files to OTHER_SRC to get automatic dependencies + OTHER_SRC += $(PNG_SRC) + ROCKS += $(PNGBUILDDIR)/imageviewer_png.ovl +else + # compile all decoder in one binary for simulator. + IMGV_SRC += $(PNG_SRC) +endif +PNG_OUTLDS = $(PNGBUILDDIR)/png.link +PNG_OVLFLAGS = -T$(PNG_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map + +$(PNGBUILDDIR)/png.refmap: $(PNG_OBJ) + +$(PNG_OUTLDS): $(PLUGIN_LDS) $(PNGBUILDDIR)/png.refmap + $(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DIMGVDECODER_OFFSET=$(shell \ + $(TOOLSDIR)/ovl_offset.pl $(PNGBUILDDIR)/png.refmap)) + +$(PNGBUILDDIR)/imageviewer_png.ovl: $(PNG_OBJ) $(PNG_OUTLDS) + $(SILENT)$(CC) $(IMGDECFLAGS) -o $(basename $@).elf \ + $(filter %.o, $^) \ + $(filter %.a, $+) \ + -lgcc $(PNG_OVLFLAGS) + $(call PRINTS,LD $(@F))$(OC) -O binary $(basename $@).elf $@ + # Use -O3 for png plugin : it gives a bigger file but very good performances -PNGFLAGS = $(PLUGINFLAGS) -O3 -DNO_GZCOMPRESS -DNO_GZIP +PNGFLAGS = $(IMGDECFLAGS) -O3 -DNO_GZCOMPRESS -DNO_GZIP -$(PNGBUILDDIR)/png.rock: $(PNG_OBJ) - # Compile PNG plugin with extra flags (adapted from ZXBox) $(PNGBUILDDIR)/%.o: $(PNGSRCDIR)/%.c $(PNGSRCDIR)/png.make $(SILENT)mkdir -p $(dir $@) Index: apps/plugins/imageviewer/png/png_ui.c =================================================================== --- apps/plugins/imageviewer/png/png_ui.c (revision 24284) +++ apps/plugins/imageviewer/png/png_ui.c (working copy) @@ -1,5 +0,0 @@ -#define PNG_VIEWER -#define MENU_TITLE "Png Menu" -#define UNSCALED_IS_AVAILABLE 1 - -#include "../imageviewer.c" Index: apps/plugins/imageviewer/png/inflate.c =================================================================== --- apps/plugins/imageviewer/png/inflate.c (revision 24284) +++ apps/plugins/imageviewer/png/inflate.c (working copy) @@ -1132,7 +1132,7 @@ //DEBUGF("%d / %d\n", strm->total_in, strm->avail_in); if (rb->button_get(false) == IMGVIEW_MENU) return PLUGIN_ABORT; - else cb_progress(insize - strm->avail_in, insize); + else iv->cb_progress(insize - strm->avail_in, insize); } /* Index: apps/plugins/imageviewer/png/png.c =================================================================== --- apps/plugins/imageviewer/png/png.c (revision 24284) +++ apps/plugins/imageviewer/png/png.c (working copy) @@ -144,7 +144,7 @@ static fb_data *disp_buf; /* my memory pool (from the mp3 buffer) */ -static char print[128]; /* use a common snprintf() buffer */ +static char print[32]; /* use a common snprintf() buffer */ unsigned char *memory, *memory_max; static size_t memory_size; @@ -467,10 +467,9 @@ size_t x, y; unsigned char c; - if (!running_slideshow) + if (!iv->running_slideshow) { - rb->snprintf(print, sizeof(print), "color conversion in progress"); - rb->lcd_puts(0, 3, print); + rb->lcd_puts(0, 3, "color conversion in progress"); rb->lcd_update(); } @@ -1199,10 +1198,9 @@ decoded_image = memory_max - decoded_image_size; if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; } memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char)); - if (!running_slideshow) + if (!iv->running_slideshow) { - rb->snprintf(print, sizeof(print), "unfiltering scanlines"); - rb->lcd_puts(0, 3, print); + rb->lcd_puts(0, 3, "unfiltering scanlines"); rb->lcd_update(); } decoder->error = postProcessScanlines(decoded_image, scanlines, decoder); @@ -1296,19 +1294,9 @@ "invalid pHYs chunk size", /*74*/ }; -bool img_ext(const char *ext) +static void draw_image_rect(struct image_info *info, + int x, int y, int width, int height) { - if (!ext) - return false; - if (!rb->strcasecmp(ext,".png")) - return true; - else - return false; -} - -void draw_image_rect(struct image_info *info, - int x, int y, int width, int height) -{ fb_data **pdisp = (fb_data**)info->data; rb->lcd_bitmap_part(*pdisp, info->x + x, info->y + y, info->width, x + MAX(0, (LCD_WIDTH-info->width)/2), @@ -1316,14 +1304,14 @@ width, height); } -int img_mem(int ds) +static int img_mem(int ds) { LodePNG_Decoder *decoder = &_decoder; return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ; } -int load_image(char *filename, struct image_info *info, - unsigned char *buf, ssize_t *buf_size) +static int load_image(char *filename, struct image_info *info, + unsigned char *buf, ssize_t *buf_size) { int fd; long time = 0; /* measured ticks */ @@ -1347,7 +1335,7 @@ DEBUGF("reading file '%s'\n", filename); - if (!running_slideshow) { + if (!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); rb->lcd_puts(0, 0, print); rb->lcd_update(); @@ -1358,7 +1346,7 @@ rb->close(fd); } else { - if (!running_slideshow) { + if (!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size); rb->lcd_puts(0, 1, print); rb->lcd_update(); @@ -1368,13 +1356,13 @@ rb->read(fd, image, image_size); rb->close(fd); - if (!running_slideshow) { + if (!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "decoding image"); rb->lcd_puts(0, 2, print); rb->lcd_update(); } #ifdef DISK_SPINDOWN - else if (immediate_ata_off) { + else if (iv->immediate_ata_off) { /* running slideshow and time is long enough: power down disk */ rb->storage_sleep(); } @@ -1388,7 +1376,7 @@ if (!decoder->error) { - if (!running_slideshow) { + if (!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "image %dx%d", decoder->infoPng.width, decoder->infoPng.height); rb->lcd_puts(0, 2, print); @@ -1404,17 +1392,17 @@ time = *rb->current_tick; #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); - LodePNG_decode(decoder, image, image_size, cb_progress); + LodePNG_decode(decoder, image, image_size, iv->cb_progress); rb->cpu_boost(false); #else - LodePNG_decode(decoder, image, image_size, cb_progress); + LodePNG_decode(decoder, image, image_size, iv->cb_progress); #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ } } time = *rb->current_tick - time; - if (!running_slideshow && !decoder->error) + if (!iv->running_slideshow && !decoder->error) { rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ @@ -1424,7 +1412,7 @@ if (decoder->error) { #if PLUGIN_BUFFER_SIZE >= MIN_MEM - if (plug_buf && (decoder->error == FILE_TOO_LARGE + if (iv->plug_buf && (decoder->error == FILE_TOO_LARGE || decoder->error == OUT_OF_MEMORY || decoder->error == Z_MEM_ERROR)) return PLUGIN_OUTOFMEM; #endif @@ -1464,7 +1452,7 @@ return PLUGIN_OK; } -int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int ds) { fb_data **p_disp = &disp[ds]; /* short cut */ LodePNG_Decoder *decoder = &_decoder; @@ -1481,7 +1469,7 @@ /* assign image buffer */ if (ds > 1) { - if (!running_slideshow) + if (!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "resizing %d*%d", info->width, info->height); @@ -1524,3 +1512,20 @@ return PLUGIN_OK; } + +struct image_decoder png_decoder = { + true, + img_mem, + load_image, + get_image, + draw_image_rect, +}; + +#ifndef SIMULATOR +IMGDEC_HEADER + +struct image_decoder *get_decoder(void) +{ + return &png_decoder; +} +#endif Index: apps/plugins/imageviewer/jpeg/jpeg.make =================================================================== --- apps/plugins/imageviewer/jpeg/jpeg.make (revision 24284) +++ apps/plugins/imageviewer/jpeg/jpeg.make (working copy) @@ -10,12 +10,35 @@ JPEGSRCDIR := $(IMGVSRCDIR)/jpeg JPEGBUILDDIR := $(IMGVBUILDDIR)/jpeg -ROCKS += $(JPEGBUILDDIR)/jpeg.rock - JPEG_SRC := $(call preprocess, $(JPEGSRCDIR)/SOURCES) JPEG_OBJ := $(call c2obj, $(JPEG_SRC)) -# add source files to OTHER_SRC to get automatic dependencies -OTHER_SRC += $(JPEG_SRC) +ifndef SIMVER + # add source files to OTHER_SRC to get automatic dependencies + OTHER_SRC += $(JPEG_SRC) + ROCKS += $(JPEGBUILDDIR)/imageviewer_jpeg.ovl +else + # compile all decoder in one binary for simulator. + IMGV_SRC += $(JPEG_SRC) +endif -$(JPEGBUILDDIR)/jpeg.rock: $(JPEG_OBJ) +JPEG_OUTLDS = $(JPEGBUILDDIR)/jpeg.link +JPEG_OVLFLAGS = -T$(JPEG_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map + +$(JPEGBUILDDIR)/jpeg.refmap: $(JPEG_OBJ) + +$(JPEG_OUTLDS): $(PLUGIN_LDS) $(JPEGBUILDDIR)/jpeg.refmap + $(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DIMGVDECODER_OFFSET=$(shell \ + $(TOOLSDIR)/ovl_offset.pl $(JPEGBUILDDIR)/jpeg.refmap)) + +$(JPEGBUILDDIR)/imageviewer_jpeg.ovl: $(JPEG_OBJ) $(JPEG_OUTLDS) + $(SILENT)$(CC) $(IMGDECFLAGS) -o $(basename $@).elf \ + $(filter %.o, $^) \ + $(filter %.a, $+) \ + -lgcc $(JPEG_OVLFLAGS) + $(call PRINTS,LD $(@F))$(OC) -O binary $(basename $@).elf $@ + +# special pattern rule for compiling image decoder with extra flags +$(JPEGBUILDDIR)/%.o: $(JPEGSRCDIR)/%.c $(JPEGSRCDIR)/jpeg.make + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(IMGDECFLAGS) -c $< -o $@ Index: apps/plugins/imageviewer/jpeg/SOURCES =================================================================== --- apps/plugins/imageviewer/jpeg/SOURCES (revision 24284) +++ apps/plugins/imageviewer/jpeg/SOURCES (working copy) @@ -1,4 +1,3 @@ -jpeg_ui.c jpeg.c jpeg_decoder.c #ifdef HAVE_LCD_COLOR Index: apps/plugins/imageviewer/jpeg/jpeg_ui.c =================================================================== --- apps/plugins/imageviewer/jpeg/jpeg_ui.c (revision 24284) +++ apps/plugins/imageviewer/jpeg/jpeg_ui.c (working copy) @@ -1,5 +0,0 @@ -#define JPEG_VIEWER -#define MENU_TITLE "Jpeg Menu" -#define UNSCALED_IS_AVAILABLE 0 - -#include "../imageviewer.c" Index: apps/plugins/imageviewer/jpeg/jpeg.c =================================================================== --- apps/plugins/imageviewer/jpeg/jpeg.c (revision 24284) +++ apps/plugins/imageviewer/jpeg/jpeg.c (working copy) @@ -69,21 +69,9 @@ /************************* Implementation ***************************/ -bool img_ext(const char *ext) +static void draw_image_rect(struct image_info *info, + int x, int y, int width, int height) { - if(!ext) - return false; - if(!rb->strcasecmp(ext,".jpg") || - !rb->strcasecmp(ext,".jpe") || - !rb->strcasecmp(ext,".jpeg")) - return true; - else - return false; -} - -void draw_image_rect(struct image_info *info, - int x, int y, int width, int height) -{ struct t_disp* pdisp = (struct t_disp*)info->data; #ifdef HAVE_LCD_COLOR yuv_bitmap_part( @@ -92,7 +80,7 @@ x + MAX(0, (LCD_WIDTH - info->width) / 2), y + MAX(0, (LCD_HEIGHT - info->height) / 2), width, height, - settings.jpeg_colour_mode, settings.jpeg_dither_mode); + iv->settings->jpeg_colour_mode, iv->settings->jpeg_dither_mode); #else MYXLCD(gray_bitmap_part)( pdisp->bitmap[0], info->x + x, info->y + y, pdisp->stride, @@ -102,7 +90,7 @@ #endif } -int img_mem(int ds) +static int img_mem(int ds) { int size; struct jpeg *p_jpg = &jpg; @@ -121,8 +109,8 @@ return size; } -int load_image(char *filename, struct image_info *info, - unsigned char *buf, ssize_t *buf_size) +static int load_image(char *filename, struct image_info *info, + unsigned char *buf, ssize_t *buf_size) { int fd; int filesize; @@ -154,7 +142,7 @@ return PLUGIN_OUTOFMEM; } - if(!running_slideshow) + if(!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); rb->lcd_puts(0, 0, print); @@ -168,14 +156,14 @@ rb->read(fd, buf_jpeg, filesize); rb->close(fd); - if(!running_slideshow) + if(!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "decoding markers"); rb->lcd_puts(0, 2, print); rb->lcd_update(); } #ifdef DISK_SPINDOWN - else if(immediate_ata_off) + else if(iv->immediate_ata_off) { /* running slideshow and time is long enough: power down disk */ rb->storage_sleep(); @@ -195,7 +183,7 @@ default_huff_tbl(p_jpg); /* use default */ build_lut(p_jpg); /* derive Huffman and other lookup-tables */ - if(!running_slideshow) + if(!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "image %dx%d", p_jpg->x_size, p_jpg->y_size); @@ -209,7 +197,7 @@ return PLUGIN_OK; } -int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int ds) { int w, h; /* used to center output */ int size; /* decompressed image size */ @@ -269,7 +257,7 @@ buf_images += size; buf_images_size -= size; - if(!running_slideshow) + if(!iv->running_slideshow) { rb->snprintf(print, sizeof(print), "decoding %d*%d", p_jpg->x_size/ds, p_jpg->y_size/ds); @@ -284,10 +272,10 @@ time = *rb->current_tick; #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); - status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress); + status = jpeg_decode(p_jpg, p_disp->bitmap, ds, iv->cb_progress); rb->cpu_boost(false); #else - status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progress); + status = jpeg_decode(p_jpg, p_disp->bitmap, ds, iv->cb_progress); #endif if (status) { @@ -296,7 +284,7 @@ } time = *rb->current_tick - time; - if(!running_slideshow) + if(!iv->running_slideshow) { rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ @@ -306,3 +294,20 @@ return PLUGIN_OK; } + +struct image_decoder jpeg_decoder = { + false, + img_mem, + load_image, + get_image, + draw_image_rect, +}; + +#ifndef SIMULATOR +IMGDEC_HEADER + +struct image_decoder *get_decoder(void) +{ + return &jpeg_decoder; +} +#endif Index: apps/plugins/imageviewer/imageviewer.c =================================================================== --- apps/plugins/imageviewer/imageviewer.c (revision 24284) +++ apps/plugins/imageviewer/imageviewer.c (working copy) @@ -24,6 +24,7 @@ #include #include #include "imageviewer.h" +#include "load_decoder.h" PLUGIN_HEADER @@ -38,16 +39,6 @@ /******************************* Globals ***********************************/ -bool slideshow_enabled = false; /* run slideshow */ -bool running_slideshow = false; /* loading image because of slideshw */ -#ifdef DISK_SPINDOWN -bool immediate_ata_off = false; /* power down disk after loading */ -#endif -#if PLUGIN_BUFFER_SIZE >= MIN_MEM -/* are we using the plugin buffer or the audio buffer? */ -bool plug_buf = true; -#endif - /* Persistent configuration */ #define IMGVIEW_CONFIGFILE "imageviewer.cfg" #define IMGVIEW_SETTINGS_MINVERSION 1 @@ -63,8 +54,7 @@ #include "jpeg/yuv2rgb.h" #endif -/* jpeg use this */ -struct imgview_settings settings = +static struct imgview_settings settings = { #ifdef HAVE_LCD_COLOR COLOURMODE_COLOUR, @@ -90,21 +80,46 @@ static fb_data* old_backdrop; #endif +static void cb_progress(int current, int total); + +static struct imgdec_api iv_api = { + .settings = &settings, + .slideshow_enabled = false, + .running_slideshow = false, +#ifdef DISK_SPINDOWN + .immediate_ata_off = false, +#endif +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + .plug_buf = true, +#endif + + .cb_progress = cb_progress, + +#ifdef USEGSLIB + .gray_bitmap_part = MYXLCD(gray_bitmap_part), +#endif +}; + +const struct imgdec_api *iv = &iv_api; + /**************** begin Application ********************/ /************************* Globals ***************************/ -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) +#if defined(HAVE_LCD_COLOR) static fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when DITHER_DIFFUSION is set */ #endif /* my memory pool (from the mp3 buffer) */ static char print[32]; /* use a common snprintf() buffer */ +/* buffer to load image decoder */ +unsigned char* decoder_buf; +ssize_t decoder_buf_size; /* the remaining free part of the buffer for loaded+resized images */ -static unsigned char* buf; -static ssize_t buf_size; +unsigned char* buf; +ssize_t buf_size; static int ds, ds_min, ds_max; /* downscaling and limits */ static struct image_info image_info; @@ -113,14 +128,48 @@ /* the current full file name */ static char np_file[MAX_PATH]; -static int curfile = 0, direction = DIR_NONE, entries = 0; +static int curfile = -1, direction = DIR_NONE, entries = 0; /* list of the supported image files */ static char **file_pt; +static struct image_decoder *imgdec = NULL; +static enum image_type image_type = IMAGE_UNKNOWN; + /************************* Implementation ***************************/ -/*Read directory contents for scrolling. */ +/* check file type by extention */ +static enum image_type get_image_type(const char *name) +{ + static const struct { + char *ext; + enum image_type type; + } ext_list[] = { + { ".bmp", IMAGE_BMP }, + { ".jpg", IMAGE_JPEG }, + { ".jpe", IMAGE_JPEG }, + { ".jpeg", IMAGE_JPEG }, +#ifdef HAVE_LCD_COLOR + { ".png", IMAGE_PNG }, +#endif + }; + + const char *ext = rb->strrchr(name, '.'); + int i; + if (!ext) + return IMAGE_UNKNOWN; + + for (i = 0; i < (int)ARRAYLEN(ext_list); i++) + { + if (!rb->strcasecmp(ext, ext_list[i].ext)) + { + return ext_list[i].type; + } + } + return IMAGE_UNKNOWN; +} + +/* Read directory contents for scrolling. */ static void get_pic_list(void) { int i; @@ -129,7 +178,7 @@ tree = rb->tree_get_context(); dircache = tree->dircache; - file_pt = (char **) buf; + file_pt = (char **) decoder_buf; /* Remove path and leave only the name.*/ pname = rb->strrchr(np_file,'/'); @@ -138,7 +187,7 @@ for (i = 0; i < tree->filesindir; i++) { if (!(dircache[i].attr & ATTR_DIRECTORY) - && img_ext(rb->strrchr(dircache[i].name,'.'))) + && get_image_type(dircache[i].name) != IMAGE_UNKNOWN) { file_pt[entries] = dircache[i].name; /* Set Selected File. */ @@ -148,8 +197,8 @@ } } - buf += (entries * sizeof(char**)); - buf_size -= (entries * sizeof(char**)); + decoder_buf += (entries * sizeof(char**)); + decoder_buf_size -= (entries * sizeof(char**)); } static int change_filename(int direct) @@ -209,11 +258,11 @@ #define ZOOM_IN 100 /* return codes for below function */ #define ZOOM_OUT 101 -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) +#if defined(HAVE_LCD_COLOR) static bool set_option_grayscale(void) { bool gray = settings.jpeg_colour_mode == COLOURMODE_GRAY; - rb->set_bool("Grayscale", &gray); + rb->set_bool("Grayscale (Jpeg)", &gray); settings.jpeg_colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR; return false; } @@ -226,14 +275,14 @@ [DITHER_DIFFUSION] = { "Diffusion", -1 }, }; - rb->set_option("Dithering", &settings.jpeg_dither_mode, INT, + rb->set_option("Dithering (Jpeg)", &settings.jpeg_dither_mode, INT, dithering, DITHER_NUM_MODES, NULL); return false; } -MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale", +MENUITEM_FUNCTION(grayscale_item, 0, "Greyscale (Jpeg)", set_option_grayscale, NULL, NULL, Icon_NOICON); -MENUITEM_FUNCTION(dithering_item, 0, "Dithering", +MENUITEM_FUNCTION(dithering_item, 0, "Dithering (Jpeg)", set_option_dithering, NULL, NULL, Icon_NOICON); MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON, &grayscale_item, &dithering_item); @@ -242,7 +291,7 @@ { rb->do_menu(&display_menu, NULL, NULL, false); } -#endif /* defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) */ +#endif /* defined(HAVE_LCD_COLOR) */ static int show_menu(void) /* return 1 to quit */ { @@ -266,19 +315,19 @@ #if PLUGIN_BUFFER_SIZE >= MIN_MEM MIID_SHOW_PLAYBACK_MENU, #endif -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) +#if defined(HAVE_LCD_COLOR) MIID_DISPLAY_OPTIONS, #endif MIID_QUIT, }; - MENUITEM_STRINGLIST(menu, MENU_TITLE, NULL, + MENUITEM_STRINGLIST(menu, "Image Viewer Menu", NULL, "Return", "Toggle Slideshow Mode", "Change Slideshow Time", #if PLUGIN_BUFFER_SIZE >= MIN_MEM "Show Playback Menu", #endif -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) +#if defined(HAVE_LCD_COLOR) "Display Options", #endif "Quit"); @@ -295,7 +344,7 @@ case MIID_RETURN: break; case MIID_TOGGLE_SS_MODE: - rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, + rb->set_option("Toggle Slideshow", &iv_api.slideshow_enabled, INT, slideshow , 2, NULL); break; case MIID_CHANGE_SS_MODE: @@ -306,7 +355,7 @@ #if PLUGIN_BUFFER_SIZE >= MIN_MEM case MIID_SHOW_PLAYBACK_MENU: - if (plug_buf) + if (iv_api.plug_buf) { playback_control(NULL); } @@ -316,7 +365,7 @@ } break; #endif -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) +#if defined(HAVE_LCD_COLOR) case MIID_DISPLAY_OPTIONS: display_options(); break; @@ -328,10 +377,10 @@ #ifdef DISK_SPINDOWN /* change ata spindown time based on slideshow time setting */ - immediate_ata_off = false; + iv_api.immediate_ata_off = false; rb->storage_spindown(rb->global_settings->disk_spindown); - if (slideshow_enabled) + if (iv_api.slideshow_enabled) { if(settings.ss_timeout < 10) { @@ -341,7 +390,7 @@ else if (!rb->mp3_is_playing()) { /* slideshow times > 10s and not playing: ata_off after load */ - immediate_ata_off = true; + iv_api.immediate_ata_off = true; } } #endif @@ -365,7 +414,8 @@ { MYXLCD(scroll_left)(move); /* scroll left */ info->x += move; - draw_image_rect(info, LCD_WIDTH - move, 0, move, info->height-info->y); + imgdec->draw_image_rect(info, LCD_WIDTH - move, 0, + move, info->height-info->y); MYLCD_UPDATE(); } } @@ -381,7 +431,7 @@ { MYXLCD(scroll_right)(move); /* scroll right */ info->x -= move; - draw_image_rect(info, 0, 0, move, info->height-info->y); + imgdec->draw_image_rect(info, 0, 0, move, info->height-info->y); MYLCD_UPDATE(); } } @@ -397,15 +447,16 @@ { MYXLCD(scroll_down)(move); /* scroll down */ info->y -= move; -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) - if (settings.jpeg_dither_mode == DITHER_DIFFUSION) +#if defined(HAVE_LCD_COLOR) + if (image_type == IMAGE_JPEG + && settings.jpeg_dither_mode == DITHER_DIFFUSION) { /* Draw over the band at the top of the last update caused by lack of error history on line zero. */ move = MIN(move + 1, info->y + info->height); } #endif - draw_image_rect(info, 0, 0, info->width-info->x, move); + imgdec->draw_image_rect(info, 0, 0, info->width-info->x, move); MYLCD_UPDATE(); } } @@ -421,8 +472,9 @@ { MYXLCD(scroll_up)(move); /* scroll up */ info->y += move; -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) - if (settings.jpeg_dither_mode == DITHER_DIFFUSION) +#if defined(HAVE_LCD_COLOR) + if (image_type == IMAGE_JPEG + && settings.jpeg_dither_mode == DITHER_DIFFUSION) { /* Save the line that was on the last line of the display and draw one extra line above then recover the line with @@ -435,10 +487,12 @@ } #endif - draw_image_rect(info, 0, LCD_HEIGHT - move, info->width-info->x, move); + imgdec->draw_image_rect(info, 0, LCD_HEIGHT - move, + info->width-info->x, move); -#if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) - if (settings.jpeg_dither_mode == DITHER_DIFFUSION) +#if defined(HAVE_LCD_COLOR) + if (image_type == IMAGE_JPEG + && settings.jpeg_dither_mode == DITHER_DIFFUSION) { /* Cover the first row drawn with previous image data. */ rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, @@ -458,12 +512,12 @@ while (true) { - if (slideshow_enabled) + if (iv_api.slideshow_enabled) button = rb->button_get_w_tmo(settings.ss_timeout * HZ); else button = rb->button_get(true); - running_slideshow = false; + iv_api.running_slideshow = false; switch(button) { @@ -494,17 +548,17 @@ break; case BUTTON_NONE: - if (!slideshow_enabled) + if (!iv_api.slideshow_enabled) break; - running_slideshow = true; + iv_api.running_slideshow = true; if (entries > 1) return change_filename(DIR_NEXT); break; #ifdef IMGVIEW_SLIDE_SHOW case IMGVIEW_SLIDE_SHOW: - slideshow_enabled = !slideshow_enabled; - running_slideshow = slideshow_enabled; + iv_api.slideshow_enabled = !iv_api.slideshow_enabled; + iv_api.running_slideshow = iv_api.slideshow_enabled; break; #endif @@ -552,7 +606,7 @@ #ifdef USEGSLIB grey_show(true); /* switch on greyscale overlay */ #else - draw_image_rect(info, 0, 0, + imgdec->draw_image_rect(info, 0, 0, info->width-info->x, info->height-info->y); MYLCD_UPDATE(); #endif @@ -573,10 +627,10 @@ /********************* main function *************************/ /* callback updating a progress meter while image decoding */ -void cb_progress(int current, int total) +static void cb_progress(int current, int total) { rb->yield(); /* be nice to the other threads */ - if(!running_slideshow) + if(!iv_api.running_slideshow) { rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], 0, LCD_HEIGHT-8, LCD_WIDTH, 8, @@ -600,10 +654,10 @@ { int downscale = 8; - if (img_mem(8) > bufsize) + if (imgdec->img_mem(8) > bufsize) return 0; /* error, too large, even 1:8 doesn't fit */ - while (downscale > 1 && img_mem(downscale/2) <= bufsize) + while (downscale > 1 && imgdec->img_mem(downscale/2) <= bufsize) downscale /= 2; return downscale; @@ -665,18 +719,26 @@ #endif rb->lcd_clear_display(); + status = get_image_type(filename); + if (image_type != status) /* type of image is changed, load decoder. */ + { + image_type = status; + imgdec = load_decoder(image_type); + if (imgdec == NULL) + return PLUGIN_ERROR; + } rb->memset(info, 0, sizeof(*info)); remaining = buf_size; if (rb->button_get(false) == IMGVIEW_MENU) status = PLUGIN_ABORT; else - status = load_image(filename, info, buf, &remaining); + status = imgdec->load_image(filename, info, buf, &remaining); if (status == PLUGIN_OUTOFMEM) { #if PLUGIN_BUFFER_SIZE >= MIN_MEM - if(plug_buf) + if(iv_api.plug_buf) { rb->lcd_setfont(FONT_SYSFIXED); rb->lcd_clear_display(); @@ -698,7 +760,7 @@ switch(button) { case IMGVIEW_ZOOM_IN: - plug_buf = false; + iv_api.plug_buf = false; buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); /*try again this file, now using the audio buffer */ return PLUGIN_OTHER; @@ -753,15 +815,17 @@ ds_min = min_downscale(remaining); /* check memory constraint */ if (ds_min == 0) { -#if UNSCALED_IS_AVAILABLE - /* Can not resize the image but original one is available, so use it. */ - ds_min = ds_max = 1; -#else - /* not enough memory to decode image. */ - rb->splash(HZ, "too large"); - file_pt[curfile] = NULL; - return change_filename(direction); -#endif + if (imgdec->unscaled_avail) + { + /* Can not resize the image but original one is available, so use it. */ + ds_min = ds_max = 1; + } + else + { + rb->splash(HZ, "too large"); + file_pt[curfile] = NULL; + return change_filename(direction); + } } else if (ds_max < ds_min) ds_max = ds_min; @@ -772,7 +836,7 @@ do /* loop the image prepare and decoding when zoomed */ { - status = get_image(info, ds); /* decode or fetch from cache */ + status = imgdec->get_image(info, ds); /* decode or fetch from cache */ if (status == PLUGIN_ERROR) { file_pt[curfile] = NULL; @@ -781,7 +845,7 @@ set_view(info, cx, cy); - if(!running_slideshow) + if(!iv_api.running_slideshow) { rb->snprintf(print, sizeof(print), "showing %dx%d", info->width, info->height); @@ -790,7 +854,7 @@ } MYLCD(clear_display)(); - draw_image_rect(info, 0, 0, + imgdec->draw_image_rect(info, 0, 0, info->width-info->x, info->height-info->y); MYLCD_UPDATE(); @@ -806,18 +870,10 @@ status = scroll_bmp(info); if (status == ZOOM_IN) { -#if UNSCALED_IS_AVAILABLE - if (ds > 1) -#else - if (ds > ds_min) -#endif + if (ds > ds_min || (imgdec->unscaled_avail && ds > 1)) { -#if UNSCALED_IS_AVAILABLE /* if 1/1 is always available, jump ds from ds_min to 1. */ int zoom = (ds == ds_min)? ds_min: 2; -#else - const int zoom = 2; -#endif ds /= zoom; /* reduce downscaling to zoom in */ get_view(info, &cx, &cy); cx *= zoom; /* prepare the position in the new image */ @@ -831,12 +887,8 @@ { if (ds < ds_max) { -#if UNSCALED_IS_AVAILABLE /* if ds is 1 and ds_min is > 1, jump ds to ds_min. */ int zoom = (ds < ds_min)? ds_min: 2; -#else - const int zoom = 2; -#endif ds *= zoom; /* increase downscaling to zoom out */ get_view(info, &cx, &cy); cx /= zoom; /* prepare the position in the new image */ @@ -875,34 +927,32 @@ if(!parameter) return PLUGIN_ERROR; -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - buf = rb->plugin_get_buffer((size_t *)&buf_size); -#else - buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); -#endif + decoder_buf = rb->plugin_get_buffer((size_t *)&decoder_buf_size); rb->strcpy(np_file, parameter); get_pic_list(); - if(!entries) return PLUGIN_ERROR; + if(curfile == -1 || !entries) return PLUGIN_ERROR; #if PLUGIN_BUFFER_SIZE >= MIN_MEM if(!rb->audio_status()) { - plug_buf = false; + iv_api.plug_buf = false; buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); } +#else + buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); #endif #ifdef USEGSLIB - if (!grey_init(buf, buf_size, GREY_ON_COP, + if (!grey_init(decoder_buf, decoder_buf_size, GREY_ON_COP, LCD_WIDTH, LCD_HEIGHT, &greysize)) { rb->splash(HZ, "grey buf error"); return PLUGIN_ERROR; } - buf += greysize; - buf_size -= greysize; + decoder_buf += greysize; + decoder_buf_size -= greysize; #endif /* should be ok to just load settings since the plugin itself has Index: apps/plugins/imageviewer/load_decoder.c =================================================================== --- apps/plugins/imageviewer/load_decoder.c (revision 0) +++ apps/plugins/imageviewer/load_decoder.c (revision 0) @@ -0,0 +1,148 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * user intereface of image viewers (jpeg, png, etc.) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include "imageviewer.h" +#include "load_decoder.h" + +extern unsigned char *decoder_buf, *buf; +extern ssize_t decoder_buf_size, buf_size; + +#ifndef SIMULATOR +static const char *decoders[MAX_IMAGE_TYPES] = { + "bmp", + "jpeg", +#ifdef HAVE_LCD_COLOR + "png", +#endif +}; + +struct image_decoder *load_decoder(enum image_type type) +{ + const char *name; + char filename[MAX_PATH]; + int fd; + ssize_t readsize, decoder_size; + struct imgdec_header header; + + if (type < 0 || type >= MAX_IMAGE_TYPES) + { + rb->splashf(2*HZ, "Unknown type: %d", type); + return NULL; + } + + name = decoders[type]; + rb->strcpy(filename, rb->plugin_get_current_filename()); + rb->snprintf(rb->strrchr(filename, '.'), MAX_PATH, "_%s.ovl", name); + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->splashf(2*HZ, "Can't open %s", filename); + return NULL; + } + readsize = rb->read(fd, &header, sizeof(struct imgdec_header)); + rb->close(fd); + /* Close for now. Less code than doing it in all error checks. + * Would need to seek back anyway. */ + + if (readsize != sizeof(struct imgdec_header) + || header.magic != PLUGIN_MAGIC + || header.target_id != TARGET_ID) + { + rb->splashf(2*HZ, "%s decoder: Incompatible model.", name); + return NULL; + } + if (header.api_version != IMGDEC_API_VERSION) + { + rb->splashf(2*HZ, "%s decoder: Incompatible version.", name); + return NULL; + } + + if (header.load_addr < decoder_buf + || header.end_addr > decoder_buf + decoder_buf_size) + { + rb->splashf(2*HZ, "%s decoder: Too large.", name); + return NULL; + } + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->splashf(2*HZ, "Can't open %s", filename); + return NULL; + } + readsize = rb->read(fd, header.load_addr, header.end_addr - header.load_addr); + rb->close(fd); + + if (readsize < 0) + { + rb->splashf(2*HZ, "Failed Reading %s decoder.", name); + return NULL; + } + decoder_size = header.end_addr - header.load_addr; + /* Zero out bss area */ + if (decoder_size > readsize) + rb->memset(header.load_addr + readsize, 0, decoder_size - readsize); + + *(header.api) = rb; + *(header.img_api) = iv; +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + if (iv->plug_buf) + { + /* decoder is loaded to end of plugin buffer. */ + buf = decoder_buf; + buf_size = header.load_addr - decoder_buf; + } +#endif + + rb->cpucache_invalidate(); + return header.get_decoder(); +} +#else +extern struct image_decoder bmp_decoder; +extern struct image_decoder jpeg_decoder; +#ifdef HAVE_LCD_COLOR +extern struct image_decoder png_decoder; +#endif +static struct image_decoder *decoders[MAX_IMAGE_TYPES] = { + &bmp_decoder, + &jpeg_decoder, +#ifdef HAVE_LCD_COLOR + &png_decoder, +#endif +}; + +struct image_decoder *load_decoder(enum image_type type) +{ + if (type < 0 || type >= MAX_IMAGE_TYPES) + return NULL; + +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + if (iv->plug_buf) + { + buf = decoder; + buf_size = decoder_size; + } +#endif + return decoders[type]; +} +#endif Index: apps/plugins/imageviewer/imageviewer.h =================================================================== --- apps/plugins/imageviewer/imageviewer.h (revision 24284) +++ apps/plugins/imageviewer/imageviewer.h (working copy) @@ -333,10 +333,16 @@ /* different graphics libraries */ #if LCD_DEPTH < 8 #define USEGSLIB +#ifdef IMGDEC +#define MYLCD(fn) iv->fn +#define MYLCD_UPDATE() +#define MYXLCD(fn) iv->fn +#else #include #define MYLCD(fn) grey_ub_ ## fn #define MYLCD_UPDATE() #define MYXLCD(fn) grey_ub_ ## fn +#endif /* IMGDEC */ #else #include #define MYLCD(fn) rb->lcd_ ## fn @@ -365,7 +371,6 @@ /* Settings. jpeg needs these */ struct imgview_settings { - /* include all settings for varias decoders as using same setting file. */ #ifdef HAVE_LCD_COLOR int jpeg_colour_mode; int jpeg_dither_mode; @@ -381,34 +386,75 @@ void *data; /* use freely in decoder. not touched in ui. */ }; -/* callback updating a progress meter while image decoding */ -extern void cb_progress(int current, int total); - -extern struct imgview_settings settings; -extern bool slideshow_enabled; -extern bool running_slideshow; +struct imgdec_api { + const struct imgview_settings *settings; + bool slideshow_enabled; /* run slideshow */ + bool running_slideshow; /* loading image because of slideshw */ #ifdef DISK_SPINDOWN -extern bool immediate_ata_off; + bool immediate_ata_off; /* power down disk after loading */ #endif #if PLUGIN_BUFFER_SIZE >= MIN_MEM -extern bool plug_buf; + bool plug_buf; /* are we using the plugin buffer or the audio buffer? */ #endif -/* functions needed to be implemented in each image decoders. */ -/* return true if ext is supported by the decoder. */ -extern bool img_ext(const char *ext); -/* return needed size of buffer to store downscaled image by ds */ -extern int img_mem(int ds); -/* load image from filename. set width and height of info properly. alos, set - * buf_size to remaining size of buf after load image. it is used to caluclate - * min downscale. */ -extern int load_image(char *filename, struct image_info *info, + /* callback updating a progress meter while image decoding */ + void (*cb_progress)(int current, int total); + +#ifdef USEGSLIB + void (*gray_bitmap_part)(const unsigned char *src, int src_x, int src_y, + int stride, int x, int y, int width, int height); +#endif +}; + +extern const struct imgdec_api *iv; + +/* functions need to be implemented in each image decoders. */ +struct image_decoder { + /* if unscaled image can be always displayed when there isn't enough memory + * for resized image. e.g. when using native format to store image. */ + const bool unscaled_avail; + + /* return needed size of buffer to store downscaled image by ds */ + int (*img_mem)(int ds); + /* load image from filename. set width and height of info properly. alos, set + * buf_size to remaining size of buf after load image. it is used to caluclate + * min downscale. */ + int (*load_image)(char *filename, struct image_info *info, unsigned char *buf, ssize_t *buf_size); -/* downscale loaded image by ds. note that buf to store reszied image is not - * provided. return PLUGIN_ERROR for error. ui will skip to next image. */ -extern int get_image(struct image_info *info, int ds); -/* draw part of image */ -extern void draw_image_rect(struct image_info *info, + /* downscale loaded image by ds. note that buf to store reszied image is not + * provided. return PLUGIN_ERROR for error. ui will skip to next image. */ + int (*get_image)(struct image_info *info, int ds); + /* draw part of image */ + void (*draw_image_rect)(struct image_info *info, int x, int y, int width, int height); +}; +#ifndef SIMULATOR +#define IMGDEC_API_VERSION (PLUGIN_API_VERSION << 4 | 0) + +/* image decoder header */ +struct imgdec_header { + unsigned long magic; + unsigned short target_id; + unsigned short api_version; + unsigned char *load_addr; + unsigned char *end_addr; + struct image_decoder *(*get_decoder)(void); + const struct plugin_api **api; + const struct imgdec_api **img_api; +}; + +#ifdef IMGDEC +#define IMGDEC_HEADER \ + const struct plugin_api *rb DATA_ATTR; \ + const struct imgdec_api *iv DATA_ATTR; \ + const struct imgdec_header __header \ + __attribute__ ((section (".header")))= { \ + PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, \ + plugin_start_addr, plugin_end_addr, get_decoder, &rb, &iv }; + +struct image_decoder *get_decoder(void); +#endif +#endif /* !SIMULATOR */ + #endif /* _IMGVIEW_IMGVIEW_H */ Index: apps/plugins/imageviewer/load_decoder.h =================================================================== --- apps/plugins/imageviewer/load_decoder.h (revision 0) +++ apps/plugins/imageviewer/load_decoder.h (revision 0) @@ -0,0 +1,39 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * user intereface of image viewers (jpeg, png, etc.) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _IMGVIEW_LOADER_H +#define _IMGVIEW_LOADER_H + +#include "imageviewer.h" + +enum image_type { + IMAGE_UNKNOWN = -1, + IMAGE_BMP = 0, + IMAGE_JPEG, +#ifdef HAVE_LCD_COLOR + IMAGE_PNG, +#endif + MAX_IMAGE_TYPES +}; + +struct image_decoder *load_decoder(enum image_type type); + +#endif /* _IMGVIEW_LOADER_H */ Index: apps/plugins/imageviewer/SUBDIRS =================================================================== --- apps/plugins/imageviewer/SUBDIRS (revision 24284) +++ apps/plugins/imageviewer/SUBDIRS (working copy) @@ -1,3 +1,4 @@ +bmp jpeg #ifdef HAVE_LCD_COLOR png Index: apps/plugins/imageviewer/bmp/bmp.c =================================================================== --- apps/plugins/imageviewer/bmp/bmp.c (revision 0) +++ apps/plugins/imageviewer/bmp/bmp.c (revision 0) @@ -0,0 +1,330 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * BMP image viewer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include "bmp.h" +#include "resize.h" +#include +#include + +#include "../imageviewer.h" + +#ifdef HAVE_LCD_COLOR +#define resize_bitmap smooth_resize_bitmap +#elif defined(USEGSLIB) +#define resize_bitmap grey_resize_bitmap +#else +#define resize_bitmap simple_resize_bitmap +#endif + +/**************** begin Application ********************/ + + +/************************* Types ***************************/ + +struct t_disp +{ + unsigned char* bitmap; +}; + +/************************* Globals ***************************/ + +/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ +static struct t_disp disp[9]; + +/* my memory pool (from the mp3 buffer) */ +static char print[32]; /* use a common snprintf() buffer */ + +/* the root of the images, hereafter are decompresed ones */ +static unsigned char* buf_root; +static int root_size; + +/* up to here currently used by image(s) */ +static unsigned char* buf_images; +static ssize_t buf_images_size; + +struct bitmap bmp; + +/************************* Implementation ***************************/ + +#ifdef USEGSLIB +/* copied & pasted from lib/pluginlib_bmp.c. */ +static void grey_resize_bitmap(struct bitmap *src, struct bitmap *dst) +{ + const int srcw = src->width; + const int srch = src->height; + const int dstw = dst->width; + const int dsth = dst->height; + unsigned char *srcd = src->data; + unsigned char *dstd = dst->data; + + const long xrstep = ((srcw-1) << 8) / (dstw-1); + const long yrstep = ((srch-1) << 8) / (dsth-1); + unsigned char *src_row, *dst_row; + long xr, yr = 0; + int src_x, src_y, dst_x, dst_y; + for (dst_y=0; dst_y < dsth; dst_y++) + { + src_y = (yr >> 8); + src_row = &srcd[src_y * srcw]; + dst_row = &dstd[dst_y * dstw]; + for (xr=0,dst_x=0; dst_x < dstw; dst_x++) + { + src_x = (xr >> 8); + dst_row[dst_x] = src_row[src_x]; + xr += xrstep; + } + yr += yrstep; + } +} +#endif /* USEGSLIB */ + +static void draw_image_rect(struct image_info *info, + int x, int y, int width, int height) +{ + struct t_disp* pdisp = (struct t_disp*)info->data; +#ifdef HAVE_LCD_COLOR + rb->lcd_bitmap_part( + (fb_data*)pdisp->bitmap, info->x + x, info->y + y, + STRIDE(SCREEN_MAIN, info->width, info->height), + x + MAX(0, (LCD_WIDTH-info->width)/2), + y + MAX(0, (LCD_HEIGHT-info->height)/2), + width, height); +#else + MYXLCD(gray_bitmap_part)( + pdisp->bitmap, info->x + x, info->y + y, info->width, + x + MAX(0, (LCD_WIDTH-info->width)/2), + y + MAX(0, (LCD_HEIGHT-info->height)/2), + width, height); +#endif +} + +static int img_mem(int ds) +{ +#ifndef USEGSLIB + return (bmp.width/ds) * (bmp.height/ds) * sizeof (fb_data); +#else + return (bmp.width/ds) * (bmp.height/ds); +#endif +} + +static int load_image(char *filename, struct image_info *info, + unsigned char *buf, ssize_t *buf_size) +{ + int w, h; /* used to center output */ + long time; /* measured ticks */ + int fd; + int size; + int format = FORMAT_NATIVE; +#ifdef USEGSLIB + const struct custom_format *cformat = &format_grey; +#else + const struct custom_format *cformat = &format_native; +#endif + + rb->memset(&disp, 0, sizeof(disp)); + rb->memset(&bmp, 0, sizeof(bmp)); /* clear info struct */ + + if ((intptr_t)buf & 3) + { + *buf_size -= 4-((intptr_t)buf&3); + buf += 4-((intptr_t)buf&3); + } + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->splashf(HZ, "err opening %s:%d", filename, fd); + return PLUGIN_ERROR; + } + int ds = 1; + /* check size of image needed to load image. */ + size = scaled_read_bmp_fd(fd, &bmp, 0, format | FORMAT_RETURN_SIZE, cformat); +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + if (!iv->plug_buf) +#endif + { + while (size > *buf_size && bmp.width >= 2 && bmp.height >= 2 && ds < 8) + { + /* too large for the buffer. resize on load. */ + format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT; + ds *= 2; + bmp.width /= 2; + bmp.height /= 2; + rb->lseek(fd, 0, SEEK_SET); + size = scaled_read_bmp_fd(fd, &bmp, 0, format | FORMAT_RETURN_SIZE, cformat); + } + } + if (size <= 0) + { + rb->close(fd); + rb->splashf(HZ, "read error %d", size); + return PLUGIN_ERROR; + } + + if (size > *buf_size) + { + rb->close(fd); + return PLUGIN_OUTOFMEM; + } + + if(!iv->running_slideshow) + { + rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); + rb->lcd_puts(0, 0, print); + rb->lcd_update(); + + rb->snprintf(print, sizeof(print), "loading %dx%d(1/%d)", + bmp.width, bmp.height, ds); + rb->lcd_puts(0, 1, print); + rb->lcd_update(); + } + + /* allocate bitmap buffer */ + bmp.data = buf; + + /* actual loading */ + time = *rb->current_tick; + rb->lseek(fd, 0, SEEK_SET); +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + size = scaled_read_bmp_fd(fd, &bmp, *buf_size, format, cformat); + rb->cpu_boost(false); +#else + size = scaled_read_bmp_fd(fd, &bmp, *buf_size, format, cformat); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + rb->close(fd); + time = *rb->current_tick - time; + + if (size <= 0) + { + rb->splashf(HZ, "load error %d", size); + return PLUGIN_ERROR; + } + + if(!iv->running_slideshow) + { + rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); + rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ + rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); + rb->lcd_update(); + } +#ifdef DISK_SPINDOWN + else if(iv->immediate_ata_off) + { + /* running slideshow and time is long enough: power down disk */ + rb->storage_sleep(); + } +#endif + + /* we can start the resized images behind it */ + buf_images = buf_root = buf + size; + buf_images_size = root_size = *buf_size - size; + + if(!iv->running_slideshow) + { + rb->snprintf(print, sizeof(print), "image %dx%d", bmp.width, bmp.height); + rb->lcd_puts(0, 2, print); + rb->lcd_update(); + } + + info->x_size = bmp.width; + info->y_size = bmp.height; + *buf_size = buf_images_size; + return PLUGIN_OK; +} + +static int get_image(struct image_info *info, int ds) +{ + struct t_disp* p_disp = &disp[ds]; /* short cut */ + + info->width = bmp.width/ds; + info->height = bmp.height/ds; + info->data = p_disp; + + if (p_disp->bitmap != NULL) + { + /* we still have it */ + return PLUGIN_OK; + } + + /* assign image buffer */ + if (ds > 1) + { + int size; /* resized image size */ + struct bitmap bmp_dst; + + size = img_mem(ds); + if (buf_images_size <= size) + { /* have to discard the current */ + int i; + for (i=1; i<=8; i++) + disp[i].bitmap = NULL; /* invalidate all bitmaps */ + buf_images = buf_root; /* start again from the beginning of the buffer */ + buf_images_size = root_size; + } + + p_disp->bitmap = buf_images; + buf_images += size; + buf_images_size -= size; + + if(!iv->running_slideshow) + { + rb->snprintf(print, sizeof(print), "resizing %d*%d", + info->width, info->height); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + + bmp_dst.width = info->width; + bmp_dst.height = info->height; + bmp_dst.data = p_disp->bitmap; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + resize_bitmap(&bmp, &bmp_dst); + rb->cpu_boost(false); +#else + resize_bitmap(&bmp, &bmp_dst); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + } + else + { + p_disp->bitmap = bmp.data; + } + + return PLUGIN_OK; +} + +struct image_decoder bmp_decoder = { + true, + img_mem, + load_image, + get_image, + draw_image_rect, +}; + +#ifndef SIMULATOR +IMGDEC_HEADER + +struct image_decoder *get_decoder(void) +{ + return &bmp_decoder; +} +#endif Index: apps/plugins/imageviewer/bmp/SOURCES =================================================================== --- apps/plugins/imageviewer/bmp/SOURCES (revision 0) +++ apps/plugins/imageviewer/bmp/SOURCES (revision 0) @@ -0,0 +1 @@ +bmp.c Index: apps/plugins/imageviewer/bmp/bmp.make =================================================================== --- apps/plugins/imageviewer/bmp/bmp.make (revision 0) +++ apps/plugins/imageviewer/bmp/bmp.make (revision 0) @@ -0,0 +1,44 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +BMPSRCDIR := $(IMGVSRCDIR)/bmp +BMPBUILDDIR := $(IMGVBUILDDIR)/bmp + +BMP_SRC := $(call preprocess, $(BMPSRCDIR)/SOURCES) +BMP_OBJ := $(call c2obj, $(BMP_SRC)) + +ifndef SIMVER + # add source files to OTHER_SRC to get automatic dependencies + OTHER_SRC += $(BMP_SRC) + ROCKS += $(BMPBUILDDIR)/imageviewer_bmp.ovl +else + # compile all decoder in one binary for simulator. + IMGV_SRC += $(BMP_SRC) +endif + +BMP_OUTLDS = $(BMPBUILDDIR)/bmp.link +BMP_OVLFLAGS = -T$(BMP_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map + +$(BMPBUILDDIR)/bmp.refmap: $(BMP_OBJ) + +$(BMP_OUTLDS): $(PLUGIN_LDS) $(BMPBUILDDIR)/bmp.refmap + $(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DIMGVDECODER_OFFSET=$(shell \ + $(TOOLSDIR)/ovl_offset.pl $(BMPBUILDDIR)/bmp.refmap)) + +$(BMPBUILDDIR)/imageviewer_bmp.ovl: $(BMP_OBJ) $(BMP_OUTLDS) + $(SILENT)$(CC) $(IMGDECFLAGS) -o $(basename $@).elf \ + $(filter %.o, $^) \ + $(filter %.a, $+) \ + -lgcc $(BMP_OVLFLAGS) + $(call PRINTS,LD $(@F))$(OC) -O binary $(basename $@).elf $@ + +# special pattern rule for compiling image decoder with extra flags +$(BMPBUILDDIR)/%.o: $(BMPSRCDIR)/%.c $(BMPSRCDIR)/bmp.make + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(IMGDECFLAGS) -c $< -o $@ Index: apps/plugin.c =================================================================== --- apps/plugin.c (revision 24284) +++ apps/plugin.c (working copy) @@ -350,8 +350,8 @@ #endif #if NUM_CORES > 1 cpucache_flush, +#endif cpucache_invalidate, -#endif timer_register, timer_unregister, timer_set_period, Index: apps/plugin.h =================================================================== --- apps/plugin.h (revision 24284) +++ apps/plugin.h (working copy) @@ -135,12 +135,12 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 179 +#define PLUGIN_API_VERSION 180 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 176 +#define PLUGIN_MIN_API_VERSION 180 /* plugin return codes */ enum plugin_status { @@ -471,8 +471,8 @@ #endif #if NUM_CORES > 1 void (*cpucache_flush)(void); +#endif void (*cpucache_invalidate)(void); -#endif bool (*timer_register)(int reg_prio, void (*unregister_callback)(void), long cycles, void (*timer_callback)(void) IF_COP(, int core)); Index: apps/filetypes.c =================================================================== --- apps/filetypes.c (revision 24284) +++ apps/filetypes.c (working copy) @@ -95,7 +95,8 @@ { "rwps",FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS }, #endif #if LCD_DEPTH > 1 - { "bmp", FILE_ATTR_BMP, Icon_Wps, VOICE_EXT_WPS }, +/* remove this so taht bmp files will be opend with bmpviewer directly. */ +/* { "bmp", FILE_ATTR_BMP, Icon_Wps, VOICE_EXT_WPS }, */ #endif #if CONFIG_TUNER { "fmr", FILE_ATTR_FMR, Icon_Preset, LANG_FMR },