diff options
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/buffering.c | 12 | ||||
-rw-r--r-- | apps/gui/gwps.c | 3 | ||||
-rw-r--r-- | apps/gui/gwps.h | 18 | ||||
-rw-r--r-- | apps/gui/wps_parser.c | 74 | ||||
-rw-r--r-- | apps/plugins/sliding_puzzle.c | 41 | ||||
-rw-r--r-- | apps/recorder/albumart.c | 9 | ||||
-rw-r--r-- | apps/recorder/albumart.h | 2 | ||||
-rw-r--r-- | apps/recorder/bmp.c | 752 | ||||
-rw-r--r-- | apps/recorder/bmp.h | 157 | ||||
-rw-r--r-- | apps/recorder/resize.c | 718 | ||||
-rw-r--r-- | apps/recorder/resize.h | 60 | ||||
-rw-r--r-- | docs/CREDITS | 1 | ||||
-rw-r--r-- | firmware/export/lcd.h | 2 |
14 files changed, 1453 insertions, 397 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index 2ea41c290a..c63bcb78e2 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -85,6 +85,7 @@ gui/viewport.c #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) gui/backdrop.c +recorder/resize.c #endif #ifdef HAVE_LCD_CHARCELLS diff --git a/apps/buffering.c b/apps/buffering.c index ba613eb9db..dfc90065d6 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -52,6 +52,9 @@ #include "bmp.h" #include "appevents.h" #include "metadata.h" +#ifdef HAVE_ALBUMART +#include "albumart.h" +#endif #if MEM > 1 #define GUARD_BUFSIZE (32*1024) @@ -852,8 +855,13 @@ static int load_bitmap(int fd) bmp->maskdata = NULL; #endif - int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx); - rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_DITHER); + int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx) + - sizeof(struct bitmap); + + get_albumart_size(bmp); + + rc = read_bmp_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| + FORMAT_RESIZE|FORMAT_KEEP_ASPECT); return rc + (rc > 0 ? sizeof(struct bitmap) : 0); } #endif diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c index 9b6d6b5558..a9ad5788b7 100644 --- a/apps/gui/gwps.c +++ b/apps/gui/gwps.c @@ -813,6 +813,9 @@ void gui_sync_wps_init(void) FOR_NB_SCREENS(i) { wps_data_init(&wps_datas[i]); +#ifdef HAVE_ALBUMART + wps_datas[i].wps_uses_albumart = 0; +#endif #ifdef HAVE_REMOTE_LCD wps_datas[i].remote_wps = (i != 0); #endif diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h index 4cffb0de2c..6311bc058b 100644 --- a/apps/gui/gwps.h +++ b/apps/gui/gwps.h @@ -47,13 +47,11 @@ #define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ #define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ -#define WPS_ALBUMART_ALIGN_RIGHT WPS_ALIGN_RIGHT /* x align: right */ -#define WPS_ALBUMART_ALIGN_CENTER WPS_ALIGN_CENTER /* x/y align: center */ -#define WPS_ALBUMART_ALIGN_LEFT WPS_ALIGN_LEFT /* x align: left */ -#define WPS_ALBUMART_ALIGN_TOP WPS_ALIGN_RIGHT /* y align: top */ -#define WPS_ALBUMART_ALIGN_BOTTOM WPS_ALIGN_LEFT /* y align: bottom */ -#define WPS_ALBUMART_INCREASE 8 /* increase if smaller */ -#define WPS_ALBUMART_DECREASE 16 /* decrease if larger */ +#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ #endif /* HAVE_ALBUMART */ @@ -382,10 +380,8 @@ struct wps_data unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ short albumart_x; short albumart_y; - unsigned short albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT, - + .._INCREASE, + .._DECREASE */ - unsigned short albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM, - + .._INCREASE, + .._DECREASE */ + unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ + unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ short albumart_max_width; short albumart_max_height; diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c index 42853e8fae..307d0bb784 100644 --- a/apps/gui/wps_parser.c +++ b/apps/gui/wps_parser.c @@ -969,23 +969,15 @@ static int parse_albumart_load(const char *wps_bufptr, { const char *_pos, *newline; bool parsing; - const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_RIGHT; - const short yalign_mask = WPS_ALBUMART_ALIGN_TOP | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_BOTTOM; - (void)token; /* silence warning */ /* reset albumart info in wps */ - wps_data->wps_uses_albumart = WPS_ALBUMART_NONE; wps_data->albumart_max_width = -1; wps_data->albumart_max_height = -1; wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ - /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */ + /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */ newline = strchr(wps_bufptr, '\n'); @@ -1020,35 +1012,24 @@ static int parse_albumart_load(const char *wps_bufptr, case 'l': case 'L': case '+': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_LEFT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT; break; case 'c': case 'C': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'r': case 'R': case '-': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_RIGHT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT; break; case 'd': case 'D': - wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE; - break; case 'i': case 'I': - wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE; - break; case 's': case 'S': - wps_data->albumart_xalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + /* simply ignored */ break; default: parsing = false; @@ -1080,35 +1061,24 @@ static int parse_albumart_load(const char *wps_bufptr, case 't': case 'T': case '-': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_TOP; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP; break; case 'c': case 'C': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'b': case 'B': case '+': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_BOTTOM; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM; break; case 'd': case 'D': - wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE; - break; case 'i': case 'I': - wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE; - break; case 's': case 'S': - wps_data->albumart_yalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + /* simply ignored */ break; default: parsing = false; @@ -1524,9 +1494,6 @@ static void wps_reset(struct wps_data *data) bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */ #endif memset(data, 0, sizeof(*data)); -#ifdef HAVE_ALBUMART - data->wps_uses_albumart = WPS_ALBUMART_NONE; -#endif wps_data_init(data); #ifdef HAVE_REMOTE_LCD data->remote_wps = rwps; @@ -1617,6 +1584,14 @@ bool wps_data_load(struct wps_data *wps_data, const char *buf, bool isfile) { +#ifdef HAVE_ALBUMART + struct mp3entry *curtrack; + long offset; + int status; + int wps_uses_albumart = wps_data->wps_uses_albumart; + int albumart_max_height = wps_data->albumart_max_height; + int albumart_max_width = wps_data->albumart_max_width; +#endif if (!wps_data || !buf) return false; @@ -1732,6 +1707,21 @@ bool wps_data_load(struct wps_data *wps_data, return false; } #endif +#ifdef HAVE_ALBUMART + status = audio_status(); + if (((!wps_uses_albumart && wps_data->wps_uses_albumart) || + (wps_data->wps_uses_albumart && + (albumart_max_height != wps_data->albumart_max_height || + albumart_max_width != wps_data->albumart_max_width))) && + status & AUDIO_STATUS_PLAY) + { + curtrack = audio_current_track(); + offset = curtrack->offset; + audio_stop(); + if (!(status & AUDIO_STATUS_PAUSE)) + audio_play(offset); + } +#endif return true; } } diff --git a/apps/plugins/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c index 755129c9b9..fddb6bebc2 100644 --- a/apps/plugins/sliding_puzzle.c +++ b/apps/plugins/sliding_puzzle.c @@ -249,11 +249,12 @@ static int num_font = FONT_UI; static int moves_font = FONT_UI; static int moves_y = 0; +#ifdef HAVE_LCD_COLOR +static unsigned char *img_buf; +static size_t buf_len; +#else static unsigned char img_buf[IMAGE_WIDTH*IMAGE_HEIGHT*sizeof(fb_data)] __attribute__ ((aligned(16))); -#if LCD_DEPTH>1 -static unsigned char temp_img_buf[LCD_WIDTH*LCD_HEIGHT*sizeof(fb_data)] -__attribute__ ((aligned(16))); #endif #ifdef HAVE_ALBUMART static char albumart_path[MAX_PATH+1]; @@ -330,37 +331,22 @@ static bool load_resize_bitmap(void) rb->memset(&main_bitmap,0,sizeof(struct bitmap)); main_bitmap.data = img_buf; -#if LCD_DEPTH>1 - struct bitmap temp_bitmap; - rb->memset(&temp_bitmap,0,sizeof(struct bitmap)); - temp_bitmap.data = temp_img_buf; - main_bitmap.width = IMAGE_WIDTH; main_bitmap.height = IMAGE_HEIGHT; - rc = rb->read_bmp_file( filename, &temp_bitmap, sizeof(temp_img_buf), - FORMAT_NATIVE ); - if( rc > 0 ) - { -#ifdef HAVE_LCD_COLOR - smooth_resize_bitmap( &temp_bitmap, &main_bitmap ); -#else - simple_resize_bitmap( &temp_bitmap, &main_bitmap ); +#ifndef HAVE_LCD_COLOR + size_t buf_len = sizeof(img_buf); #endif - puzzle_bmp_ptr = (const fb_data *)img_buf; - rb->strcpy( img_buf_path, filename ); - return true; - } -#else - rc = rb->read_bmp_file( filename, &main_bitmap, sizeof(img_buf), - FORMAT_NATIVE ); + + rc = rb->read_bmp_file( filename, &main_bitmap, + buf_len, + FORMAT_NATIVE|FORMAT_RESIZE ); if( rc > 0 ) { puzzle_bmp_ptr = (const fb_data *)img_buf; rb->strcpy( img_buf_path, filename ); return true; } -#endif } /* something must have failed. get_albumart_bmp_path could return @@ -632,6 +618,13 @@ enum plugin_status plugin_start(const struct plugin_api* api, const void* parame initial_bmp_path=(const char *)parameter; picmode = PICMODE_INITIAL_PICTURE; img_buf_path[0] = '\0'; +#ifdef HAVE_LCD_COLOR + unsigned char *img_buf_end; + img_buf = (unsigned char *)(rb->plugin_get_buffer(&buf_len)); + img_buf_end = img_buf + buf_len; + rb->align_buffer(PUN_PTR(void **,&img_buf), buf_len, 16); + buf_len = img_buf_end - img_buf; +#endif /* If launched as a viewer, just go straight to the game without bothering with the splash or instructions page */ diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c index 30a4e0c49f..babf63ef1c 100644 --- a/apps/recorder/albumart.c +++ b/apps/recorder/albumart.c @@ -297,3 +297,12 @@ void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear) gwps->display->set_drawmode(DRMODE_SOLID); } } + +void get_albumart_size(struct bitmap *bmp) +{ + /* FIXME: What should we do with albumart on remote? */ + struct wps_data *data = gui_wps[0].data; + + bmp->width = data->albumart_max_width; + bmp->height = data->albumart_max_height; +} diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h index 52e7c74c02..ecfdb9c9ed 100644 --- a/apps/recorder/albumart.h +++ b/apps/recorder/albumart.h @@ -40,6 +40,8 @@ void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear); bool search_albumart_files(const struct mp3entry *id3, const char *size_string, char *buf, int buflen); +void get_albumart_size(struct bitmap *bmp); + #endif /* HAVE_ALBUMART */ #endif /* _ALBUMART_H_ */ diff --git a/apps/recorder/bmp.c b/apps/recorder/bmp.c index 9327ac5ec4..cdff8ccb07 100644 --- a/apps/recorder/bmp.c +++ b/apps/recorder/bmp.c @@ -29,6 +29,13 @@ - better protection against malformed / non-standard BMPs - code heavily optimised for both size and speed - dithering for 2 bit targets +2008-11-02 Akio Idehara: refactor for scaler frontend +2008-12-08 Andrew Mahone: partial-line reading, scaler frontend + - read_part_line does the actual source BMP reading, return columns read + and updates fields in a struct bmp_args with the new data and current + reader state + - skip_lines_bmp and store_part_bmp implement the scaler callbacks to skip + ahead by whole lines, or read the next chunk of the current line */ #include <stdio.h> @@ -41,10 +48,16 @@ #ifdef HAVE_REMOTE_LCD #include "lcd-remote.h" #endif +#ifdef ROCKBOX_DEBUG_BMP_LOADER +#define BDEBUGF DEBUGF +#else +#define BDEBUGF(...) +#endif #ifndef __PCTOOL__ #include "config.h" #include "system.h" #include "bmp.h" +#include "resize.h" #include "debug.h" #else #undef DEBUGF @@ -88,16 +101,31 @@ union rgb_union { uint32_t raw; }; -/* masks for supported BI_BITFIELDS encodings (16/32 bit), little endian */ -static const unsigned char bitfields[3][12] = { - { 0x00,0x7c,0x00,0, 0xe0,0x03,0x00,0, 0x1f,0x00,0x00,0 }, /* 15 bit */ - { 0x00,0xf8,0x00,0, 0xe0,0x07,0x00,0, 0x1f,0x00,0x00,0 }, /* 16 bit */ - { 0x00,0x00,0xff,0, 0x00,0xff,0x00,0, 0xff,0x00,0x00,0 }, /* 32 bit */ +/* masks for supported BI_BITFIELDS encodings (16/32 bit) */ +static const struct uint8_rgb bitfields[3][3] = { + /* 15bit */ + { + { .blue = 0x00, .green = 0x7c, .red = 0x00 }, + { .blue = 0xe0, .green = 0x03, .red = 0x00 }, + { .blue = 0x1f, .green = 0x00, .red = 0x00 }, + }, + /* 16bit */ + { + { .blue = 0x00, .green = 0xf8, .red = 0x00 }, + { .blue = 0xe0, .green = 0x07, .red = 0x00 }, + { .blue = 0x1f, .green = 0x00, .red = 0x00 }, + }, + /* 32bit */ + { + { .blue = 0x00, .green = 0x00, .red = 0xff }, + { .blue = 0x00, .green = 0xff, .red = 0x00 }, + { .blue = 0xff, .green = 0x00, .red = 0x00 }, + }, }; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) /* canonical ordered dither matrix */ -static const unsigned char dither_matrix[16][16] = { +const unsigned char dither_matrix[16][16] = { { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, @@ -120,31 +148,11 @@ static const unsigned char dither_matrix[16][16] = { #if ((LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED)) \ || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH == 2) \ && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)) -static const unsigned short vi_pattern[4] = { +const unsigned short vi_pattern[4] = { 0x0101, 0x0100, 0x0001, 0x0000 }; #endif -/* little endian functions */ -static inline unsigned readshort(uint16_t *value) -{ - unsigned char* bytes = (unsigned char*) value; - return (unsigned)bytes[0] | ((unsigned)bytes[1] << 8); -} - -static inline uint32_t readlong(uint32_t *value) -{ - unsigned char* bytes = (unsigned char*) value; - return (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) | - ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); -} - -static inline unsigned brightness(union rgb_union color) -{ - return (3 * (unsigned)color.red + 6 * (unsigned)color.green - + (unsigned)color.blue) / 10; -} - /****************************************************************************** * read_bmp_file() * @@ -165,11 +173,219 @@ int read_bmp_file(const char* filename, return fd * 10 - 1; } + BDEBUGF("read_bmp_file: '%s' remote: %d resize: %d keep_aspect: %d\n", + filename, !!(format & FORMAT_REMOTE), !!(format & FORMAT_RESIZE), + !!(format & FORMAT_KEEP_ASPECT)); ret = read_bmp_fd(fd, bm, maxsize, format); close(fd); return ret; } +static inline void set_rgb_union(struct uint8_rgb *dst, union rgb_union src) +{ + dst->red = src.red; + dst->green = src.green; + dst->blue = src.blue; +} + +struct bmp_args { + int fd; + short padded_width; + short read_width; + short width; + short depth; + unsigned char buf[MAX_WIDTH * 4]; + struct uint8_rgb *palette; +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + int cur_row; + int cur_col; + struct img_part part; +#endif +}; + +static unsigned int read_part_line(struct bmp_args *ba) +{ + const int padded_width = ba->padded_width; + const int read_width = ba->read_width; + const int width = ba->width; + const int depth = ba->depth; +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + int cur_row = ba->cur_row; + int cur_col = ba->cur_col; +#endif + const int fd = ba->fd; + uint8_t *ibuf; + struct uint8_rgb *buf = (struct uint8_rgb *)(ba->buf); + const struct uint8_rgb *palette = ba->palette; + uint32_t component, data = data; + int ret; + int i, cols, len; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + cols = MIN(width - cur_col,(int)MAX_WIDTH); + len = (cols * (depth == 15 ? 16 : depth) + 7) >> 3; +#else + cols = width; + len = read_width; +#endif + ibuf = ((unsigned char *)buf) + (MAX_WIDTH << 2) - len; + BDEBUGF("read_part_line: cols=%d len=%d\n",cols,len); + ret = read(fd, ibuf, len); + if (ret != len) + { + DEBUGF("read_part_line: error reading image, read returned %d " + "expected %d\n", ret, len); + BDEBUGF("cur_row: %d cur_col: %d cols: %d len: %d\n", cur_row, cur_col, + cols, len); + return 0; + } + for (i = 0; i < cols; i++) + { + switch (depth) + { + case 1: + if ((i & 7) == 0) + data = *ibuf++; + *buf = palette[(data >> 7) & 1]; + data <<= 1; + break; + case 4: + *buf = palette[*ibuf >> 4]; + if (i & 1) + ibuf++; + else + *ibuf <<= 4; + break; + case 8: + *buf = palette[*ibuf++]; + break; + case 15: + case 16: + data = letoh16(*(uint16_t*)ibuf); + component = (data << 3) & 0xf8; + component |= component >> 5; + buf->blue = component; + if (depth == 15) + { + data >>= 2; + component = data & 0xf8; + component |= component >> 5; + } else { + data >>= 3; + component = data & 0xfc; + component |= component >> 6; + } + buf->green = component; + data >>= 5; + component = data & 0xf8; + component |= component >> 5; + buf->red = component; + ibuf += 2; + break; + case 32: + case 24: + buf->blue = *ibuf++; + buf->green = *ibuf++; + buf->red = *ibuf++; + if (depth == 32) + ibuf++; + break; + } + buf++; + } + +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + cur_col += cols; + if (cur_col == width) + { +#endif + int pad = padded_width - read_width; + if (pad > 0) + { + BDEBUGF("seeking %d bytes to next line\n",pad); + lseek(fd, pad, SEEK_CUR); + } +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + cur_col = 0; + BDEBUGF("read_part_line: completed row %d\n", cur_row); + cur_row += 1; + } + + ba->cur_row = cur_row; + ba->cur_col = cur_col; +#endif + return cols; +} + +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) +static struct img_part *store_part_bmp(void *args) +{ + struct bmp_args *ba = (struct bmp_args *)args; + + ba->part.len = read_part_line(ba); + ba->part.buf = (struct uint8_rgb *)ba->buf; + if (ba->part.len) + return &(ba->part); + else + return NULL; +} + +static bool skip_lines_bmp(void *args, unsigned int lines) +{ + struct bmp_args * ba = (struct bmp_args *)args; + + int pad = lines * ba->padded_width + + (ba->cur_col + ? ((ba->cur_col * ba->depth + 7) >> 3) - ba->padded_width + : 0); + if (pad) + { + if(lseek(ba->fd, pad, SEEK_CUR) < 0) + + return false; + } + ba->cur_row += lines + (ba->cur_col ? 1 : 0); + ba->cur_col = 0; + return true; +} +#endif + +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) +static inline int recalc_dimension(struct dim *dst, struct dim *src) +{ + int tmp; + if (dst->width <= 0) + dst->width = LCD_WIDTH; + if (dst->height <= 0) + dst->height = LCD_HEIGHT; +#ifndef HAVE_UPSCALER + if (dst->width > src->width || dst->height > src->height) + { + dst->width = src->width; + dst->height = src->height; + } + if (src->width == dst->width && src->height == dst->height) + return 1; +#endif + tmp = (src->width * dst->height + (src->height >> 1)) / src->height; + if (tmp > dst->width) + dst->height = (src->height * dst->width + (src->width >> 1)) + / src->width; + else + dst->width = tmp; + return src->width == dst->width && src->height == dst->height; +} +#endif + +static inline int rgbcmp(struct uint8_rgb rgb1, struct uint8_rgb rgb2) +{ + if ((rgb1.red == rgb2.red) && (rgb1.green == rgb2.green) && + (rgb1.blue == rgb2.blue)) + return 0; + else + return 1; +} + /****************************************************************************** * read_bmp_fd() * @@ -183,21 +399,22 @@ int read_bmp_fd(int fd, int format) { struct bmp_header bmph; - int width, height, padded_width; - int dst_height, dst_width; + int padded_width; + int read_width; int depth, numcolors, compression, totalsize; - int row, col, ret; - int rowstart, rowstop, rowstep; + int ret; unsigned char *bitmap = bm->data; - uint32_t bmpbuf[LCD_WIDTH]; /* Buffer for one line */ - uint32_t palette[256]; + struct uint8_rgb palette[256]; + bool remote = false; + struct rowset rset; + struct dim src_dim; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - bool transparent = false; + unsigned int resize = IMG_NORESIZE; bool dither = false; -#ifdef HAVE_REMOTE_LCD - bool remote = false; + bool transparent = false; +#ifdef HAVE_REMOTE_LCD if (format & FORMAT_REMOTE) { remote = true; #if LCD_REMOTE_DEPTH == 1 @@ -207,6 +424,12 @@ int read_bmp_fd(int fd, #endif } #endif /* HAVE_REMOTE_LCD */ + + if (format & FORMAT_RESIZE) { + resize = IMG_RESIZE; + format &= ~FORMAT_RESIZE; + } + if (format & FORMAT_TRANSPARENT) { transparent = true; format &= ~FORMAT_TRANSPARENT; @@ -218,7 +441,7 @@ int read_bmp_fd(int fd, #else (void)format; -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ +#endif /*(LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)*/ /* read fileheader */ ret = read(fd, &bmph, sizeof(struct bmp_header)); @@ -231,78 +454,78 @@ int read_bmp_fd(int fd, return -3; } - width = readlong(&bmph.width); - if (width > LCD_WIDTH) { - DEBUGF("read_bmp_fd: Bitmap too wide (%d pixels, max is %d)\n", - width, LCD_WIDTH); - return -4; - } - - height = readlong(&bmph.height); - if (height < 0) { /* Top-down BMP file */ - height = -height; - rowstart = 0; - rowstop = height; - rowstep = 1; + src_dim.width = letoh32(bmph.width); + src_dim.height = letoh32(bmph.height); + if (src_dim.height < 0) { /* Top-down BMP file */ + src_dim.height = -src_dim.height; + rset.rowstep = 1; } else { /* normal BMP */ - rowstart = height - 1; - rowstop = -1; - rowstep = -1; + rset.rowstep = -1; } - depth = readshort(&bmph.bit_count); - padded_width = ((width * depth + 31) >> 3) & ~3; /* 4-byte boundary aligned */ + depth = letoh16(bmph.bit_count); + /* 4-byte boundary aligned */ + read_width = ((src_dim.width * (depth == 15 ? 16 : depth) + 7) >> 3); + padded_width = (read_width + 3) & ~3; + BDEBUGF("width: %d height: %d depth: %d padded_width: %d\n", src_dim.width, + src_dim.height, depth, padded_width); + #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - if (format == FORMAT_ANY) { + if ((format & 3) == FORMAT_ANY) { if (depth == 1) - format = FORMAT_MONO; + format = (format & ~3); else - format = FORMAT_NATIVE; + format = (format & ~3) | FORMAT_NATIVE; } - bm->format = format; -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ - /* returning image size */ - bm->width = width; - bm->height = height; - -#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - if (format == FORMAT_NATIVE) { -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - if (remote) { -#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) - dst_width = width; - dst_height = (height + 7) >> 3; -#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ - totalsize = dst_width * dst_height * sizeof(fb_remote_data); - } else -#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ - { -#if LCD_DEPTH == 2 -#if LCD_PIXELFORMAT == HORIZONTAL_PACKING - dst_width = (width + 3) >> 2; - dst_height = height; -#elif LCD_PIXELFORMAT == VERTICAL_PACKING - dst_width = width; - dst_height = (height + 3) >> 2; -#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED - dst_width = width; - dst_height = (height + 7) >> 3; -#endif /* LCD_PIXELFORMAT */ -#elif LCD_DEPTH == 16 - dst_width = width; - dst_height = height; -#endif /* LCD_DEPTH */ - totalsize = dst_width * dst_height * sizeof(fb_data); - } - } else -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + bm->format = format & 3; + if ((format & 3) == FORMAT_MONO) { - dst_width = width; - dst_height = (height + 7) >> 3; - totalsize = dst_width * dst_height; + resize &= ~IMG_RESIZE; + resize |= IMG_NORESIZE; + remote = 0; + } +#else + if (src_dim.width > MAX_WIDTH) + return -6; +#endif /*(LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)*/ + +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + if (resize & IMG_RESIZE) { + if(format & FORMAT_KEEP_ASPECT) { + /* keep aspect ratio.. */ + format &= ~FORMAT_KEEP_ASPECT; + struct dim resize_dim = { + .width = bm->width, + .height = bm->height, + }; + if (recalc_dimension(&resize_dim, &src_dim)) + resize = IMG_NORESIZE; + bm->width = resize_dim.width; + bm->height = resize_dim.height; + } } + if (!(resize & IMG_RESIZE)) { +#endif + /* returning image size */ + bm->width = src_dim.width; + bm->height = src_dim.height; + +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + } +#endif + + if (rset.rowstep > 0) { /* Top-down BMP file */ + rset.rowstart = 0; + rset.rowstop = bm->height; + } else { /* normal BMP */ + rset.rowstart = bm->height - 1; + rset.rowstop = -1; + } + + totalsize = get_totalsize(bm, remote); + /* Check if this fits the buffer */ if (totalsize > maxsize) { DEBUGF("read_bmp_fd: Bitmap too large for buffer: " @@ -310,20 +533,24 @@ int read_bmp_fd(int fd, return -6; } - compression = readlong(&bmph.compression); + compression = letoh32(bmph.compression); if (depth <= 8) { - numcolors = readlong(&bmph.clr_used); + numcolors = letoh32(bmph.clr_used); if (numcolors == 0) numcolors = 1 << depth; } else numcolors = (compression == 3) ? 3 : 0; - + if (numcolors > 0 && numcolors <= 256) { - if (read(fd, palette, numcolors * sizeof(uint32_t)) - != numcolors * (int)sizeof(uint32_t)) - { - DEBUGF("read_bmp_fd: Can't read color palette\n"); - return -7; + int i; + union rgb_union pal; + for (i = 0; i < numcolors; i++) { + if (read(fd, &pal, sizeof(pal)) != (int)sizeof(pal)) + { + DEBUGF("read_bmp_fd: Can't read color palette\n"); + return -7; + } + set_rgb_union(&palette[i], pal); } } @@ -343,15 +570,27 @@ int read_bmp_fd(int fd, case 32: if (compression == 3) { /* BI_BITFIELDS */ - if (!memcmp(palette, bitfields[0], 12)) { /* 15 bit */ - depth = 15; - break; + bool found; + int i, j; + + /* (i == 0) is 15bit, (i == 1) is 16bit, (i == 2) is 32bit */ + for (i = 0; i < ARRAY_SIZE(bitfields); i++) { + for (j = 0; j < ARRAY_SIZE(bitfields[0]); j++) { + if (!rgbcmp(palette[j], bitfields[i][j])) { + found = true; + } else { + found = false; + break; + } + } + if (found) { + if (i == 0) /* 15bit */ + depth = 15; + break; + } } - if (!memcmp(palette, bitfields[1], 12) /* 16 bit */ - || !memcmp(palette, bitfields[2], 12)) /* 32 bit */ - { + if (found) break; - } } /* else fall through */ default: @@ -364,227 +603,104 @@ int read_bmp_fd(int fd, } /* Search to the beginning of the image data */ - lseek(fd, (off_t)readlong(&bmph.off_bits), SEEK_SET); + lseek(fd, (off_t)letoh32(bmph.off_bits), SEEK_SET); memset(bitmap, 0, totalsize); - /* loop to read rows and put them to buffer */ - for (row = rowstart; row != rowstop; row += rowstep) { - unsigned data, mask; - unsigned char *p; - uint16_t *p2; - uint32_t *rp; - union rgb_union *qp; - union rgb_union q0, q1; - - /* read one row */ - ret = read(fd, bmpbuf, padded_width); - if (ret != padded_width) { - DEBUGF("read_bmp_fd: error reading image, read returned: %d " - "expected: %d\n", ret, padded_width); - return -9; - } + struct bmp_args ba = { + .fd = fd, .padded_width = padded_width, .read_width = read_width, + .width = src_dim.width, .depth = depth, .palette = palette, +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + .cur_row = 0, .cur_col = 0, .part = {0,0} +#endif + }; - /* convert whole line in-place to XRGB8888 (little endian) */ - rp = bmpbuf + width; - switch (depth) { - case 1: - q0.raw = palette[0]; - q1.raw = palette[1]; - p = (unsigned char*)bmpbuf + ((width + 7) >> 3); - mask = 0x80 >> ((width + 7) & 7); - while (p > (unsigned char*)bmpbuf) { - data = *(--p); - for (; mask <= 0x80; mask <<= 1) - *(--rp) = (data & mask) ? q1.raw : q0.raw; - mask = 0x01; - } - break; +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) +#if LCD_DEPTH == 16 +#ifdef HAVE_REMOTE_LCD + if (resize & IMG_RESIZE || remote) +#else + if (resize & IMG_RESIZE) +#endif +#else + if (format == FORMAT_NATIVE) +#endif + return resize_on_load(bm, dither, &src_dim, &rset, remote, +#ifdef HAVE_LCD_COLOR + bitmap + totalsize, maxsize - totalsize, +#endif + store_part_bmp, skip_lines_bmp, &ba); +#endif /* LCD_DEPTH */ - case 4: - if (width & 1) - rp++; - p = (unsigned char*)bmpbuf + ((width + 1) >> 1); - while (p > (unsigned char*)bmpbuf) { - data = *(--p); - *(--rp) = palette[data & 0x0f]; - *(--rp) = palette[data >> 4]; - } - break; + int fb_width = get_fb_width(bm, remote); + int col, row; - case 8: - p = (unsigned char*)bmpbuf + width; - while (p > (unsigned char*)bmpbuf) - *(--rp) = palette[*(--p)]; - break; - - case 15: - case 16: - p2 = (uint16_t *)bmpbuf + width; - while (p2 > (uint16_t *)bmpbuf) { - unsigned component, rgb; - - data = letoh16(*(--p2)); - /* blue */ - component = (data << 3) & 0xf8; -#ifdef ROCKBOX_BIG_ENDIAN - rgb = (component | (component >> 5)) << 8; - /* green */ - data >>= 2; - if (depth == 15) { - component = data & 0xf8; - rgb |= component | (component >> 5); - } else { - data >>= 1; - component = data & 0xfc; - rgb |= component | (component >> 6); - } - /* red */ - data >>= 5; - component = data & 0xf8; - rgb = (rgb << 8) | component | (component >> 5); - *(--rp) = rgb << 8; -#else /* little endian */ - rgb = component | (component >> 5); - /* green */ - data >>= 2; - if (depth == 15) { - component = data & 0xf8; - rgb |= (component | (component >> 5)) << 8; - } else { - data >>= 1; - component = data & 0xfc; - rgb |= (component | (component >> 6)) << 8; - } - /* red */ - data >>= 5; - component = data & 0xf8; - rgb |= (component | (component >> 5)) << 16; - *(--rp) = rgb; -#endif - } - break; + /* loop to read rows and put them to buffer */ + for (row = rset.rowstart; row != rset.rowstop; row += rset.rowstep) { + struct uint8_rgb *qp; + unsigned mask; + unsigned char *p; +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + unsigned int len; - case 24: - p = (unsigned char*)bmpbuf + 3 * width; - while (p > (unsigned char*)bmpbuf) { - data = *(--p); - data = (data << 8) | *(--p); - data = (data << 8) | *(--p); - *(--rp) = htole32(data); - } - break; + if (!(len = read_part_line(&ba))) + return -9; +#else + if (!read_part_line(&ba)) + return -9; +#endif - case 32: /* already in desired format */ - break; - } - /* Convert to destination format */ - qp = (union rgb_union *)bmpbuf; -#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - if (format == FORMAT_NATIVE) { -#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 - if (remote) { -#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) - /* iAudio X5/M5 remote */ - fb_remote_data *dest = (fb_remote_data *)bitmap - + dst_width * (row >> 3); - int shift = row & 7; - int delta = 127; - unsigned bright; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= vi_pattern[bright] << shift; - } -#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ - } else -#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ - { -#if LCD_DEPTH == 2 -#if LCD_PIXELFORMAT == HORIZONTAL_PACKING - /* greyscale iPods */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; - int shift = 6; - int delta = 127; - unsigned bright; - unsigned data = 0; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - data |= (~bright & 3) << shift; - shift -= 2; - if (shift < 0) { - *dest++ = data; - data = 0; - shift = 6; - } - } - if (shift < 6) - *dest++ = data; -#elif LCD_PIXELFORMAT == VERTICAL_PACKING - /* iriver H1x0 */ - fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 2); - int shift = 2 * (row & 3); - int delta = 127; - unsigned bright; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= (~bright & 3) << shift; - } -#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED - /* iAudio M3 */ - fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 3); - int shift = row & 7; - int delta = 127; - unsigned bright; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - bright = brightness(*qp++); - bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= vi_pattern[bright] << shift; - } -#endif /* LCD_PIXELFORMAT */ -#elif LCD_DEPTH == 16 - /* iriver h300, colour iPods, X5 */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; - int delta = 127; - unsigned r, g, b; - - for (col = 0; col < width; col++) { - if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; - q0 = *qp++; - r = (31 * q0.red + (q0.red >> 3) + delta) >> 8; - g = (63 * q0.green + (q0.green >> 2) + delta) >> 8; - b = (31 * q0.blue + (q0.blue >> 3) + delta) >> 8; - *dest++ = LCD_RGBPACK_LCD(r, g, b); + qp = (struct uint8_rgb *) ba.buf; +#if LCD_DEPTH == 16 + if (format == FORMAT_NATIVE) + { + /* iriver h300, colour iPods, X5 */ + fb_data *dest = (fb_data *)bitmap + fb_width * row; + int delta = 127; + unsigned r, g, b; + struct uint8_rgb q0; + + for (col = 0; col < src_dim.width; col++) { + if (dither) + delta = dither_mat(row & 0xf, col & 0xf); + if (!len) + { + if(!(len = read_part_line(&ba))) + return -9; + else + qp = (struct uint8_rgb *)ba.buf; } -#endif /* LCD_DEPTH */ + q0 = *qp++; + len--; + r = (31 * q0.red + (q0.red >> 3) + delta) >> 8; + g = (63 * q0.green + (q0.green >> 2) + delta) >> 8; + b = (31 * q0.blue + (q0.blue >> 3) + delta) >> 8; + *dest++ = LCD_RGBPACK_LCD(r, g, b); } - } else -#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + } + else +#endif { - p = bitmap + dst_width * (row >> 3); + p = bitmap + fb_width * (row >> 3); mask = 1 << (row & 7); - - for (col = 0; col < width; col++, p++) + for (col = 0; col < src_dim.width; col++) + { +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) + if (!len) + { + if(!(len = read_part_line(&ba))) + return -9; + else + qp = (struct uint8_rgb *)ba.buf; + } + len--; +#endif if (brightness(*qp++) < 128) *p |= mask; + p++; + } } } - return totalsize; /* return the used buffer size. */ } diff --git a/apps/recorder/bmp.h b/apps/recorder/bmp.h index 3ac73de03b..1f29103556 100644 --- a/apps/recorder/bmp.h +++ b/apps/recorder/bmp.h @@ -23,6 +23,163 @@ #include "config.h" #include "lcd.h" +#include "inttypes.h" +#ifdef HAVE_REMOTE_LCD +#include "lcd-remote.h" +#endif + +#define ARRAY_SIZE(array) (int)(sizeof(array)/(sizeof(array[0]))) + +#define IMG_NORESIZE 0 +#define IMG_RESIZE 1 +#if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) +#define MAX_WIDTH 8 +#else +#define MAX_WIDTH LCD_WIDTH +#endif + +struct uint8_rgb { + uint8_t blue; + uint8_t green; + uint8_t red; +}; + +struct dim { + short width; + short height; +}; + +struct rowset { + short rowstep; + short rowstart; + short rowstop; +}; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) +extern const unsigned char dither_matrix[16][16]; +static inline unsigned char dither_mat(unsigned int x, unsigned int y) +{ + return dither_matrix[y][x]; +} +#endif + +static inline unsigned brightness(struct uint8_rgb color) +{ + return (3 * (unsigned)color.red + 6 * (unsigned)color.green + + (unsigned)color.blue) / 10; +} + +#if ((LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED)) \ + || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH == 2) \ + && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)) +extern const unsigned short vi_pattern[4]; +static inline unsigned short vi_pat(unsigned int bright) +{ + return vi_pattern[bright]; +} +#endif + +static inline int get_fb_height(struct bitmap *bm, bool remote) +{ + const int height = bm->height; +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + const int format = bm->format; +#endif + int dst_height; + +#if !defined(HAVE_REMOTE_LCD) || \ + (defined(HAVE_REMOTE_LCD) &&(LCD_REMOTE_DEPTH == 1)) + (void) remote; +#endif + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + if (format == FORMAT_NATIVE) { +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (remote) { +#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + dst_height = (height + 7) >> 3; +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ + { +#if LCD_DEPTH == 2 +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + dst_height = height; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + dst_height = (height + 3) >> 2; +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + dst_height = (height + 7) >> 3; +#endif /* LCD_PIXELFORMAT */ +#elif LCD_DEPTH == 16 + dst_height = height; +#endif /* LCD_DEPTH */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + dst_height = (height + 7) >> 3; + } + + return dst_height; +} + +static inline int get_fb_width(struct bitmap *bm, bool remote) +{ + const int width = bm->width; +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + const int format = bm->format; +#endif + int dst_width; + +#if !defined(HAVE_REMOTE_LCD) || \ + (defined(HAVE_REMOTE_LCD) &&(LCD_REMOTE_DEPTH == 1)) + (void) remote; +#endif + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + if (format == FORMAT_NATIVE) { +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (remote) { +#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + dst_width = width; +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ + { +#if LCD_DEPTH == 2 +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + dst_width = (width + 3) >> 2; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + dst_width = width; +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + dst_width = width; +#endif /* LCD_PIXELFORMAT */ +#elif LCD_DEPTH == 16 + dst_width = width; +#endif /* LCD_DEPTH */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + dst_width = width; + } + + return dst_width; +} + +static inline int get_totalsize(struct bitmap *bm, bool remote) +{ + int sz; +#if defined(HAVE_REMOTE_LCD) && \ + (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + if (remote) + sz = sizeof(fb_remote_data); + else +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + sz = sizeof(fb_data); + + return get_fb_width(bm, remote) * get_fb_height(bm, remote) * sz; +} /********************************************************************* * read_bmp_file() diff --git a/apps/recorder/resize.c b/apps/recorder/resize.c new file mode 100644 index 0000000000..cce20cdd77 --- /dev/null +++ b/apps/recorder/resize.c @@ -0,0 +1,718 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id + * + * Copyright (C) 2008 by Akio Idehara, Andrew Mahone + * + * 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. + * + ****************************************************************************/ + +/* + * Implementation of area average and linear row and vertical scalers, and + * nearest-neighbor grey scaler (C) 2008 Andrew Mahone + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "inttypes.h" +#include "debug.h" +#include "lcd.h" +#include "file.h" +#ifdef HAVE_REMOTE_LCD +#include "lcd-remote.h" +#endif +#ifdef ROCKBOX_DEBUG_SCALERS +#define SDEBUGF DEBUGF +#else +#define SDEBUGF(...) +#endif +#ifndef __PCTOOL__ +#include "config.h" +#include "system.h" +#include "bmp.h" +#include "resize.h" +#include "resize.h" +#include "debug.h" +#else +#undef DEBUGF +#define DEBUGF(...) +#endif + +#ifdef HAVE_LCD_COLOR +#define PACKRED(r, delta) ((31 * r + (r >> 3) + delta) >> 8) +#define PACKGREEN(g, delta) ((63 * g + (g >> 2) + delta) >> 8) +#define PACKBLUE(b, delta) ((31 * b + (b >> 3) + delta) >> 8) + +#define FILL_BUF_INIT(img_part, store_part, args) { \ + part = store_part(args); \ + if (part == NULL) \ + return false; \ +} + +#define FILL_BUF(img_part, store_part, args) { \ + if (part->len == 0) \ + part = store_part(args); \ + if (part == NULL) \ + return false; \ +} + +struct uint32_rgb { + uint32_t r; + uint32_t g; + uint32_t b; +}; + +struct scaler_context { + uint32_t divmul; + uint32_t round; + struct img_part* (*store_part)(void *); + long last_tick; + unsigned char *buf; + int len; + void *args; +}; + +static void scale_h_area_setup(struct bitmap *bm, struct dim *src, + struct scaler_context *ctx) +{ + (void) bm; + ctx->divmul = ((src->width - 1 + 0x80000000U) / src->width) << 1; + ctx->round = (src->width + 1) >> 1; +} + +/* horizontal area average scaler */ +static bool scale_h_area(struct bitmap *bm, struct dim *src, + struct uint32_rgb *out_line, + struct scaler_context *ctx, bool accum) +{ + SDEBUGF("scale_h_area\n"); + unsigned int ix, ox, oxe, mul; + struct uint32_rgb rgbval1, rgbval2; + struct img_part *part; + FILL_BUF_INIT(part,ctx->store_part,ctx->args); + ox = 0; + oxe = 0; + rgbval1.r = 0; + rgbval1.g = 0; + rgbval1.b = 0; + rgbval2.r = 0; + rgbval2.g = 0; + rgbval2.b = 0; + mul = 0; + for (ix = 0; ix < (unsigned int)src->width; ix++) + { + oxe += bm->width; + if (oxe >= (unsigned int)src->width) + { + if (ctx->last_tick != current_tick) + { + yield(); + ctx->last_tick = current_tick; + } + oxe -= src->width; + rgbval1.r = rgbval1.r * bm->width + rgbval2.r * mul; + rgbval1.g = rgbval1.g * bm->width + rgbval2.g * mul; + rgbval1.b = rgbval1.b * bm->width + rgbval2.b * mul; + FILL_BUF(part,ctx->store_part,ctx->args); + rgbval2.r = part->buf->red; + rgbval2.g = part->buf->green; + rgbval2.b = part->buf->blue; + part->buf++; + part->len--; + mul = bm->width - oxe; + rgbval1.r += rgbval2.r * mul; + rgbval1.g += rgbval2.g * mul; + rgbval1.b += rgbval2.b * mul; + out_line[ox].r = (accum ? out_line[ox].r : 0) + + (((uint64_t)rgbval1.r + ctx->round) * + ctx->divmul >> 32); + out_line[ox].g = (accum ? out_line[ox].g : 0) + + (((uint64_t)rgbval1.g + ctx->round) * + ctx->divmul >> 32); + out_line[ox].b = (accum ? out_line[ox].b : 0) + + (((uint64_t)rgbval1.b + ctx->round) * + ctx->divmul >> 32); + rgbval1.r = 0; + rgbval1.g = 0; + rgbval1.b = 0; + mul = bm->width - mul; + ox += 1; + } else { + FILL_BUF(part,ctx->store_part,ctx->args); + rgbval1.r += part->buf->red; + rgbval1.g += part->buf->green; + rgbval1.b += part->buf->blue; + part->buf++; + part->len--; + } + } + return true; +} + +/* vertical area average scaler */ +static bool scale_v_area(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + bool (*h_scaler)(struct bitmap*, struct dim*, + struct uint32_rgb*, + struct scaler_context*, bool), + struct scaler_context *ctx) +{ + uint32_t mul, divmul, x, oy, iy, oye, round; + int delta = 127, r, g, b; + fb_data *row, *pix; + divmul = ((src->height - 1 + 0x80000000U) / src->height) << 1; + round = (src->height + 1) >> 1; + mul = 0; + oy = 0; + oye = 0; + struct uint32_rgb *crow1 = (struct uint32_rgb *)(ctx->buf), + *crow2 = crow1 + bm->width; + + SDEBUGF("scale_v_area\n"); + memset((void *)ctx->buf, 0, bm->width * 2 * sizeof(struct uint32_rgb)); + row = (fb_data *)(bm->data) + bm->width * + (rset->rowstep == -1 ? bm->height - 1 : 0); + for (iy = 0; iy < (unsigned int)src->height; iy++) + { + oye += bm->height; + if (oye >= (unsigned int)src->height) + { + oye -= src->height; + for (x = 0; x < 3 *(unsigned int)bm->width; x++) + ((uint32_t*)crow1)[x] = ((uint32_t*)crow1)[x] * + bm->height + mul * + ((uint32_t*)crow2)[x]; + if(!h_scaler(bm, src, crow2, ctx, false)) + goto fail; + mul = bm->height - oye; + for (x = 0; x < 3 *(unsigned int)bm->width; x++) + { + ((uint32_t*)crow1)[x] += mul * ((uint32_t*)crow2)[x]; + ((uint32_t*)crow1)[x] = (uint64_t)(round + + ((uint32_t*)crow1)[x]) * + divmul >> 32; + } + pix = row; + for (x = 0; x < (unsigned int)bm->width; x++) + { + if (dither) + delta = dither_mat(x & 0xf, oy & 0xf); + r = PACKRED(crow1[x].r,delta); + g = PACKGREEN(crow1[x].g,delta); + b = PACKBLUE(crow1[x].b,delta); + *pix++ = LCD_RGBPACK_LCD(r, g, b); + } + memset((void *)crow1, 0, bm->width * sizeof(struct uint32_rgb)); + mul = oye; + row += bm->width * rset->rowstep; + oy += 1; + } else { + if (!h_scaler(bm, src, crow1, ctx, true)) + goto fail; + } + } + return true; + fail: + return false; +} + +#ifdef HAVE_UPSCALER +static void scale_h_linear_setup(struct bitmap *bm, struct dim *src, + struct scaler_context *ctx) +{ + (void) src; + ctx->divmul = ((bm->width - 2 + 0x80000000U) / (bm->width - 1)) << 1; + ctx->round = bm->width >> 1; +} + +/* horizontal linear scaler */ +static bool scale_h_linear(struct bitmap *bm, struct dim *src, + struct uint32_rgb *out_line, + struct scaler_context *ctx, bool accum) +{ + unsigned int ix, ox, ixe; + struct uint32_rgb rgbval=rgbval, rgbinc=rgbinc; + struct img_part *part; + SDEBUGF("scale_h_linear\n"); + FILL_BUF_INIT(part,ctx->store_part,ctx->args); + ix = 0; + ixe = bm->width - 1; + for (ox = 0; ox < (uint32_t)bm->width; ox++) { + if (ixe >= ((uint32_t)bm->width - 1)) + { + if (ctx->last_tick != current_tick) + { + yield(); + ctx->last_tick = current_tick; + } + ixe -= (bm->width - 1); + rgbinc.r = -(part->buf->red); + rgbinc.g = -(part->buf->green); + rgbinc.b = -(part->buf->blue); + rgbval.r = (part->buf->red) * (bm->width - 1); + rgbval.g = (part->buf->green) * (bm->width - 1); + rgbval.b = (part->buf->blue) * (bm->width - 1); + ix += 1; + if (ix < (uint32_t)src->width) { + part->buf++; + part->len--; + FILL_BUF(part,ctx->store_part,ctx->args); + rgbinc.r += part->buf->red; + rgbinc.g += part->buf->green; + rgbinc.b += part->buf->blue; + rgbval.r += rgbinc.r * ixe; + rgbval.g += rgbinc.g * ixe; + rgbval.b += rgbinc.b * ixe; + } + rgbinc.r *= src->width - 1; + rgbinc.g *= src->width - 1; + rgbinc.b *= src->width - 1; + } + out_line[ox].r = (accum ? out_line[ox].r : 0) + + (((uint64_t)rgbval.r + ctx->round) * + ctx->divmul >> 32); + out_line[ox].g = (accum ? out_line[ox].g : 0) + + (((uint64_t)rgbval.g + ctx->round) * + ctx->divmul >> 32); + out_line[ox].b = (accum ? out_line[ox].b : 0) + + (((uint64_t)rgbval.b + ctx->round) * + ctx->divmul >> 32); + rgbval.r += rgbinc.r; + rgbval.g += rgbinc.g; + rgbval.b += rgbinc.b; + ixe += src->width - 1; + } + return true; +} + +/* vertical linear scaler */ +static bool scale_v_linear(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + bool (*h_scaler)(struct bitmap*, struct dim*, + struct uint32_rgb*, + struct scaler_context*, bool), + struct scaler_context *ctx) +{ + uint32_t mul, divmul, x, oy, iy, iye, round; + int delta = 127; + struct uint32_rgb p; + fb_data *row, *pix; + divmul = ((bm->height - 2 + 0x80000000U) / (bm->height - 1)) << 1; + round = bm->height >> 1; + mul = 0; + iy = 0; + iye = bm->height - 1; + struct uint32_rgb *crow1 = (struct uint32_rgb *)(ctx->buf), + *crow2 = crow1 + bm->width, + *t; + + SDEBUGF("scale_v_linear\n"); + row = (fb_data *)(bm->data) + bm->width * + (rset->rowstep == -1 ? bm->height - 1 : 0); + if(!h_scaler(bm, src, crow2, ctx, false)) + goto fail; + for (oy = 0; oy < (uint32_t)bm->height; oy++) + { + if (iye >= (uint32_t)bm->height - 1) + { + t = crow2; + crow2 = crow1; + crow1 = t; + iye -= bm->height - 1; + iy += 1; + if (iy < (uint32_t)src->height) + { + if (!h_scaler(bm, src, crow2, ctx, false)) + goto fail; + } + } + pix = row; + for (x = 0; x < (uint32_t)bm->width; x++) + { + p.r = (uint64_t)(crow1[x].r * (bm->height - 1 - iye) + + crow2[x].r * iye + round) * divmul >> 32; + p.g = (uint64_t)(crow1[x].g * (bm->height - 1 - iye) + + crow2[x].g * iye + round) * divmul >> 32; + p.b = (uint64_t)(crow1[x].b * (bm->height - 1 - iye) + + crow2[x].b * iye + round) * divmul >> 32; + if (dither) + delta = dither_mat(x & 0xf, oy & 0xf); + p.r = PACKRED(p.r,delta); + p.g = PACKGREEN(p.g,delta); + p.b = PACKBLUE(p.b,delta); + *pix++ = LCD_RGBPACK_LCD(p.r, p.g, p.b); + } + row += bm->width * rset->rowstep; + iye += src->height - 1; + } + return true; + fail: + return false; +} +#endif /* HAVE_UPSCALER */ +#endif /* HAVE_LCD_COLOR */ + +#if LCD_DEPTH < 8 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH < 8) +/* nearest-neighbor up/down/non-scaler */ +static inline bool scale_nearest(struct bitmap *bm, + struct dim *src, + struct rowset *rset, + bool remote, bool dither, + struct img_part* (*store_part)(void *args), + bool (*skip_lines)(void *args, unsigned int), + void *args) +{ + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + unsigned char *bitmap = bm->data; + const int rowstep = rset->rowstep; + const int rowstart = rset->rowstart; + const int rowstop = rset->rowstop; + const int fb_width = get_fb_width(bm, false); + long last_tick = current_tick; + int ix, ox, lx, xe, iy, oy, ly, ye, yet, oyt; + int ixls, xels, iyls, yelsi, oyls, yelso, p; + struct img_part *cur_part; +#ifndef HAVE_LCD_COLOR + fb_data *dest, *dest_t; +#endif +#ifdef HAVE_REMOTE_LCD + fb_remote_data *rdest, *rdest_t; +#endif + + SDEBUGF("scale_nearest sw=%d sh=%d dw=%d dh=%d remote=%d\n", sw, sh, dw, + dh, remote); + ly = 0; + iy = 0; + ye = 0; + ixls = (sw > (dw - 1) && dw > 1) ? sw / (dw - 1) : 1; + xels = sw - ixls * (dw - 1) + (dw == 1 ? 1 : 0); + iyls = (sh > (dh - 1) && dh > 1) ? sh / (dh - 1) : 1; + oyls = dh > sh ? dh / sh : 1; + yelsi = iyls * (dh - 1) + (dh == 1 ? 1 : 0); + yelso = oyls * sh; + oyls *= rowstep; + int delta = 127; +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING || \ + (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_PIXELFORMAT == HORIZONTAL_PACKING) + uint8_t buf[4]; + int data, oxt; +#endif +#if LCD_PIXELFORMAT == VERTICAL_PACKING || \ + LCD_PIXELFORMAT == VERTICAL_INTERLEAVED || \ + (defined(HAVE_REMOTE_LCD) && \ + (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED || \ + LCD_REMOTE_PIXELFORMAT == VERTICAL_PACKING)) + int bright, shift; +#endif + for (oy = rowstart; oy != rowstop;) { + SDEBUGF("oy=%d iy=%d\n", oy, iy); + if (last_tick != current_tick) + { + yield(); + last_tick = current_tick; + } + if (iy > ly && !skip_lines(args, iy - ly - 1)) + return false; + ly = iy; + + cur_part = store_part(args); + if (cur_part == NULL) + return false; + + lx = 0; + ix = 0; + xe = 0; +#if defined(HAVE_REMOTE_LCD) && !defined(HAVE_LCD_COLOR) + if(!remote) +#else + (void)remote; +#endif +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + dest = (fb_data *)bitmap + fb_width * oy; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + dest = (fb_data *)bitmap + fb_width * (oy >> 2); +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + dest = (fb_data *)bitmap + fb_width * (oy >> 3); +#endif +#ifdef HAVE_REMOTE_LCD +#ifndef HAVE_LCD_COLOR + else +#endif + rdest = (fb_remote_data *)bitmap + fb_width * (oy >> 3); +#endif + for (ox = 0; ox < dw; ox++) { + while (cur_part->len <= ix - lx) + { + lx += cur_part->len; + cur_part = store_part(args); + if (cur_part == NULL) + return false; + } + cur_part->len -= ix - lx; + cur_part->buf += ix - lx; + lx = ix; +#if defined(HAVE_REMOTE_LCD) && !defined(HAVE_LCD_COLOR) + if(!remote) + { +#endif +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + /* greyscale iPods */ + buf[ox & 3] = brightness(*(cur_part->buf)); + if ((ox & 3) == 3 || ox == dw - 1) + { + dest_t = dest++; + oyt = oy; + yet = ye; + int xo = ox & ~3; + while(yet < dh) + { + data = 0; + for (oxt = 0; oxt < (ox & 3) + 1; oxt++) + { + if (dither) + delta = dither_mat(oyt & 0xf, (xo + oxt) & 0xf); + p = (3 * buf[oxt] + (buf[oxt] >> 6) + delta) >> 8; + data |= (~p & 3) << ((3 - oxt) << 1); + } + *dest_t = data; + dest_t += rowstep * fb_width; + yet += sh; + oyt += 1; + } + } +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + /* iriver H1x0 */ + bright = brightness(*(cur_part->buf)); + dest_t = dest++; + oyt = oy; + yet = ye; + while(yet < dh) + { + shift = (oyt & 3) << 1; + if (dither) + delta = dither_mat(oyt & 0xf, ox & 0xf); + + p = (3 * bright + (bright >> 6) + delta) >> 8; + *dest_t |= (~p & 3) << shift; + if ((rowstep > 0 && shift == 6) || shift == 0) + dest_t += rowstep * fb_width; + yet += sh; + oyt += 1; + } +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + bright = brightness(*(cur_part->buf)); + dest_t = dest++; + oyt = oy; + yet = ye; + while(yet < dh) + { + shift = oyt & 7; + if (dither) + delta = dither_mat(oyt & 0xf, ox & 0xf); + + p = (3 * bright + (bright >> 6) + delta) >> 8; + *dest_t |= vi_pat(p) << shift; + if ((rowstep > 0 && shift == 7) || shift == 0) + dest_t += rowstep * fb_width; + yet += sh; + oyt += 1; + } +#endif /* LCD_PIXELFORMAT */ +#ifdef HAVE_REMOTE_LCD +#ifndef HAVE_LCD_COLOR + } else +#endif + { +#if LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED + bright = brightness(*(cur_part->buf)); + rdest_t = rdest++; + oyt = oy; + yet = ye; + while(yet < dh) + { + shift = oyt & 7; + if (dither) + delta = dither_mat(oyt & 0xf, ox & 0xf); + + p = (3 * bright + (bright >> 6) + delta) >> 8; + *rdest_t |= vi_pat(p) << shift; + if ((rowstep > 0 && shift == 7) || shift == 0) + rdest_t += rowstep * fb_width; + yet += sh; + oyt += 1; + } +#else + bright = brightness(*(cur_part->buf)); + rdest_t = rdest++; + oyt = oy; + yet = ye; + while(yet < dh) + { + shift = oyt & 7; + if (dither) + delta = dither_mat(oyt & 0xf, ox & 0xf); + p = (bright + delta) >> 8; + *rdest_t |= (~p & 1) << shift; + if ((rowstep > 0 && shift == 7) || shift == 0) + rdest_t += rowstep * fb_width; + yet += sh; + oyt += 1; + } +#endif + } +#endif + xe += xels; + ix += ixls; + while (xe >= dw) + { + xe -= dw - 1; + ix += 1; + } + } + oy += oyls; + ye += yelso; + while (ye < dh) + { + ye += sh; + oy += rowstep; + } + iy += iyls; + ye -= yelsi; + while (ye >= dh) + { + ye -= dh - 1; + iy += 1; + } + } + return true; +} +#endif + +int resize_on_load(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, bool remote, +#ifdef HAVE_LCD_COLOR + unsigned char *buf, unsigned int len, +#endif + struct img_part* (*store_part)(void *args), + bool (*skip_lines)(void *args, unsigned int lines), + void *args) +{ + +#if defined(HAVE_LCD_COLOR) && !defined(HAVE_REMOTE_LCD) + (void)skip_lines; +#endif +#ifdef HAVE_LCD_COLOR +#ifdef HAVE_REMOTE_LCD + if (!remote) +#endif + { +#ifdef HAVE_UPSCALER + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; +#endif + int ret; + unsigned int needed = sizeof(struct uint32_rgb) * 2 * bm->width; +#if MAX_SC_STACK_ALLOC + uint8_t sc_buf[(needed <= len || needed > MAX_SC_STACK_ALLOC) ? + 0 : needed]; +#endif + if (needed > len) + { +#if MAX_SC_STACK_ALLOC + if (needed > MAX_SC_STACK_ALLOC) + { + DEBUGF("unable to allocate required buffer: %d needed, " + "%d available, %d permitted from stack\n", + needed, len, MAX_SC_STACK_ALLOC); + return 0; + } + if (sizeof(sc_buf) < needed) + { + DEBUGF("failed to allocate large enough buffer on stack: " + "%d needed, only got %d", + needed, MAX_SC_STACK_ALLOC); + return 0; + } +#else + DEBUGF("unable to allocate required buffer: %d needed, " + "%d available\n", needed, len); + return 0; +#endif + } + + bool (*h_scaler)(struct bitmap*, struct dim*, + struct uint32_rgb*, + struct scaler_context*, bool); + struct scaler_context ctx; + ctx.last_tick = current_tick; + cpu_boost(true); +#ifdef HAVE_UPSCALER + if (sw > dw) + { +#endif + h_scaler = scale_h_area; + scale_h_area_setup(bm, src, &ctx); +#ifdef HAVE_UPSCALER + } else { + h_scaler = scale_h_linear; + scale_h_linear_setup(bm, src, &ctx); + } +#endif + ctx.store_part = store_part; + ctx.args = args; +#if MAX_SC_STACK_ALLOC + ctx.buf = needed > len ? sc_buf : buf; +#else + ctx.buf = buf; +#endif + ctx.len = len; +#ifdef HAVE_UPSCALER + if (sh > dh) +#endif + ret = scale_v_area(bm, dither, src, rset, h_scaler, &ctx); +#ifdef HAVE_UPSCALER + else + ret = scale_v_linear(bm, dither, src, rset, h_scaler, &ctx); +#endif + cpu_boost(false); + if (!ret) + return 0; + } +#ifdef HAVE_REMOTE_LCD + else +#endif +#endif /* HAVE_LCD_COLOR */ +#if !defined(HAVE_LCD_COLOR) || defined(HAVE_REMOTE_LCD) + { + if (!scale_nearest(bm, src, rset, remote, dither, store_part, + skip_lines, args)) + return 0; + } +#endif /* !HAVE_LCD_COLOR || HAVE_REMOTE_LCD*/ + return get_totalsize(bm, remote); +} diff --git a/apps/recorder/resize.h b/apps/recorder/resize.h new file mode 100644 index 0000000000..133ac50fdd --- /dev/null +++ b/apps/recorder/resize.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id + * + * Copyright (C) 2008 by Akio Idehara + * + * 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 _RESIZE_H_ +#define _RESIZE_H_ + +#include "config.h" +#include "lcd.h" + +/**************************************************************** + * resize_on_load() + * + * resize bitmap on load with scaling + * + * If HAVE_LCD_COLOR then this func use smooth scaling algorithm + * - downscaling both way use "Area Sampling" + * if IMG_RESIZE_BILINER or IMG_RESIZE_NEAREST is NOT set + * - otherwise "Bilinear" or "Nearest Neighbour" + * + * If !(HAVE_LCD_COLOR) then use simple scaling algorithm "Nearest Neighbour" + * + * return -1 for error + ****************************************************************/ + +/* nothing needs the on-stack buffer right now */ +#define MAX_SC_STACK_ALLOC 0 +#define HAVE_UPSCALER 1 + +struct img_part { + int len; + struct uint8_rgb* buf; +}; + +int resize_on_load(struct bitmap *bm, bool dither, + struct dim *src, + struct rowset *tmp_row, bool remote, +#ifdef HAVE_LCD_COLOR + unsigned char *buf, unsigned int len, +#endif + struct img_part* (*store_part)(void *args), + bool (*skip_lines)(void *args, unsigned int lines), + void *args); +#endif /* _RESIZE_H_ */ diff --git a/docs/CREDITS b/docs/CREDITS index ba2bc54dbd..2b6128d14a 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -434,6 +434,7 @@ Alex Bennee Stéphane Quertinmont Bartosz Fabianowski Adam Hogan +Andrew Mahone The libmad team diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index 60329147da..ea5851e736 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h @@ -368,6 +368,8 @@ enum #define FORMAT_TRANSPARENT 0x40000000 #define FORMAT_DITHER 0x20000000 #define FORMAT_REMOTE 0x10000000 +#define FORMAT_RESIZE 0x08000000 +#define FORMAT_KEEP_ASPECT 0x04000000 #define TRANSPARENT_COLOR LCD_RGBPACK(255,0,255) #define REPLACEWITHFG_COLOR LCD_RGBPACK(0,255,255) |