diff options
Diffstat (limited to 'apps/plugins/imageviewer')
| -rw-r--r-- | apps/plugins/imageviewer/SUBDIRS | 3 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/image_decoder.c | 6 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/image_decoder.h | 3 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/ppm/SOURCES | 2 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/ppm/ppm.c | 233 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/ppm/ppm.make | 29 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/ppm/ppm_decoder.c | 250 | ||||
| -rw-r--r-- | apps/plugins/imageviewer/ppm/ppm_decoder.h | 44 |
8 files changed, 570 insertions, 0 deletions
diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS index 9d8da7d..0f8d953 100644 --- a/apps/plugins/imageviewer/SUBDIRS +++ b/apps/plugins/imageviewer/SUBDIRS @@ -1,3 +1,6 @@ bmp jpeg png +#ifdef HAVE_LCD_COLOR +ppm +#endif diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c index b4fa27e..553632e 100644 --- a/apps/plugins/imageviewer/image_decoder.c +++ b/apps/plugins/imageviewer/image_decoder.c @@ -27,6 +27,9 @@ static const char *decoder_names[MAX_IMAGE_TYPES] = { "bmp", "jpeg", "png", +#ifdef HAVE_LCD_COLOR + "ppm" +#endif }; /* check file type by extention */ @@ -41,6 +44,9 @@ enum image_type get_image_type(const char *name) { ".jpe", IMAGE_JPEG }, { ".jpeg", IMAGE_JPEG }, { ".png", IMAGE_PNG }, +#ifdef HAVE_LCD_COLOR + { ".ppm", IMAGE_PPM }, +#endif }; const char *ext = rb->strrchr(name, '.'); diff --git a/apps/plugins/imageviewer/image_decoder.h b/apps/plugins/imageviewer/image_decoder.h index 93608b1..1ccf9ca 100644 --- a/apps/plugins/imageviewer/image_decoder.h +++ b/apps/plugins/imageviewer/image_decoder.h @@ -29,6 +29,9 @@ enum image_type { IMAGE_BMP = 0, IMAGE_JPEG, IMAGE_PNG, +#ifdef HAVE_LCD_COLOR + IMAGE_PPM, +#endif MAX_IMAGE_TYPES }; diff --git a/apps/plugins/imageviewer/ppm/SOURCES b/apps/plugins/imageviewer/ppm/SOURCES new file mode 100644 index 0000000..492cca3 --- /dev/null +++ b/apps/plugins/imageviewer/ppm/SOURCES @@ -0,0 +1,2 @@ +ppm.c +ppm_decoder.c diff --git a/apps/plugins/imageviewer/ppm/ppm.c b/apps/plugins/imageviewer/ppm/ppm.c new file mode 100644 index 0000000..100a00a --- /dev/null +++ b/apps/plugins/imageviewer/ppm/ppm.c @@ -0,0 +1,233 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Marcin Bukat + * + * 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 "lcd.h" +#include <lib/pluginlib_bmp.h> +#include "../imageviewer.h" +#include "ppm_decoder.h" +#include "bmp.h" + +static char print[32]; /* use a common snprintf() buffer */ + +/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ +static unsigned char *disp[9]; +static unsigned char *disp_buf; +static struct ppm_info ppm; + +#if defined(HAVE_LCD_COLOR) +#define resize_bitmap smooth_resize_bitmap +#else +#define resize_bitmap grey_resize_bitmap +#endif + +#if defined(USEGSLIB) && (CONFIG_PLATFORM & PLATFORM_HOSTED) +/* hack: fix error "undefined reference to `_grey_info'". */ +GREY_INFO_STRUCT +#endif /* USEGSLIB */ + +static void draw_image_rect(struct image_info *info, + int x, int y, int width, int height) +{ + unsigned char **pdisp = (unsigned char **)info->data; + +#ifdef HAVE_LCD_COLOR + rb->lcd_bitmap_part((fb_data *)*pdisp, 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 + mylcd_ub_gray_bitmap_part(*pdisp, + 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) +{ + +#ifdef USEGSLIB + return (ppm.x/ds) * (ppm.y/ds); +#else + return (ppm.x/ds) * (ppm.y/ds) * FB_DATA_SZ; +#endif +} + +static int load_image(char *filename, struct image_info *info, + unsigned char *buf, ssize_t *buf_size) +{ + int fd; + int rc = PLUGIN_OK; + long time = 0; /* measured ticks */ + int w, h; /* used to center output */ + + unsigned char *memory, *memory_max; + size_t memory_size, file_size; + + /* cleanup */ + memset(&disp, 0, sizeof(disp)); + + /* align buffer */ + memory = (unsigned char *)((intptr_t)(buf + 3) & ~3); + memory_max = (unsigned char *)((intptr_t)(memory + *buf_size) & ~3); + memory_size = memory_max - memory; + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->splashf(HZ, "err opening %s: %d", filename, fd); + return PLUGIN_ERROR; + } + + file_size = rb->filesize(fd); + DEBUGF("reading file '%s'\n", filename); + + if (!iv->running_slideshow) + { + rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1); + rb->lcd_update(); + } + + if (!iv->running_slideshow) + { + rb->lcd_putsf(0, 1, "loading %zu bytes", file_size); + rb->lcd_update(); + } + + /* init decoder struct */ + ppm.buf = memory; + ppm.buf_size = memory_size; + + /* the actual decoding */ + time = *rb->current_tick; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + rc = read_ppm(fd, &ppm); + rb->cpu_boost(false); +#else + rc = read_ppm(fd, &ppm); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + time = *rb->current_tick - time; + + /* close file descriptor */ + rb->close(fd); + + /* check return value from decoder */ + if ( rc == PLUGIN_ERROR ) + { + rb->splashf(HZ, "ppm decoder error"); + 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(); + } + + info->x_size = ppm.x; + info->y_size = ppm.y; + + ppm.native_img_size = (ppm.native_img_size + 3) & ~3; + disp_buf = buf + ppm.native_img_size; + *buf_size = memory_max - disp_buf; + + return PLUGIN_OK; +} + +static int get_image(struct image_info *info, int ds) +{ + unsigned char **p_disp = &disp[ds]; /* short cut */ + struct ppm_info *p_ppm = &ppm; + + info->width = ppm.x / ds; + info->height = ppm.y / ds; + info->data = p_disp; + + if (*p_disp != NULL) + { + /* we still have it */ + return PLUGIN_OK; + } + + /* assign image buffer */ + if (ds > 1) + { + if (!iv->running_slideshow) + { + rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height); + rb->lcd_update(); + } + + struct bitmap bmp_src, bmp_dst; + int size = img_mem(ds); + + if (disp_buf + size >= p_ppm->buf + p_ppm->buf_size) + { + /* have to discard the current */ + int i; + for (i=1; i<=8; i++) + disp[i] = NULL; /* invalidate all bitmaps */ + + /* start again from the beginning of the buffer */ + disp_buf = p_ppm->buf + p_ppm->native_img_size; + } + + *p_disp = disp_buf; + disp_buf += size; + + bmp_src.width = ppm.x; + bmp_src.height = ppm.y; + bmp_src.data = ppm.buf; + + bmp_dst.width = info->width; + bmp_dst.height = info->height; + bmp_dst.data = *p_disp; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + resize_bitmap(&bmp_src, &bmp_dst); + rb->cpu_boost(false); +#else + resize_bitmap(&bmp_src, &bmp_dst); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + } + else + { + *p_disp = p_ppm->buf; + } + + return PLUGIN_OK; +} + +const struct image_decoder image_decoder = { + true, + img_mem, + load_image, + get_image, + draw_image_rect, +}; + +IMGDEC_HEADER diff --git a/apps/plugins/imageviewer/ppm/ppm.make b/apps/plugins/imageviewer/ppm/ppm.make new file mode 100644 index 0000000..e77f282 --- /dev/null +++ b/apps/plugins/imageviewer/ppm/ppm.make @@ -0,0 +1,29 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +PPMSRCDIR := $(IMGVSRCDIR)/ppm +PPMBUILDDIR := $(IMGVBUILDDIR)/ppm + +PPM_SRC := $(call preprocess, $(PPMSRCDIR)/SOURCES) +PPM_OBJ := $(call c2obj, $(PPM_SRC)) + +OTHER_SRC += $(PPM_SRC) + +ROCKS += $(PPMBUILDDIR)/ppm.ovl + +$(PPMBUILDDIR)/ppm.refmap: $(PPM_OBJ) +$(PPMBUILDDIR)/ppm.link: $(PPM_OBJ) $(PPMBUILDDIR)/ppm.refmap +$(PPMBUILDDIR)/ppm.ovl: $(PPM_OBJ) + +PPMFLAGS = $(IMGDECFLAGS) -Os + +# Compile PPM plugin with extra flags (adapted from ZXBox) +$(PPMBUILDDIR)/%.o: $(PPMSRCDIR)/%.c $(PPMSRCDIR)/ppm.make + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PPMFLAGS) -c $< -o $@ diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.c b/apps/plugins/imageviewer/ppm/ppm_decoder.c new file mode 100644 index 0000000..44c4f92 --- /dev/null +++ b/apps/plugins/imageviewer/ppm/ppm_decoder.c @@ -0,0 +1,250 @@ +/***************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / + * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 Alexander Papst + * + * 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 "lib/pluginlib_bmp.h" +#include "ppm_decoder.h" + +static int ppm_read_magic_number(int fd) +{ + char i1, i2; + if(!rb->read(fd, &i1, 1) || !rb->read(fd, &i2, 1)) + { + ppm_error( "Error reading magic number from ppm image stream. "\ + "Most often, this means your input file is empty." ); + return PLUGIN_ERROR; + } + return i1 * 256 + i2; +} + +static char ppm_getc(int fd) +{ + char ch; + + if (!rb->read(fd, &ch, 1)) { + ppm_error("EOF. Read error reading a byte"); + return PLUGIN_ERROR; + } + + if (ch == '#') { + do { + if (!rb->read(fd, &ch, 1)) { + ppm_error("EOF. Read error reading a byte"); + return PLUGIN_ERROR; + } + } while (ch != '\n' && ch != '\r'); + } + return ch; +} + +static int ppm_getuint(int fd) +{ + char ch; + int i; + int digitVal; + + do { + ch = ppm_getc(fd); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') { + ppm_error("Junk (%c) in file where an integer should be.", ch); + return PLUGIN_ERROR; + } + + i = 0; + + do { + digitVal = ch - '0'; + + if (i > INT_MAX/10 - digitVal) { + ppm_error("ASCII decimal integer in file is "\ + "too large to be processed."); + return PLUGIN_ERROR; + } + + i = i * 10 + digitVal; + ch = ppm_getc(fd); + + } while (ch >= '0' && ch <= '9'); + + return i; +} + +static int ppm_getrawbyte(int fd) +{ + unsigned char by; + + if (!rb->read(fd, &by, 1)) { + ppm_error("EOF. Read error while reading a one-byte sample."); + return PLUGIN_ERROR; + } + + return (int)by; +} + +static int ppm_getrawsample(int fd, int const maxval) +{ + if (maxval < 256) { + /* The sample is just one byte. Read it. */ + return(ppm_getrawbyte(fd)); + } else { + /* The sample is two bytes. Read both. */ + unsigned char byte_pair[2]; + + if (!rb->read(fd, byte_pair, 2)) { + ppm_error("EOF. Read error while reading a long sample."); + return PLUGIN_ERROR; + } + return((byte_pair[0]<<8) | byte_pair[1]); + } +} + +/* Read from the file header dimensions as well as max + * int value used + */ +static int read_ppm_init_rest(int fd, struct ppm_info *ppm) +{ + /* Read size. */ + ppm->x = ppm_getuint(fd); + ppm->y = ppm_getuint(fd); + +#ifdef HAVE_LCD_COLOR + ppm->native_img_size = ppm->x * ppm->y * FB_DATA_SZ; +#endif + + if (ppm->native_img_size > ppm->buf_size) { + ppm_error("Imagesize (%ld pixels) is too large. "\ + "The maximum allowed is %ld.", + (long)ppm->native_img_size, + (long)ppm->buf_size); + return PLUGIN_ERROR; + } + + /* Read maxval. */ + ppm->maxval = ppm_getuint(fd); + + if (ppm->maxval > PPM_OVERALLMAXVAL) { + ppm_error("maxval of input image (%u) is too large. "\ + "The maximum allowed by the PPM is %u.", + ppm->maxval, PPM_OVERALLMAXVAL); + return PLUGIN_ERROR; + } + if (ppm->maxval == 0) { + ppm_error("maxval of input image is zero."); + return PLUGIN_ERROR; + } + return 1; +} + +static void read_ppm_init(int fd, struct ppm_info *ppm) +{ + /* Check magic number. */ + ppm->format = ppm_read_magic_number( fd ); + + if (ppm->format == PLUGIN_ERROR) return; + switch (ppm->format) { + case PPM_FORMAT: + case RPPM_FORMAT: + if(read_ppm_init_rest(fd, ppm) == PLUGIN_ERROR) { + ppm->format = PLUGIN_ERROR; + } + break; + + default: + ppm_error( "Bad magic number - not a ppm or rppm file." ); + ppm->format = PLUGIN_ERROR; + } +} + +#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE +#define BUFADDR(x, y, width, height) ( ppm->buf + (height*(x) + (y))*FB_DATA_SZ) +#else +#define BUFADDR(x, y, width, height) ( ppm->buf + (width*(y) + (x))*FB_DATA_SZ) +#endif + +static int read_ppm_row(int fd, struct ppm_info *ppm, int row) +{ + + int col; + int r, g, b; + switch (ppm->format) { + case PPM_FORMAT: + for (col = 0; col < ppm->x; ++col) { + r = ppm_getuint(fd); + g = ppm_getuint(fd); + b = ppm_getuint(fd); + + if (r == PLUGIN_ERROR || g == PLUGIN_ERROR || + b == PLUGIN_ERROR) + { + return PLUGIN_ERROR; + } + *(fb_data *)BUFADDR(col, row, ppm->x, ppm->y) = LCD_RGBPACK( + (255 * r)/ppm->maxval, + (255 * g)/ppm->maxval, + (255 * b)/ppm->maxval); + } + break; + + case RPPM_FORMAT: + for (col = 0; col < ppm->x; ++col) { + r = ppm_getrawsample(fd, ppm->maxval); + g = ppm_getrawsample(fd, ppm->maxval); + b = ppm_getrawsample(fd, ppm->maxval); + + if (r == PLUGIN_ERROR || g == PLUGIN_ERROR || + b == PLUGIN_ERROR) + { + return PLUGIN_ERROR; + } + *(fb_data *)BUFADDR(col, row, ppm->x, ppm->y) = LCD_RGBPACK( + (255 * r)/ppm->maxval, + (255 * g)/ppm->maxval, + (255 * b)/ppm->maxval); + } + break; + + default: + ppm_error("What?!"); + return PLUGIN_ERROR; + } + return 1; +} + +/* public */ +int read_ppm(int fd, struct ppm_info *ppm) +{ + int row; + + read_ppm_init(fd, ppm); + + if(ppm->format == PLUGIN_ERROR) { + return PLUGIN_ERROR; + } + + for (row = 0; row < ppm->y; ++row) { + if( read_ppm_row(fd, ppm, row) == PLUGIN_ERROR) { + return PLUGIN_ERROR; + } + } + return 1; +} diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.h b/apps/plugins/imageviewer/ppm/ppm_decoder.h new file mode 100644 index 0000000..edd1bcb --- /dev/null +++ b/apps/plugins/imageviewer/ppm/ppm_decoder.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Marcin Bukat + * + * 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. + * + ****************************************************************************/ + +/* Magic constants. */ +#define PPM_MAGIC1 'P' +#define PPM_MAGIC2 '3' +#define RPPM_MAGIC2 '6' +#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2) +#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2) + +#define PPM_OVERALLMAXVAL 65535 + +#define ppm_error(...) rb->splashf(HZ*2, __VA_ARGS__ ) + +struct ppm_info { + int x; + int y; + int maxval; + int format; + unsigned char *buf; + size_t buf_size; + unsigned int native_img_size; +}; + +/* public prototype */ +int read_ppm(int fd, struct ppm_info *ppm); |