diff --git a/apps/SOURCES b/apps/SOURCES index 84f2eec..2b70459 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -93,6 +93,12 @@ recorder/bmp.c recorder/icons.c recorder/keyboard.c recorder/peakmeter.c +recorder/resize-common.c +#ifdef HAVE_LCD_COLOR +recorder/resize-color.c +#else +recorder/resize-gray.c +#endif #ifdef HAVE_ALBUMART recorder/albumart.c #endif diff --git a/apps/plugin.h b/apps/plugin.h index 464614d..3a6239f 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -711,7 +711,7 @@ struct plugin_api { #endif #ifdef HAVE_LCD_BITMAP int (*read_bmp_file)(const char* filename, struct bitmap *bm, int maxsize, - int format); + unsigned int format); void (*screen_dump_set_hook)(void (*hook)(int fh)); #endif int (*show_logo)(void); diff --git a/apps/recorder/bmp.c b/apps/recorder/bmp.c index 9327ac5..23ce9c6 100644 --- a/apps/recorder/bmp.c +++ b/apps/recorder/bmp.c @@ -45,6 +45,7 @@ #include "config.h" #include "system.h" #include "bmp.h" +#include "resize.h" #include "debug.h" #else #undef DEBUGF @@ -88,16 +89,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,7 +136,7 @@ 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 @@ -139,12 +155,6 @@ static inline uint32_t readlong(uint32_t *value) ((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() * @@ -154,7 +164,7 @@ static inline unsigned brightness(union rgb_union color) int read_bmp_file(const char* filename, struct bitmap *bm, int maxsize, - int format) + unsigned int format) { int fd, ret; fd = open(filename, O_RDONLY); @@ -170,6 +180,253 @@ int read_bmp_file(const char* filename, 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; +} + +#if (LCD_DEPTH > 1) +#define LIMIT_WIDTH 5000 +#define CACHE_SZ 1 +#else +#define LIMIT_WIDTH LCD_WIDTH +#define CACHE_SZ 1 +#endif + +#if (LCD_WIDTH < LIMIT_WIDTH) +#define MAX_WIDTH (LIMIT_WIDTH * CACHE_SZ) +#else +#define MAX_WIDTH (LCD_WIDTH * CACHE_SZ) +#endif + +static int read_one_line(int fd, struct uint8_rgb *bmpb, int padded_width, + int width, int depth, struct uint8_rgb *palette) +{ + uint32_t data = 0, mask, mask_end; + int ret; + struct uint8_rgb q0, q1; + int i, end; + uint8_t data1; + uint16_t data2; + uint32_t data4; + + switch (depth) { + case 1: + q0 = palette[0]; + q1 = palette[1]; + end = (width + 7) >> 3; + for (i = 0; i < end; i++) { + ret = read(fd, &data1, sizeof(data1)); + if (ret != sizeof(data1)) { + DEBUGF("read_one_line: error reading image, read returned: %d " + "expected: %d\n", ret, sizeof(data1)); + return -9; + } + + if (i != (end - 1)) + mask_end = 0x00; + else + mask_end = 0x80 >> (width & 7); + + for (mask = 0x80; mask != mask_end; mask >>= 1) + *bmpb++ = (data1 & mask) ? q1 : q0; + } + break; + + case 4: + end = (width + 1) >> 1; + for (i = 0; i < end; i++) { + ret = read(fd, &data1, sizeof(data1)); + if (ret != sizeof(data1)) { + DEBUGF("read_one_line: error reading image, read returned: %d " + "expected: %d\n", ret, sizeof(data1)); + return -9; + } + + *bmpb++ = palette[data1 >> 4]; + if (!((i == (end - 1)) && (width & 1))) + *bmpb++ = palette[data1 & 0x0f]; + } + break; + + case 8: + end = width; + for (i = 0; i < end; i++) { + ret = read(fd, &data1, sizeof(data1)); + if (ret != sizeof(data1)) { + DEBUGF("read_one_line: error reading image, read returned: %d " + "expected: %d\n", ret, sizeof(data1)); + return -9; + } + + *bmpb++ = palette[data1]; + } + break; + + case 15: + case 16: + end = width; + for (i = 0; i < end; i++) { + uint32_t component, rgb; + + ret = read(fd, &data2, sizeof(data2)); + if (ret != sizeof(data2)) { + DEBUGF("read_one_line: error reading image, read returned: %d " + "expected: %d\n", ret, sizeof(data2)); + return -9; + } + + data = letoh16(data2); + /* 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); + set_rgb_union(bmpb, (union rgb_union)(rgb << 8)); + bmpb++; +#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; + set_rgb_union(bmpb, (union rgb_union)rgb); + bmpb++; +#endif + } + break; + + case 24: + end = width; + for (i = 0; i < end; i++) { + int j; + for (j = 0; j < 3; j++) { + ret = read(fd, &data1, sizeof(data1)); + if (ret != sizeof(data1)) { + DEBUGF("read_one_line: error reading image, read returned:" + " %d expected: %d\n", ret, sizeof(data1)); + return -9; + } + if (j == 0) + bmpb->blue = data1; + else if (j == 1) + bmpb->green = data1; + else if (j == 2) + bmpb->red = data1; + + } + bmpb++; + } + break; + + case 32: + end = width; + for (i = 0; i < end; i++) { + ret = read(fd, &data4, sizeof(data4)); + if (ret != sizeof(data4)) { + DEBUGF("read_one_line: error reading image, read returned: %d " + "expected: %d\n", ret, sizeof(data4)); + return -9; + } + + set_rgb_union(bmpb, (union rgb_union)(data4)); + bmpb++; + } + break; + } + + int pad = padded_width - ((width * depth + 7) >> 3); + if (pad > 0) + lseek(fd, pad, SEEK_CUR); + + return 0; +} + +struct cache_line { + struct uint8_rgb *yp; + short y; +}; + +static struct uint8_rgb bmpbuf[MAX_WIDTH]; + + +struct bmp_args { + int fd; + short padded_width; + short width; + short depth; + int cur_row; + struct uint8_rgb *palette; +}; + +static struct uint8_rgb *store_line_bmp(int y, void *args) +{ + struct bmp_args *ba = (struct bmp_args *)args; + struct uint8_rgb *qp = NULL; + int i; + + /* skip some needless lines */ + int skip = y - ba->cur_row - 1; + if (skip > 0) { + if(-1 == lseek(ba->fd, ba->padded_width * skip, SEEK_CUR)) + return NULL; + } + + /* read one line form disk, and store to cache */ + if (0 != read_one_line(ba->fd, bmpbuf, ba->padded_width, ba->width, + ba->depth, ba->palette)) + return NULL; + + ba->cur_row = y; + + return bmpbuf; +} + +static inline int recalc_dimension(struct dim *dst, struct dim *src) +{ + int tmp; + 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; +} + +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() * @@ -180,24 +437,24 @@ int read_bmp_file(const char* filename, int read_bmp_fd(int fd, struct bitmap *bm, int maxsize, - int format) + unsigned int format) { struct bmp_header bmph; - int width, height, padded_width; - int dst_height, dst_width; + int padded_width; + struct dim src_dim; int depth, numcolors, compression, totalsize; int row, col, ret; - int rowstart, rowstop, rowstep; unsigned char *bitmap = bm->data; - uint32_t bmpbuf[LCD_WIDTH]; /* Buffer for one line */ - uint32_t palette[256]; + unsigned int resize = IMG_NORESIZE; + struct uint8_rgb palette[256]; + bool remote = false; + bool dither = false; + struct rowset rset; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) bool transparent = false; - bool dither = false; -#ifdef HAVE_REMOTE_LCD - bool remote = false; +#ifdef HAVE_REMOTE_LCD if (format & FORMAT_REMOTE) { remote = true; #if LCD_REMOTE_DEPTH == 1 @@ -207,6 +464,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; @@ -231,27 +494,55 @@ int read_bmp_fd(int fd, return -3; } - width = readlong(&bmph.width); - if (width > LCD_WIDTH) { + src_dim.width = readlong(&bmph.width); + if (src_dim.width > MAX_WIDTH) { DEBUGF("read_bmp_fd: Bitmap too wide (%d pixels, max is %d)\n", - width, LCD_WIDTH); + src_dim.width, MAX_WIDTH); return -4; } - height = readlong(&bmph.height); - if (height < 0) { /* Top-down BMP file */ - height = -height; - rowstart = 0; - rowstop = height; - rowstep = 1; + src_dim.height = readlong(&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 */ + /* 4-byte boundary aligned */ + padded_width = ((src_dim.width * depth + 31) >> 3) & ~3; + + int cache_sz = 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; + } + } +out: + + if (!(resize & IMG_RESIZE)) { + /* returning image size */ + bm->width = src_dim.width; + bm->height = src_dim.height; + } + + 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; + } #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) if (format == FORMAT_ANY) { @@ -262,46 +553,8 @@ int read_bmp_fd(int fd, } 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) */ - { - dst_width = width; - dst_height = (height + 7) >> 3; - totalsize = dst_width * dst_height; - } + totalsize = get_totalsize(bm, remote); /* Check if this fits the buffer */ if (totalsize > maxsize) { @@ -317,13 +570,17 @@ int read_bmp_fd(int fd, 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 +600,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: @@ -368,119 +637,30 @@ int read_bmp_fd(int fd, 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; - } + if (resize & IMG_RESIZE) { + struct bmp_args ba = { + .fd = fd, .padded_width = padded_width, .width = src_dim.width, + .depth = depth, .cur_row = 0, .palette = palette, + }; - /* 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; + return resize_on_load(bm, dither, &src_dim, &rset, remote, + resize, store_line_bmp, &ba); + } - 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); - 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) { + unsigned mask; + unsigned char *p; + struct uint8_rgb *qp; - 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 (0 != read_one_line(fd, bmpbuf, padded_width, src_dim.width, + depth, palette)) + return -9; - case 32: /* already in desired format */ - break; - } - /* Convert to destination format */ - qp = (union rgb_union *)bmpbuf; + qp = 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 @@ -488,17 +668,17 @@ int read_bmp_fd(int fd, #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); + + fb_width * (row >> 3); int shift = row & 7; int delta = 127; unsigned bright; - for (col = 0; col < width; col++) { + for (col = 0; col < src_dim.width; col++) { if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; + delta = dither_mat(row & 0xf, col & 0xf); bright = brightness(*qp++); bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= vi_pattern[bright] << shift; + *dest++ |= vi_pat(bright) << shift; } #endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ } else @@ -507,15 +687,15 @@ int read_bmp_fd(int fd, #if LCD_DEPTH == 2 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING /* greyscale iPods */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; + fb_data *dest = (fb_data *)bitmap + fb_width * row; int shift = 6; int delta = 127; unsigned bright; unsigned data = 0; - for (col = 0; col < width; col++) { + for (col = 0; col < src_dim.width; col++) { if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; + delta = dither_mat(row & 0xf, col & 0xf); bright = brightness(*qp++); bright = (3 * bright + (bright >> 6) + delta) >> 8; data |= (~bright & 3) << shift; @@ -530,42 +710,43 @@ int read_bmp_fd(int fd, *dest++ = data; #elif LCD_PIXELFORMAT == VERTICAL_PACKING /* iriver H1x0 */ - fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 2); + fb_data *dest = (fb_data *)bitmap + fb_width * (row >> 2); int shift = 2 * (row & 3); int delta = 127; unsigned bright; - for (col = 0; col < width; col++) { + for (col = 0; col < src_dim.width; col++) { if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; + delta = dither_mat(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); + fb_data *dest = (fb_data *)bitmap + fb_width * (row >> 3); int shift = row & 7; int delta = 127; unsigned bright; - for (col = 0; col < width; col++) { + for (col = 0; col < src_dim.width; col++) { if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; + delta = dither_mat(row & 0xf, col & 0xf); bright = brightness(*qp++); bright = (3 * bright + (bright >> 6) + delta) >> 8; - *dest++ |= vi_pattern[bright] << shift; + *dest++ |= vi_pat(bright) << shift; } #endif /* LCD_PIXELFORMAT */ #elif LCD_DEPTH == 16 /* iriver h300, colour iPods, X5 */ - fb_data *dest = (fb_data *)bitmap + dst_width * row; + fb_data *dest = (fb_data *)bitmap + fb_width * row; int delta = 127; unsigned r, g, b; + struct uint8_rgb q0; - for (col = 0; col < width; col++) { + for (col = 0; col < src_dim.width; col++) { if (dither) - delta = dither_matrix[row & 0xf][col & 0xf]; + delta = dither_mat(row & 0xf, col & 0xf); q0 = *qp++; r = (31 * q0.red + (q0.red >> 3) + delta) >> 8; g = (63 * q0.green + (q0.green >> 2) + delta) >> 8; @@ -577,10 +758,10 @@ int read_bmp_fd(int fd, } else #endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ { - 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++, p++) if (brightness(*qp++) < 128) *p |= mask; } diff --git a/apps/recorder/bmp.h b/apps/recorder/bmp.h index 3ac73de..961f50f 100644 --- a/apps/recorder/bmp.h +++ b/apps/recorder/bmp.h @@ -23,6 +23,162 @@ #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 +#define IMG_BILINEAR 2 +#define IMG_NEAREST 4 +#define IMG_RESIZE_BILINEAR (IMG_RESIZE | IMG_BILINEAR) +#define IMG_RESIZE_NEAREST (IMG_RESIZE | IMG_NEAREST) + +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 unsigned 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 unsigned 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() @@ -35,10 +191,10 @@ int read_bmp_file(const char* filename, struct bitmap *bm, int maxsize, - int format); + unsigned int format); int read_bmp_fd(int fd, struct bitmap *bm, int maxsize, - int format); + unsigned int format); #endif diff --git a/apps/recorder/resize-color.c b/apps/recorder/resize-color.c new file mode 100644 index 0000000..3682773 --- /dev/null +++ b/apps/recorder/resize-color.c @@ -0,0 +1,432 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +/* + * Code for the scaling algorithm: + * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code + * is by Willem Monsuwe . Additional modifications are by + * (C) Daniel M. Duley. + * + * Port to Rockbox + * Copyright (C) 2007 Jonas Hurrelmann (j@outpo.st) + * + * 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. + * + ****************************************************************************/ + +/* + * Copyright (C) 2004, 2005 Daniel M. Duley + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* OTHER CREDITS: + * + * This is the normal smoothscale method, based on Imlib2's smoothscale. + * + * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow + * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's + * C algorithm and it ran at about the same speed as my MMX optimized one... + * Finally I ported Imlib's MMX version and it ran in less than half the + * time as my MMX algorithm, (taking only a quarter of the time Qt does). + * After further optimization it seems to run at around 1/6th. + * + * Changes include formatting, namespaces and other C++'ings, removal of old + * #ifdef'ed code, and removal of unneeded border calculation code. + * + * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code + * is by Willem Monsuwe . All other modifications are + * (C) Daniel M. Duley. + */ + +#include +#include +#include +#include "inttypes.h" +#include "debug.h" +#include "lcd.h" +#include "file.h" +#ifdef HAVE_REMOTE_LCD +#include "lcd-remote.h" +#endif +#ifndef __PCTOOL__ +#include "config.h" +#include "system.h" +#include "bmp.h" +#include "resize.h" +#include "resize-common.h" +#include "debug.h" +#else +#undef DEBUGF +#define DEBUGF(...) +#endif + +#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) + +struct uint32_rgb { + uint32_t r; + uint32_t g; + uint32_t b; +}; + +struct scaler_context { + uint32_t divmul; + uint32_t shift; +}; + +static struct uint32_rgb crow1[LCD_WIDTH]; +static struct uint32_rgb crow2[LCD_WIDTH]; +static struct uint32_rgb crow3[LCD_WIDTH]; + +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; + ctx->shift = 4; + while (ctx->shift && !(ctx->divmul & 0x8000000U)) + { + ctx->divmul <<= 1; + ctx->shift -= 1; + } +} + +static void scale_h_linear_setup(struct bitmap *bm, struct dim *src, + struct scaler_context *ctx) +{ + (void) src; + ctx->divmul = (bm->width - 1 + 0x80000000U) / bm->width; + ctx->shift = 4; + while (ctx->shift && !(ctx->divmul & 0x8000000U)) + { + ctx->divmul <<= 1; + ctx->shift -= 1; + } +} + +static void scale_h_area(struct bitmap *bm, struct dim *src, + struct uint8_rgb *in_line, + struct uint32_rgb *out_line, + struct scaler_context *ctx, bool accum) +{ + unsigned int ix, ox, oxe, mul; + struct uint32_rgb rgbval1, rgbval2; + 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) + { + 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; + rgbval2.r = in_line[ix].red; + rgbval2.g = in_line[ix].green; + rgbval2.b = in_line[ix].blue; + 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->divmul >> 32) + 1) >> 1); + out_line[ox].g = (accum ? out_line[ox].g : 0) + ((((uint64_t)rgbval1.g * ctx->divmul >> 32) + 1) >> 1); + out_line[ox].b = (accum ? out_line[ox].b : 0) + ((((uint64_t)rgbval1.b * ctx->divmul >> 32) + 1) >> 1); + rgbval1.r = 0; + rgbval1.g = 0; + rgbval1.b = 0; + mul = bm->width - mul; + ox += 1; + } else { + rgbval1.r += in_line[ix].red; + rgbval1.g += in_line[ix].green; + rgbval1.b += in_line[ix].blue; + } + } +} + +static void scale_h_linear(struct bitmap *bm, struct dim *src, + struct uint8_rgb *in_line, + struct uint32_rgb *out_line, + struct scaler_context *ctx, bool accum) +{ + unsigned int ix, ox, ixe; + struct uint32_rgb rgbval, rgbinc; + ix = 0; + ixe = bm->width - 1; + for (ox = 0; ox < (uint32_t)bm->width; ox++) { + if (ixe >= ((uint32_t)bm->width - 1)) + { + ixe -= (bm->width - 1); + rgbinc.r = -in_line[ix].red; + rgbinc.g = -in_line[ix].green; + rgbinc.b = -in_line[ix].blue; + rgbval.r = in_line[ix].red * bm->width; + rgbval.g = in_line[ix].green * bm->width; + rgbval.b = in_line[ix].blue * bm->width; + ix += 1; + if (ix < (uint32_t)src->width) { + rgbinc.r += in_line[ix].red; + rgbinc.g += in_line[ix].green; + rgbinc.b += in_line[ix].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->divmul >> 32) + 1) >> 1); + out_line[ox].g = (accum ? out_line[ox].g : 0) + ((((uint64_t)rgbval.g * ctx->divmul >> 32) + 1) >> 1); + out_line[ox].b = (accum ? out_line[ox].b : 0) + ((((uint64_t)rgbval.b * ctx->divmul >> 32) + 1) >> 1); + rgbval.r += rgbinc.r; + rgbval.g += rgbinc.g; + rgbval.b += rgbinc.b; + ixe += src->width - 1; + } +} + +static bool scale_v_area(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + void (*h_scaler)(struct bitmap*, struct dim*, + struct uint8_rgb*, + struct uint32_rgb*, + struct scaler_context*, bool), + struct scaler_context *ctx, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + uint32_t mul, divmul, x, oy, iy, oye; + struct uint8_rgb *in_line; + int delta = 127, r, g, b; + fb_data *row, *pix; + divmul = (src->height - 1 + 0x80000000U) / src->height; + mul = 0; + oy = 0; + oye = 0; + memset((void *)crow1, 0, bm->width * 12); + memset((void *)crow2, 0, bm->width * 12); + memset((void *)crow3, 0, bm->width * 12); + 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; + in_line = (*store_line)(iy, args); + if (in_line == NULL) + goto fail; + 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]; + h_scaler(bm, src, in_line, crow2, ctx, false); + 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)(((uint32_t*)crow1)[x]) * divmul >> 32 >> ctx->shift) + 1) >> 1); + } + 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 * 12); + mul = oye; + row += bm->width * rset->rowstep; + oy += 1; + } else { + h_scaler(bm, src, in_line, crow1, ctx, true); + } + } + return true; + fail: + return false; +} + +static bool scale_v_linear(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + void (*h_scaler)(struct bitmap*, struct dim*, + struct uint8_rgb*, + struct uint32_rgb*, + struct scaler_context*, bool), + struct scaler_context *ctx, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + uint32_t mul, divmul, x, oy, iy, iye; + struct uint8_rgb *in_line; + int delta = 127, r, g, b; + fb_data *row, *pix; + divmul = (bm->height - 2 + 0x80000000U) / (bm->height - 1); + mul = 0; + iy = 0; + iye = bm->height - 1; +// memset((void *)crow1, 0, bm->width * 12); +// memset((void *)crow2, 0, bm->width * 12); + memset((void *)crow3, 0, bm->width * 12); + row = (fb_data *)(bm->data) + bm->width * (rset->rowstep == -1 ? bm->height - 1 : 0); + in_line = (*store_line)(iy, args); + if (in_line == NULL) + goto fail; + h_scaler(bm, src, in_line, crow3, ctx, false); + for (oy = 0; oy < (uint32_t)bm->height; oy++) + { + if (iye >= (uint32_t)bm->height - 1) + { + iye -= bm->height - 1; + for (x = 0; x < 3 * (uint32_t) bm->width; x++) + { + ((uint32_t*)crow2)[x] = -((uint32_t*)crow3)[x]; + ((uint32_t*)crow1)[x] = ((uint32_t*)crow3)[x] * bm->height; + } + iy += 1; + if (iy < (uint32_t)src->height) + { + in_line = (*store_line)(iy, args); + if (in_line == NULL) + goto fail; + h_scaler(bm, src, in_line, crow3, ctx, false); + for (x = 0; x < 3 * (uint32_t) bm->width; x++) + { + ((uint32_t*)crow2)[x] += ((uint32_t*)crow3)[x]; + ((uint32_t*)crow1)[x] += ((uint32_t*)crow2)[x] * iye; + ((uint32_t*)crow2)[x] *= src->height - 1; + } + } + } + pix = row; + for (x = 0; x < (uint32_t)bm->width; x++) + { + r = ((((uint64_t)crow1[x].r * divmul >> 32 >> ctx->shift) + 1) >> 1); + g = ((((uint64_t)crow1[x].g * divmul >> 32 >> ctx->shift) + 1) >> 1); + b = ((((uint64_t)crow1[x].b * divmul >> 32 >> ctx->shift) + 1) >> 1); + if (dither) + delta = dither_mat(x & 0xf, oy & 0xf); + r = PACKRED(r,delta); + g = PACKGREEN(g,delta); + b = PACKBLUE(b,delta); + crow1[x].r += crow2[x].r; + crow1[x].g += crow2[x].g; + crow1[x].b += crow2[x].b; + *pix++ = LCD_RGBPACK_LCD(r, g, b); + } + row += bm->width * rset->rowstep; + iye += src->height - 1; + } + return true; + fail: + return false; +} + +int resize_on_load(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, bool remote, unsigned int resize, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + void (*h_scaler)(struct bitmap*, struct dim*, + struct uint8_rgb*, + struct uint32_rgb*, + struct scaler_context*, bool); + struct scaler_context ctx; + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + int format = bm->format; + (void)resize; + + 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) + /* resize with "Nearest Neighbour" */ + if (!nearest_remote(bm, dither, src, rset, store_line, args)) + goto out; +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ + { + if (sw > dw) + { + h_scaler = scale_h_area; + scale_h_area_setup(bm, src, &ctx); + } else { + h_scaler = scale_h_linear; + scale_h_linear_setup(bm, src, &ctx); + } + if (sh > dh) { + if (!scale_v_area(bm, dither, src, rset, h_scaler, &ctx, store_line, args)) + goto out; + } else { + if (!scale_v_linear(bm, dither, src, rset, h_scaler, &ctx, store_line, args)) + goto out; + } + } + } else { + /* resize with "Nearest Neighbour" */ + if (!nearest_mono(bm, src, rset, store_line, args)) + goto out; + } + return get_totalsize(bm, remote); + +out: + return -1; +} diff --git a/apps/recorder/resize-common.c b/apps/recorder/resize-common.c new file mode 100644 index 0000000..a078b6a --- /dev/null +++ b/apps/recorder/resize-common.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include "inttypes.h" +#include "debug.h" +#include "lcd.h" +#include "file.h" +#ifdef HAVE_REMOTE_LCD +#include "lcd-remote.h" +#endif +#ifndef __PCTOOL__ +#include "config.h" +#include "system.h" +#include "bmp.h" +#include "resize.h" +#include "debug.h" +#else +#undef DEBUGF +#define DEBUGF(...) +#endif + +/* resize with "Nearest Neighbour" */ +bool nearest_mono(struct bitmap *bm, struct dim *src, struct rowset *rset, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + const int inc_x = (sw << 16) / dw; + const int inc_y = (sh << 16) / dh; + unsigned char *bitmap = bm->data; + int val_y = 0, val_x; + int x, y, xpoint, ypoint; + const int rowstep = rset->rowstep; + const int rowstart = rset->rowstart; + const int rowstop = rset->rowstop; + + for (y = rowstart; y != rowstop; y += rowstep) { + unsigned char *p = bitmap + get_fb_width(bm, false) * (y >> 3); + unsigned mask = 1 << (y & 7); + struct uint8_rgb *qp; + + ypoint = (val_y >> 8); + if ((qp = (*store_line)(ypoint, args)) == NULL) + return false; + + for (val_x = 0, x = 0; x < dw; x++, p++) { + struct uint8_rgb *pix; + xpoint = (val_x >> 8); + pix = qp + xpoint; + if (brightness(*pix) < 128) + *p |= mask; + val_x += inc_x; + } + val_y += inc_y; + } + + return true; +} + +#if defined(HAVE_REMOTE_LCD) && \ + (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) +/* resize with "Nearest Neighbour" */ +bool nearest_remote(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + const int inc_x = (sw << 16) / dw; + const int inc_y = (sh << 16) / dh; + unsigned char *bitmap = bm->data; + int val_y = 0, val_x; + int x, y, xpoint, ypoint; + const int rowstep = rset->rowstep; + const int rowstart = rset->rowstart; + const int rowstop = rset->rowstop; + + for (y = rowstart; y != rowstop; y += rowstep) { + /* iAudio X5/M5 remote */ + fb_remote_data *dest = (fb_remote_data *)bitmap + + get_fb_width(bm, true) * (y >> 3); + int shift = y & 7; + int delta = 127; + unsigned bright; + struct uint8_rgb *qp; + + ypoint = (val_y >> 8); + if ((qp = (*store_line)(ypoint, args)) == NULL) + return false; + + for (val_x = 0, x = 0; x < dw; x++) { + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 8); + pix = qp + xpoint; + bright = brightness(*pix); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pat(bright) << shift; + val_x += inc_x; + } + val_y += inc_y; + } + + return true; +} +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ diff --git a/apps/recorder/resize-common.h b/apps/recorder/resize-common.h new file mode 100644 index 0000000..e1e779c --- /dev/null +++ b/apps/recorder/resize-common.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * __________ __ ___. + * 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_COMMON_H_ +#define _RESIZE_COMMON_H_ + +#include "config.h" +#include "lcd.h" + +bool nearest_mono(struct bitmap *bm, struct dim *src, struct rowset *rset, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args); +#if defined(HAVE_REMOTE_LCD) && \ + (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) +bool nearest_remote(struct bitmap *bm, bool dither, struct dim *src, + struct rowset *rset, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args); +#endif + +#endif /* _RESIZE_COMMON_H_ */ diff --git a/apps/recorder/resize-gray.c b/apps/recorder/resize-gray.c new file mode 100644 index 0000000..4be81ed --- /dev/null +++ b/apps/recorder/resize-gray.c @@ -0,0 +1,202 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#include +#include +#include +#include "inttypes.h" +#include "debug.h" +#include "lcd.h" +#include "file.h" +#ifdef HAVE_REMOTE_LCD +#include "lcd-remote.h" +#endif +#ifndef __PCTOOL__ +#include "config.h" +#include "system.h" +#include "bmp.h" +#include "resize.h" +#include "resize-common.h" +#include "debug.h" +#else +#undef DEBUGF +#define DEBUGF(...) +#endif + +#if (LCD_DEPTH == 2) +/* + * This algorithm is taken from apps/plugins/lib/bmp.c simple_resize_bitmap(). + * And that algorithm is "Nearest Neighbour". + */ +static inline bool nearest_2bpp(struct bitmap *bm, bool dither, + struct dim *src, + struct rowset *rset, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + int val_y = 0, val_x; + const int inc_x = ((sw - 1) << 8) / (dw - 1); + const int inc_y = ((sh - 1) << 8) / (dh - 1); + unsigned char *bitmap = bm->data; + int x, y, xpoint, ypoint; + const int rowstep = rset->rowstep; + const int rowstart = rset->rowstart; + const int rowstop = rset->rowstop; + const int fb_width = get_fb_width(bm, false); + + for (y = rowstart; y != rowstop; y += rowstep) { +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + /* greyscale iPods */ + fb_data *dest = (fb_data *)bitmap + fb_width * y; + int shift = 6; + int delta = 127; + unsigned bright; + unsigned data = 0; + struct uint8_rgb *qp; + + ypoint = (val_y >> 8); + if ((qp = (*store_line)(ypoint, args)) == NULL) + return false; + + for (val_x = 0, x = 0; x < dw; x++) { + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 8); + pix = qp + xpoint; + bright = brightness(*pix); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + data |= (~bright & 3) << shift; + shift -= 2; + if (shift < 0) { + *dest++ = data; + data = 0; + shift = 6; + } + val_x += inc_x; + } + val_y += inc_y; + if (shift < 6) + *dest++ = data; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + /* iriver H1x0 */ + fb_data *dest = (fb_data *)bitmap + fb_width * (y >> 2); + int shift = 2 * (y & 3); + int delta = 127; + unsigned bright; + struct uint8_rgb *qp; + + ypoint = (val_y >> 8); + if ((qp = (*store_line)(ypoint, args)) == NULL) + return false; + + for (val_x = 0, x = 0; x < dw; x++) { + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 8); + pix = qp + xpoint; + bright = brightness(*pix); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= (~bright & 3) << shift; + val_x += inc_x; + } + val_y += inc_y; +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + /* iAudio M3 */ + fb_data *dest = (fb_data *)bitmap + fb_width * (y >> 3); + int shift = y & 7; + int delta = 127; + unsigned bright; + struct uint8_rgb *qp; + + ypoint = (val_y >> 8); + if ((qp = (*store_line)(ypoint, args)) == NULL) + return false; + + for (val_x = 0, x = 0; x < dw; x++) { + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 8); + pix = qp + xpoint; + bright = brightness(*pix); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pat(bright) << shift; + val_x += inc_x; + } + val_y += inc_y; +#endif /* LCD_PIXELFORMAT */ + } + + return true; +} +#endif + +int resize_on_load(struct bitmap *bm, bool dither, + struct dim *src, + struct rowset *rset, bool remote, unsigned int resize, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args) +{ +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + int format = bm->format; +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + (void) resize; + +#if (LCD_DEPTH == 1) + (void) dither; +#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) + /* resize with "Nearest Neighbour" */ + if (!nearest_remote(bm, dither, src, rset, store_line, args)) + goto out; +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ + { +#if LCD_DEPTH == 2 + /* resize with "Nearest Neighbour" */ + if (!nearest_2bpp(bm, dither, src, rset, store_line, args)) + goto out; +#endif /* LCD_DEPTH == 2 */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + /* resize with "Nearest Neighbour" */ + if (!nearest_mono(bm, src, rset, store_line, args)) + goto out; + } + return get_totalsize(bm, remote); + +out: + return -1; +} diff --git a/apps/recorder/resize.h b/apps/recorder/resize.h new file mode 100644 index 0000000..9360b50 --- /dev/null +++ b/apps/recorder/resize.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * __________ __ ___. + * 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 + ****************************************************************/ + +int resize_on_load(struct bitmap *bm, bool dither, + struct dim *src, + struct rowset *tmp_row, bool remote, unsigned int resize, + struct uint8_rgb* (*store_line)(int y, void *args), + void *args); +#endif /* _RESIZE_H_ */ diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index 6032914..ea5851e 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)