diff --git a/apps/plugins/bench_mem_jpeg.c b/apps/plugins/bench_mem_jpeg.c new file mode 100644 index 0000000000..958e5208e8 --- /dev/null +++ b/apps/plugins/bench_mem_jpeg.c @@ -0,0 +1,150 @@ +/***************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / + * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Andrew Mahone + * + * In-memory JPEG decode benchmark. + * + * 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/jpeg_mem.h" +PLUGIN_HEADER + +/* a null output plugin to save memory and better isolate decode cost */ +static unsigned int get_size_null(struct bitmap *bm) +{ + (void) bm; + return 1; +} + +static void output_row_null(uint32_t row, void * row_in, + struct scaler_context *ctx) +{ + (void) row; + (void) row_in; + (void) ctx; + return; +} + +const struct custom_format format_null = { +#ifdef HAVE_LCD_COLOR + .output_row = { + output_row_null, + output_row_null + }, +#else + .output_row = output_row_null, +#endif + .get_size = get_size_null +}; + +static char output_buf[256]; +static int output_y = 0; +static int font_h; + +#define lcd_printf(...) \ +do { \ + rb->snprintf(output_buf, sizeof(output_buf), __VA_ARGS__); \ + rb->lcd_putsxy(0, output_y, output_buf); \ + rb->lcd_update_rect(0, output_y, LCD_WIDTH, font_h); \ + output_y += font_h; \ +} while (0) + +/* this is the plugin entry point */ +enum plugin_status plugin_start(const void* parameter) +{ + size_t plugin_buf_len; + unsigned char * plugin_buf = + (unsigned char *)rb->plugin_get_buffer(&plugin_buf_len); + static char filename[MAX_PATH]; + struct bitmap bm = { + .width = LCD_WIDTH, + .height = LCD_HEIGHT, + }; + int ret; + + if(!parameter) return PLUGIN_ERROR; + + rb->strcpy(filename, parameter); + rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT); + rb->lcd_set_drawmode(DRMODE_SOLID); + rb->lcd_getstringsize("A", NULL, &font_h); + int fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + lcd_printf("file open failed: %d", fd); + goto wait; + } + unsigned long filesize = rb->filesize(fd); + if (filesize > plugin_buf_len) + { + lcd_printf("file too large"); + goto wait; + } + plugin_buf_len -= filesize; + unsigned char *jpeg_buf = plugin_buf; + plugin_buf += filesize; + rb->read(fd, jpeg_buf, filesize); + rb->close(fd); + bm.data = plugin_buf; + struct dim jpeg_size; + get_jpeg_dim_mem(jpeg_buf, filesize, &jpeg_size); + lcd_printf("jpeg file size: %dx%d",jpeg_size.width, jpeg_size.height); + bm.width = jpeg_size.width; + bm.height = jpeg_size.height; + char *size_str[] = { "1/1", "1/2", "1/4", "1/8" }; + int i; + for (i = 0; i < 4; i++) + { + lcd_printf("timing %s decode", size_str[i]); + ret = decode_jpeg_mem(jpeg_buf, filesize, &bm, plugin_buf_len, + FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_KEEP_ASPECT, + &format_null); + if (ret == 1) + { + long t1, t2; + int count = 0; + t2 = *(rb->current_tick); + while (t2 != (t1 = *(rb->current_tick))); + do { + decode_jpeg_mem(jpeg_buf, filesize, &bm, plugin_buf_len, + FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_KEEP_ASPECT, + &format_null); + count++; + t2 = *(rb->current_tick); + } while (t2 - t1 < HZ || count < 10); + t2 -= t1; + t2 *= 10; + t2 += count >> 1; + t2 /= count; + t1 = t2 / 1000; + t2 -= t1 * 1000; + lcd_printf("%01d.%03d secs/decode", (int)t1, (int)t2); + bm.width >>= 1; + bm.height >>= 1; + if (!(bm.width && bm.height)) + break; + } else + lcd_printf("insufficient memory"); + } + +wait: + while (rb->get_action(CONTEXT_STD,1) != ACTION_STD_OK) rb->yield(); + return PLUGIN_OK; +} diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES index 889fb7922f..21a35d478a 100644 --- a/apps/plugins/lib/SOURCES +++ b/apps/plugins/lib/SOURCES @@ -27,9 +27,8 @@ playergfx.c profile_plugin.c #endif #ifdef HAVE_LCD_BITMAP -#if !defined(HAVE_BMP_SCALING) || !defined(HAVE_JPEG) +pluginlib_jpeg_mem.c pluginlib_resize.c -#endif #ifndef HAVE_JPEG pluginlib_jpeg_load.c #endif diff --git a/apps/plugins/lib/jpeg_mem.h b/apps/plugins/lib/jpeg_mem.h new file mode 100644 index 0000000000..8636f702cf --- /dev/null +++ b/apps/plugins/lib/jpeg_mem.h @@ -0,0 +1,41 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2009 by Andrew Mahone +* +* Header for the in-memory JPEG decoder. +* +* 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 "resize.h" +#include "bmp.h" +#include "jpeg_common.h" + +#ifndef _JPEG_MEM_H +#define _JPEG_MEM_H + +int get_jpeg_dim_mem(unsigned char *data, unsigned long len, + struct dim *size); + +int decode_jpeg_mem(unsigned char *data, unsigned long len, + struct bitmap *bm, + int maxsize, + int format, + const struct custom_format *cformat); + +#endif /* _JPEG_MEM_H */ diff --git a/apps/plugins/lib/pluginlib_jpeg_mem.c b/apps/plugins/lib/pluginlib_jpeg_mem.c new file mode 100644 index 0000000000..4a6c1bb2ac --- /dev/null +++ b/apps/plugins/lib/pluginlib_jpeg_mem.c @@ -0,0 +1,29 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2009 by Andrew Mahone +* +* This is a wrapper for the core jpeg_load.c, to provide the from-memory +* version of the decoder. +* +* 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 +#include "wrappers.h" +#define JPEG_FROM_MEM + +#include "../../recorder/jpeg_load.c" diff --git a/apps/plugins/test_mem_jpeg.c b/apps/plugins/test_mem_jpeg.c new file mode 100644 index 0000000000..50969c3503 --- /dev/null +++ b/apps/plugins/test_mem_jpeg.c @@ -0,0 +1,103 @@ +/***************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / + * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Andrew Mahone + * + * In-memory JPEG decode test. + * + * 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/grey.h" +#include "lib/jpeg_mem.h" +PLUGIN_HEADER + +/* different graphics libraries */ +#if LCD_DEPTH < 8 +#define USEGSLIB +GREY_INFO_STRUCT +#define MYLCD(fn) grey_ub_ ## fn +#define MYLCD_UPDATE() +#define MYXLCD(fn) grey_ub_ ## fn +#define CFORMAT &format_grey +#else +#define MYLCD(fn) rb->lcd_ ## fn +#define MYLCD_UPDATE() rb->lcd_update(); +#define MYXLCD(fn) xlcd_ ## fn +#define CFORMAT &format_native +#endif + +/* this is the plugin entry point */ +enum plugin_status plugin_start(const void* parameter) +{ + size_t plugin_buf_len; + unsigned char * plugin_buf = + (unsigned char *)rb->plugin_get_buffer(&plugin_buf_len); + static char filename[MAX_PATH]; + struct bitmap bm = { + .width = LCD_WIDTH, + .height = LCD_HEIGHT, + }; + int ret; + + if(!parameter) return PLUGIN_ERROR; + + rb->strcpy(filename, parameter); + +#ifdef USEGSLIB + long greysize; + if (!grey_init(plugin_buf, plugin_buf_len, GREY_ON_COP, + LCD_WIDTH, LCD_HEIGHT, &greysize)) + { + rb->splash(HZ, "grey buf error"); + return PLUGIN_ERROR; + } + plugin_buf += greysize; + plugin_buf_len -= greysize; +#endif + int fd = rb->open(filename, O_RDONLY); + if (fd < 0) + return PLUGIN_ERROR; + unsigned long filesize = rb->filesize(fd); + if (filesize > plugin_buf_len) + return PLUGIN_ERROR; + plugin_buf_len -= filesize; + unsigned char *jpeg_buf = plugin_buf; + plugin_buf += filesize; + rb->read(fd, jpeg_buf, filesize); + rb->close(fd); + bm.data = plugin_buf; + ret = decode_jpeg_mem(jpeg_buf, filesize, &bm, plugin_buf_len, + FORMAT_NATIVE|FORMAT_RESIZE|FORMAT_KEEP_ASPECT, + CFORMAT); + if (ret < 1) + return PLUGIN_ERROR; +#ifdef USEGSLIB + grey_show(true); + grey_ub_gray_bitmap((fb_data *)bm.data, (LCD_WIDTH - bm.width) >> 1, + (LCD_HEIGHT - bm.height) >> 1, bm.width, bm.height); +#else + rb->lcd_bitmap((fb_data *)bm.data, (LCD_WIDTH - bm.width) >> 1, + (LCD_HEIGHT - bm.height) >> 1, bm.width, bm.height); +#endif + MYLCD_UPDATE(); + while (rb->get_action(CONTEXT_STD,1) != ACTION_STD_OK) rb->yield(); +#ifdef USEGSLIB + grey_release(); +#endif + return PLUGIN_OK; +} diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index 09d0455c22..939a0a5534 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -30,6 +30,12 @@ bmp,viewers/test_greylib_bitmap_scale,- jpeg,viewers/test_core_jpeg,- jpe,viewers/test_core_jpeg,- jpg,viewers/test_core_jpeg,- +jpeg,viewers/test_mem_jpeg,- +jpe,viewers/test_mem_jpeg,- +jpg,viewers/test_mem_jpeg,- +jpeg,viewers/bench_mem_jpeg,- +jpe,viewers/bench_mem_jpeg,- +jpg,viewers/bench_mem_jpeg,- bmp,apps/rockpaint,11 bmp,games/sliding_puzzle,11 mpg,viewers/mpegplayer,4 diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c index 1d8c9edba3..5b16e6f960 100644 --- a/apps/recorder/jpeg_load.c +++ b/apps/recorder/jpeg_load.c @@ -36,6 +36,11 @@ #define MEMCPY(d,s,c) memcpy(d,s,c) #define INLINE static inline #define ENDIAN_SWAP16(n) n /* only for poor little endian machines */ +#ifdef ROCKBOX_DEBUG_JPEG +#define JDEBUGF DEBUGF +#else +#define JDEBUGF(...) +#endif /**************** begin JPEG code ********************/ @@ -51,9 +56,14 @@ typedef uint8_t jpeg_pix_t; */ struct jpeg { +#ifdef JPEG_FROM_MEM + unsigned char *data; + unsigned long len; +#else int fd; int buf_left; unsigned char *buf_index; +#endif unsigned long int bitbuf; int bitbuf_bits; int marker_ind; @@ -103,6 +113,10 @@ struct jpeg struct img_part part; }; +#ifdef JPEG_FROM_MEM +static struct jpeg jpeg; +#endif + INLINE unsigned range_limit(int value) { #if CONFIG_CPU == SH7034 @@ -811,6 +825,37 @@ struct idct_entry idct_tbl[] = { /* JPEG decoder implementation */ +#ifdef JPEG_FROM_MEM +INLINE unsigned char *getc(struct jpeg* p_jpeg) +{ + if (p_jpeg->len) + { + p_jpeg->len--; + return p_jpeg->data++; + } else + return NULL; +} + +INLINE bool skip_bytes(struct jpeg* p_jpeg, int count) +{ + if (p_jpeg->len >= (unsigned)count) + { + p_jpeg->len -= count; + p_jpeg->data += count; + return true; + } else { + p_jpeg->data += p_jpeg->len; + p_jpeg->len = 0; + return false; + } +} + +INLINE void putc(struct jpeg* p_jpeg) +{ + p_jpeg->len++; + p_jpeg->data--; +} +#else INLINE void fill_buf(struct jpeg* p_jpeg) { p_jpeg->buf_left = read(p_jpeg->fd, p_jpeg->buf, JPEG_READ_BUF_SIZE); @@ -842,6 +887,13 @@ static bool skip_bytes(struct jpeg* p_jpeg, int count) return p_jpeg->buf_left >= 0 || skip_bytes_seek(p_jpeg); } +static void putc(struct jpeg* p_jpeg) +{ + p_jpeg->buf_left++; + p_jpeg->buf_index--; +} +#endif + #define e_skip_bytes(jpeg, count) \ do {\ if (!skip_bytes((jpeg),(count))) \ @@ -863,12 +915,6 @@ do {\ c; \ }) -static void putc(struct jpeg* p_jpeg) -{ - p_jpeg->buf_left++; - p_jpeg->buf_index--; -} - /* Preprocess the JPEG JFIF file */ static int process_markers(struct jpeg* p_jpeg) { @@ -881,11 +927,13 @@ static int process_markers(struct jpeg* p_jpeg) { if (c != 0xFF) /* no marker? */ { + JDEBUGF("Non-marker data\n"); putc(p_jpeg); break; /* exit marker processing */ } c = e_getc(p_jpeg, -1); + JDEBUGF("marker value %X\n",c); switch (c) { case 0xFF: /* Fill byte */ @@ -896,9 +944,11 @@ static int process_markers(struct jpeg* p_jpeg) case 0xC0: /* SOF Huff - Baseline DCT */ { + JDEBUGF("SOF marker "); ret |= SOF0; marker_size = e_getc(p_jpeg, -1) << 8; /* Highbyte */ marker_size |= e_getc(p_jpeg, -1); /* Lowbyte */ + JDEBUGF("len: %d\n", marker_size); n = e_getc(p_jpeg, -1); /* sample precision (= 8 or 12) */ if (n != 8) { @@ -908,6 +958,8 @@ static int process_markers(struct jpeg* p_jpeg) p_jpeg->y_size |= e_getc(p_jpeg, -1); /* Lowbyte */ p_jpeg->x_size = e_getc(p_jpeg, -1) << 8; /* Highbyte */ p_jpeg->x_size |= e_getc(p_jpeg, -1); /* Lowbyte */ + JDEBUGF(" dimensions: %dx%d\n", p_jpeg->x_size, + p_jpeg->y_size); n = (marker_size-2-6)/3; if (e_getc(p_jpeg, -1) != n || (n != 1 && n != 3)) @@ -1013,8 +1065,13 @@ static int process_markers(struct jpeg* p_jpeg) return(-6); /* Arithmetic coding not supported */ case 0xD8: /* Start of Image */ + JDEBUGF("SOI\n"); + break; case 0xD9: /* End of Image */ + JDEBUGF("EOI\n"); + break; case 0x01: /* for temp private use arith code */ + JDEBUGF("private\n"); break; /* skip parameterless marker */ @@ -1107,6 +1164,7 @@ static int process_markers(struct jpeg* p_jpeg) marker_size = e_getc(p_jpeg, -1) << 8; /* Highbyte */ marker_size |= e_getc(p_jpeg, -1); /* Lowbyte */ marker_size -= 2; + JDEBUGF("unhandled marker len %d\n", marker_size); e_skip_bytes(p_jpeg, marker_size); /* skip segment */ } break; @@ -1693,8 +1751,10 @@ static struct img_part *store_row_jpeg(void *jpeg_args) int mcu_hscale = p_jpeg->h_scale[1]; int mcu_vscale = p_jpeg->v_scale[1]; #else - int mcu_hscale = (p_jpeg->h_scale[0] + p_jpeg->frameheader[0].horizontal_sampling - 1); - int mcu_vscale = (p_jpeg->v_scale[0] + p_jpeg->frameheader[0].vertical_sampling - 1); + int mcu_hscale = (p_jpeg->h_scale[0] + + p_jpeg->frameheader[0].horizontal_sampling - 1); + int mcu_vscale = (p_jpeg->v_scale[0] + + p_jpeg->frameheader[0].vertical_sampling - 1); #endif unsigned int width = p_jpeg->x_mbl << mcu_hscale; unsigned int b_width = width * JPEG_PIX_SZ; @@ -1855,6 +1915,7 @@ static struct img_part *store_row_jpeg(void *jpeg_args) * Reads a JPEG file and puts the data in rockbox format in *bitmap. * *****************************************************************************/ +#ifndef JPEG_FROM_MEM int read_jpeg_file(const char* filename, struct bitmap *bm, int maxsize, @@ -1874,6 +1935,7 @@ int read_jpeg_file(const char* filename, close(fd); return ret; } +#endif static int calc_scale(int in_size, int out_size) { @@ -1889,7 +1951,28 @@ static int calc_scale(int in_size, int out_size) return scale; } +#ifdef JPEG_FROM_MEM +int get_jpeg_dim_mem(unsigned char *data, unsigned long len, + struct dim *size) +{ + struct jpeg *p_jpeg = &jpeg; + memset(p_jpeg, 0, sizeof(struct jpeg)); + p_jpeg->data = data; + p_jpeg->len = len; + int status = process_markers(p_jpeg); + if (status < 0) + return status; + if ((status & (DQT | SOF0)) != (DQT | SOF0)) + return -(status * 16); + size->width = p_jpeg->x_size; + size->height = p_jpeg->y_size; + return 0; +} + +int decode_jpeg_mem(unsigned char *data, unsigned long len, +#else int read_jpeg_fd(int fd, +#endif struct bitmap *bm, int maxsize, int format, @@ -1898,17 +1981,25 @@ int read_jpeg_fd(int fd, bool resize = false, dither = false; struct rowset rset; struct dim src_dim; - struct jpeg *p_jpeg = (struct jpeg*)bm->data; - int tmp_size = maxsize; int status; int bm_size; +#ifdef JPEG_FROM_MEM + struct jpeg *p_jpeg = &jpeg; +#else + struct jpeg *p_jpeg = (struct jpeg*)bm->data; + int tmp_size = maxsize; ALIGN_BUFFER(p_jpeg, tmp_size, sizeof(int)); /* not enough memory for our struct jpeg */ if ((size_t)tmp_size < sizeof(struct jpeg)) return -1; - +#endif memset(p_jpeg, 0, sizeof(struct jpeg)); +#ifdef JPEG_FROM_MEM + p_jpeg->data = data; + p_jpeg->len = len; +#else p_jpeg->fd = fd; +#endif status = process_markers(p_jpeg); if (status < 0) return status; @@ -1968,14 +2059,16 @@ int read_jpeg_fd(int fd, char *buf_start = (char *)bm->data + bm_size; char *buf_end = (char *)bm->data + maxsize; maxsize = buf_end - buf_start; +#ifndef JPEG_FROM_MEM ALIGN_BUFFER(buf_start, maxsize, sizeof(uint32_t)); if (maxsize < (int)sizeof(struct jpeg)) return -1; memmove(buf_start, p_jpeg, sizeof(struct jpeg)); p_jpeg = (struct jpeg *)buf_start; - fix_huff_tables(p_jpeg); buf_start += sizeof(struct jpeg); maxsize = buf_end - buf_start; +#endif + fix_huff_tables(p_jpeg); #ifdef HAVE_LCD_COLOR int decode_buf_size = (p_jpeg->x_mbl << p_jpeg->h_scale[1]) << p_jpeg->v_scale[1];