diff --git a/apps/SOURCES b/apps/SOURCES index 918fa5d..820c930 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/recorder/bmp.c b/apps/recorder/bmp.c index b74397b..5a13ca1 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 @@ -186,6 +187,20 @@ static inline void set_rgb_union(struct uint8_rgb *dst, union rgb_union src) dst->blue = src.blue; } +#if (LCD_DEPTH > 1) +#define LIMIT_WIDTH 500 +#define CACHE_SZ 5 +#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) { @@ -354,6 +369,129 @@ static int read_one_line(int fd, struct uint8_rgb *bmpb, int padded_width, return 0; } +struct cache_line { + struct uint8_rgb *yp; + short y; +}; + +static struct uint8_rgb bmpbuf[MAX_WIDTH]; + +static struct cache_line cache[CACHE_SZ]; +static short cache_index = 0; +static short cache_srcy = -1; + +struct bmp_args { + int fd; + short padded_width; + short width; + short depth; + short cache_sz; + 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; + + for (i = 0; i < ba->cache_sz; i++) { + if (y == cache[i].y) { + qp = cache[i].yp; + + cache_index = i + 1; + if (cache_index == ba->cache_sz) + cache_index = 0; + + return qp; /* cache found */ + } + } + + /* cache missed, so read some lines from disk.. */ + + /* skip some needless lines */ + int skip = y - cache_srcy - 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 */ + qp = cache[cache_index].yp = bmpbuf + ((ba->width) * cache_index); + if (0 != read_one_line(ba->fd, qp, ba->padded_width, ba->width, + ba->depth, ba->palette)) + return NULL; + + cache[cache_index].y = y; + cache_srcy = y; + + cache_index++; + if (cache_index == ba->cache_sz) + cache_index = 0; + + return qp; +} + +static void init_cache(void) +{ + int i; + cache_index = 0; + cache_srcy = -1; + for (i = 0; i < ARRAY_SIZE(cache); i++) { + cache[i].y = -1; + cache[i].yp = NULL; + } +} + +#define SCALE 13 +#define FACT_SCALE (1 << SCALE) + +static inline int recalc_dimension(struct dim *dst, struct dim *src) +{ + int dst_width = dst->width; + int dst_height = dst->height; + const int src_width = src->width; + const int src_height = src->height; + int ret = 0; + + if (dst_width < 0 || dst_height < 0 || src_width <= 0 || src_height <= 0) { + dst->width = src_width; + dst->height = src_height; + ret = 0; + goto out; + } + + if (dst_width == 0) + dst_width = src_width; + if (dst_height == 0) + dst_height = src_height; + + if ((dst_width == src_width) && (dst_height == src_height)) { + dst->width = src_width; + dst->height = src_height; + ret = 0; + } else { + /* recalc bmpsize to keep aspect ratio */ + const unsigned int fact_w = (dst_width << SCALE) / src_width; + const unsigned int fact_h = (dst_height << SCALE) / src_height; + const unsigned int fact = MIN(fact_w, fact_h); + + /* "1 << (SCALE -1)" is "round off" */ + dst->width = (src_width * fact + (1 << (SCALE - 1))) >> SCALE; + dst->height = (src_height * fact + (1 << (SCALE -1))) >> SCALE; + + if (fact == FACT_SCALE) /* no resize */ + ret = 0; + else if (fact < FACT_SCALE) /* Downscaling */ + ret = (FACT_SCALE + (fact - 1)) / fact; + else /* Upscaling */ + ret = - ((fact + (FACT_SCALE - 1))/ FACT_SCALE); + } + +out: + return ret; +} + static inline int rgbcmp(struct uint8_rgb rgb1, struct uint8_rgb rgb2) { if ((rgb1.red == rgb2.red) && (rgb1.green == rgb2.green) && @@ -382,13 +520,13 @@ int read_bmp_fd(int fd, int row, col, ret; unsigned char *bitmap = bm->data; - struct uint8_rgb bmpbuf[LCD_WIDTH]; /* Buffer for one line */ + 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 if (format & FORMAT_REMOTE) { @@ -400,6 +538,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; @@ -425,21 +569,17 @@ int read_bmp_fd(int fd, } src_dim.width = readlong(&bmph.width); - if (src_dim.width > LCD_WIDTH) { + if (src_dim.width > MAX_WIDTH) { DEBUGF("read_bmp_fd: Bitmap too wide (%d pixels, max is %d)\n", - src_dim.width, LCD_WIDTH); + src_dim.width, MAX_WIDTH); return -4; } src_dim.height = readlong(&bmph.height); if (src_dim.height < 0) { /* Top-down BMP file */ src_dim.height = -src_dim.height; - rset.rowstart = 0; - rset.rowstop = src_dim.height; rset.rowstep = 1; } else { /* normal BMP */ - rset.rowstart = src_dim.height - 1; - rset.rowstop = -1; rset.rowstep = -1; } @@ -447,6 +587,75 @@ int read_bmp_fd(int fd, /* 4-byte boundary aligned */ padded_width = ((src_dim.width * depth + 31) >> 3) & ~3; + int fact_w = 0; + + 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 ((fact_w = recalc_dimension(&resize_dim, &src_dim)) == 0) + resize = IMG_NORESIZE; + bm->width = resize_dim.width; + bm->height = resize_dim.height; + } else { + if ((bm->width == src_dim.width) && (bm->height == src_dim.height)) + resize = IMG_NORESIZE; + else { + fact_w = (bm->width << SCALE) / src_dim.width; + if (fact_w == FACT_SCALE) /* no resize */ + fact_w = 0; + else if (fact_w < FACT_SCALE) /* Downscaling */ + fact_w = (FACT_SCALE + (fact_w - 1)) / fact_w; + else /* Upscaling */ + fact_w = - ((fact_w + (FACT_SCALE - 1))/ FACT_SCALE); + } + } + } + + if (!(resize & IMG_RESIZE)) { + /* returning image size */ + bm->width = src_dim.width; + bm->height = src_dim.height; + } + + int cache_sz = 1; + +#ifdef HAVE_LCD_COLOR + if (resize & IMG_RESIZE) { + if (src_dim.width * 2 > MAX_WIDTH) { + DEBUGF("Warning: This bitmap is too large to downscale!\n"); + DEBUGF("use: nearest neighbour\n"); + resize = IMG_RESIZE_NEAREST; + cache_sz = 1; + } else if (fact_w > 0) { + /* downscaling */ + if (src_dim.width * fact_w > MAX_WIDTH) { + DEBUGF("Warning: This bitmap is too large to downscale!\n"); + DEBUGF("use: bilinear\n"); + resize = IMG_RESIZE_BILINEAR; + cache_sz = 2; + } else { + cache_sz = fact_w; + } + } else { + /* upscaling */ + cache_sz = 2; + } + } +#endif /* HAVE_LCD_COLOR */ + + 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) { if (depth == 1) @@ -456,9 +665,6 @@ 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 = src_dim.width; - bm->height = src_dim.height; totalsize = get_totalsize(bm, remote); @@ -476,7 +682,7 @@ int read_bmp_fd(int fd, numcolors = 1 << depth; } else numcolors = (compression == 3) ? 3 : 0; - + if (numcolors > 0 && numcolors <= 256) { int i; union rgb_union pal; @@ -542,6 +748,19 @@ int read_bmp_fd(int fd, lseek(fd, (off_t)readlong(&bmph.off_bits), SEEK_SET); memset(bitmap, 0, totalsize); + + if (resize & IMG_RESIZE) { + struct bmp_args ba = { + .fd = fd, .padded_width = padded_width, .width = src_dim.width, + .depth = depth, .cache_sz = cache_sz, .palette = palette, + }; + + init_cache(); + + return resize_on_load(bm, dither, &src_dim, &rset, remote, + resize, store_line_bmp, &ba); + } + int fb_width = get_fb_width(bm, remote); /* loop to read rows and put them to buffer */ diff --git a/apps/recorder/bmp.c.orig b/apps/recorder/bmp.c.orig new file mode 100644 index 0000000..b74397b --- /dev/null +++ b/apps/recorder/bmp.c.orig @@ -0,0 +1,666 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * 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. + * + ****************************************************************************/ + +/* +2005-04-16 Tomas Salfischberger: + - New BMP loader function, based on the old one (borrowed a lot of + calculations and checks there.) + - Conversion part needs some optimization, doing unneeded calulations now. +2006-11-18 Jens Arnold: complete rework + - All canonical formats supported now (1, 4, 8, 15/16, 24 and 32 bit) + - better protection against malformed / non-standard BMPs + - code heavily optimised for both size and speed + - dithering for 2 bit targets +*/ + +#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 "debug.h" +#else +#undef DEBUGF +#define DEBUGF(...) +#endif + +#ifdef __GNUC__ +#define STRUCT_PACKED __attribute__((packed)) +#else +#define STRUCT_PACKED +#pragma pack (push, 2) +#endif + +/* BMP header structure */ +struct bmp_header { + uint16_t type; /* signature - 'BM' */ + uint32_t size; /* file size in bytes */ + uint16_t reserved1; /* 0 */ + uint16_t reserved2; /* 0 */ + uint32_t off_bits; /* offset to bitmap */ + uint32_t struct_size; /* size of this struct (40) */ + int32_t width; /* bmap width in pixels */ + int32_t height; /* bmap height in pixels */ + uint16_t planes; /* num planes - always 1 */ + uint16_t bit_count; /* bits per pixel */ + uint32_t compression; /* compression flag */ + uint32_t size_image; /* image size in bytes */ + int32_t x_pels_per_meter; /* horz resolution */ + int32_t y_pels_per_meter; /* vert resolution */ + uint32_t clr_used; /* 0 -> color table size */ + uint32_t clr_important; /* important color count */ +} STRUCT_PACKED; + +union rgb_union { + struct { /* Little endian */ + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char reserved; + }; + uint32_t raw; +}; + +/* 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 */ +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 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; +#endif + +#if ((LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED)) \ + || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH == 2) \ + && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)) +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); +} + +/****************************************************************************** + * read_bmp_file() + * + * Reads a BMP file and puts the data in rockbox format in *bitmap. + * + *****************************************************************************/ +int read_bmp_file(const char* filename, + struct bitmap *bm, + int maxsize, + unsigned int format) +{ + int fd, ret; + fd = open(filename, O_RDONLY); + + /* Exit if file opening failed */ + if (fd < 0) { + DEBUGF("read_bmp_file: can't open '%s', rc: %d\n", filename, fd); + return fd * 10 - 1; + } + + 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; +} + +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; + } + + if (depth == 15) + depth = 16; + int pad = padded_width - ((width * depth + 7) >> 3); + if (pad > 0) + lseek(fd, pad, SEEK_CUR); + + return 0; +} + +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() + * + * Reads a BMP file in an open file descriptor and puts the data in rockbox + * format in *bitmap. + * + *****************************************************************************/ +int read_bmp_fd(int fd, + struct bitmap *bm, + int maxsize, + unsigned int format) +{ + struct bmp_header bmph; + int padded_width; + struct dim src_dim; + int depth, numcolors, compression, totalsize; + int row, col, ret; + + unsigned char *bitmap = bm->data; + struct uint8_rgb bmpbuf[LCD_WIDTH]; /* Buffer for one line */ + struct uint8_rgb palette[256]; + bool remote = 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 + if (format & FORMAT_REMOTE) { + remote = true; +#if LCD_REMOTE_DEPTH == 1 + format = FORMAT_MONO; +#else + format &= ~FORMAT_REMOTE; +#endif + } +#endif /* HAVE_REMOTE_LCD */ + if (format & FORMAT_TRANSPARENT) { + transparent = true; + format &= ~FORMAT_TRANSPARENT; + } + if (format & FORMAT_DITHER) { + dither = true; + format &= ~FORMAT_DITHER; + } +#else + + (void)format; +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + + /* read fileheader */ + ret = read(fd, &bmph, sizeof(struct bmp_header)); + if (ret < 0) { + return ret * 10 - 2; + } + + if (ret != sizeof(struct bmp_header)) { + DEBUGF("read_bmp_fd: can't read BMP header."); + return -3; + } + + src_dim.width = readlong(&bmph.width); + if (src_dim.width > LCD_WIDTH) { + DEBUGF("read_bmp_fd: Bitmap too wide (%d pixels, max is %d)\n", + src_dim.width, LCD_WIDTH); + return -4; + } + + src_dim.height = readlong(&bmph.height); + if (src_dim.height < 0) { /* Top-down BMP file */ + src_dim.height = -src_dim.height; + rset.rowstart = 0; + rset.rowstop = src_dim.height; + rset.rowstep = 1; + } else { /* normal BMP */ + rset.rowstart = src_dim.height - 1; + rset.rowstop = -1; + rset.rowstep = -1; + } + + depth = readshort(&bmph.bit_count); + /* 4-byte boundary aligned */ + padded_width = ((src_dim.width * depth + 31) >> 3) & ~3; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + if (format == FORMAT_ANY) { + if (depth == 1) + format = FORMAT_MONO; + else + format = FORMAT_NATIVE; + } + bm->format = format; +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + /* returning image size */ + bm->width = src_dim.width; + bm->height = src_dim.height; + + totalsize = get_totalsize(bm, remote); + + /* Check if this fits the buffer */ + if (totalsize > maxsize) { + DEBUGF("read_bmp_fd: Bitmap too large for buffer: " + "%d bytes.\n", totalsize); + return -6; + } + + compression = readlong(&bmph.compression); + if (depth <= 8) { + numcolors = readlong(&bmph.clr_used); + if (numcolors == 0) + numcolors = 1 << depth; + } else + numcolors = (compression == 3) ? 3 : 0; + + if (numcolors > 0 && numcolors <= 256) { + 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); + } + } + + switch (depth) { + case 16: +#if LCD_DEPTH >= 16 + /* don't dither 16 bit BMP to LCD with same or larger depth */ +#ifdef HAVE_REMOTE_LCD + if (!remote) +#endif + dither = false; +#endif + if (compression == 0) { /* BI_RGB, i.e. 15 bit */ + depth = 15; + break; + } /* else fall through */ + + case 32: + if (compression == 3) { /* BI_BITFIELDS */ + 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 (found) + break; + } /* else fall through */ + + default: + if (compression != 0) { /* not BI_RGB */ + DEBUGF("read_bmp_fd: Unsupported compression (type %d)\n", + compression); + return -8; + } + break; + } + + /* Search to the beginning of the image data */ + lseek(fd, (off_t)readlong(&bmph.off_bits), SEEK_SET); + + memset(bitmap, 0, totalsize); + int fb_width = get_fb_width(bm, remote); + + /* 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; + + if (0 != read_one_line(fd, bmpbuf, padded_width, src_dim.width, + depth, palette)) + return -9; + + /* Convert to destination format */ + 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 + if (remote) { +#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + /* iAudio X5/M5 remote */ + fb_remote_data *dest = (fb_remote_data *)bitmap + + fb_width * (row >> 3); + int shift = row & 7; + int delta = 127; + unsigned bright; + + for (col = 0; col < src_dim.width; col++) { + if (dither) + delta = dither_mat(row & 0xf, col & 0xf); + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pat(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 + fb_width * row; + int shift = 6; + int delta = 127; + unsigned bright; + unsigned data = 0; + + for (col = 0; col < src_dim.width; col++) { + if (dither) + delta = dither_mat(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 + fb_width * (row >> 2); + int shift = 2 * (row & 3); + int delta = 127; + unsigned bright; + + for (col = 0; col < src_dim.width; col++) { + if (dither) + 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 + fb_width * (row >> 3); + int shift = row & 7; + int delta = 127; + unsigned bright; + + for (col = 0; col < src_dim.width; col++) { + if (dither) + delta = dither_mat(row & 0xf, col & 0xf); + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pat(bright) << shift; + } +#endif /* LCD_PIXELFORMAT */ +#elif LCD_DEPTH == 16 + /* 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); + 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); + } +#endif /* LCD_DEPTH */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + p = bitmap + fb_width * (row >> 3); + mask = 1 << (row & 7); + + for (col = 0; col < src_dim.width; col++, p++) + if (brightness(*qp++) < 128) + *p |= mask; + } + } + + return totalsize; /* return the used buffer size. */ +} diff --git a/apps/recorder/bmp.h b/apps/recorder/bmp.h index 73ed4cb..961f50f 100644 --- a/apps/recorder/bmp.h +++ b/apps/recorder/bmp.h @@ -30,6 +30,13 @@ #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; diff --git a/apps/recorder/resize-color.c b/apps/recorder/resize-color.c new file mode 100644 index 0000000..a3b6922 --- /dev/null +++ b/apps/recorder/resize-color.c @@ -0,0 +1,510 @@ +/*************************************************************************** + * __________ __ ___. + * 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, delata) (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 int32_rgb { + int32_t r; + int32_t g; + int32_t b; +}; + +/* + * "BILINEAR" algorithm is taken from app/plugins/lib/bmp_smooth_scale.c + * smooth_resize_bitmap() (xup_yup == 3). + * And that is take from Imlib2 + */ +static inline bool bilinear(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 XAP, YAP, INV_YAP, INV_XAP; + 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) { + struct uint8_rgb *qp, *qp2; + int delta = 127; + + ypoint = (val_y >> 16); + fb_data *dest = (fb_data *)bitmap + y * dw; + + if ((qp = (*store_line)(ypoint, args)) == NULL) + goto out; + + YAP = ((val_y >> 16) >= (sh - 1)) ? 0 : + (val_y >> 8) - ((val_y >> 8) & 0xffffff00); + INV_YAP = 256 - YAP; + val_y += inc_y; + val_x = 0; + if (YAP > 0) { + for (x = 0; x < dw; x++) { + int r = 0, g = 0, b = 0; + int rr = 0, gg = 0, bb = 0; + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 16); + XAP = ((val_x >> 16) >= (sw - 1)) ? 0 : + (val_x >> 8) - ((val_x >> 8) & 0xffffff00); + INV_XAP = 256 - XAP; + val_x += inc_x; + + if (XAP > 0) { + pix = qp + xpoint; + r = pix->red * INV_XAP; + g = pix->green * INV_XAP; + b = pix->blue * INV_XAP; + pix++; + r += pix->red * XAP; + g += pix->green * XAP; + b += pix->blue * XAP; + + if ((qp2 = (*store_line)(ypoint + 1, args)) == NULL) + goto out; + + + pix = qp2 + xpoint; + rr = pix->red * INV_XAP; + gg = pix->green * INV_XAP; + bb = pix->blue * INV_XAP; + pix++; + rr += pix->red * XAP; + gg += pix->green * XAP; + bb += pix->blue * XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + r = PACKRED(r, delta); + g = PACKGREEN(g, delta); + b = PACKBLUE(b, delta); + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } else { + pix = qp + xpoint; + r = pix->red * INV_YAP; + g = pix->green * INV_YAP; + b = pix->blue * INV_YAP; + + if ((qp2 = (*store_line)(ypoint + 1, args)) == NULL) + goto out; + + pix = qp2 + xpoint; + r += pix->red * YAP; + g += pix->green * YAP; + b += pix->blue * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + r = PACKRED(r, delta); + g = PACKGREEN(g, delta); + b = PACKBLUE(b, delta); + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } + } + } else { + for (x = 0; x < dw; x++) { + int r = 0, g = 0, b = 0; + struct uint8_rgb *pix; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 16); + XAP = ((val_x >> 16) >= (sw - 1)) ? 0 : + (val_x >> 8) - ((val_x >> 8) & 0xffffff00); + INV_XAP = 256 - XAP; + val_x += inc_x; + + if (XAP > 0) { + pix = qp + xpoint; + r = pix->red * INV_XAP; + g = pix->green * INV_XAP; + b = pix->blue * INV_XAP; + pix++; + r += pix->red * XAP; + g += pix->green * XAP; + b += pix->blue * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + r = PACKRED(r, delta); + g = PACKGREEN(g, delta); + b = PACKBLUE(b, delta); + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } else { + pix = qp + xpoint; + r = pix->red; + g = pix->green; + b = pix->blue; + r = PACKRED(r, delta); + g = PACKGREEN(g, delta); + b = PACKBLUE(b, delta); + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } + } + } + } + return true; + +out: + return false; +} + +/* + * "AREA SAMPLING" algorithm is taken from app/plugins/lib/bmp_smooth_scale.c + * smooth_resize_bitmap() (xup_yup == 0). + * And that is take from Imlib2 + */ +static void area_sample_part(struct uint8_rgb *pix, int xap, + int Cx, int Cy, struct int32_rgb *rgb) +{ + int i; + int rx = (pix->red * xap) >> 9; + int gx = (pix->green * xap) >> 9; + int bx = (pix->blue * xap) >> 9; + pix++; + for (i = (1 << 14) - xap; i > Cx; i -= Cx) { + rx += (pix->red * Cx) >> 9; + gx += (pix->green * Cx) >> 9; + bx += (pix->blue * Cx) >> 9; + pix++; + } + if (i > 0) { + rx += (pix->red * i) >> 9; + gx += (pix->green * i) >> 9; + bx += (pix->blue * i) >> 9; + } + + rgb->r = (rx * Cy) >> 14; + rgb->g = (gx * Cy) >> 14; + rgb->b = (bx * Cy) >> 14; +} + +static inline bool area_sample(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; + const int Cp_x = ((dw << 14) / sw) + 1; + const int Cp_y = ((dh << 14) / sh) + 1; + unsigned char *bitmap = bm->data; + int XAP, YAP, INV_YAP, INV_XAP; + 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) { + int delta = 127; + struct uint8_rgb *qp; + int Cx, Cy, j; + struct uint8_rgb *pix; + int r, g, b; + int xap, yap; + fb_data *dest = (fb_data *)bitmap + y * dw; + + if ((qp = (*store_line)(val_y >> 16, args)) == NULL) + goto out; + + YAP = (((0x100 - ((val_y >> 8) & 0xff)) * Cp_y) >> 8) | + (Cp_y << 16); + INV_YAP = 256 - YAP; + val_y += inc_y; + val_x = 0; + + Cy = YAP >> 16; + yap = YAP & 0xffff; + + for (x = 0; x < dw; x++) { + struct int32_rgb rgbset; + ypoint = ((val_y - inc_y) >> 16); + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 16); + XAP = (((0x100 - ((val_x >> 8) & 0xff)) * Cp_x) >> 8) | + (Cp_x << 16); + INV_XAP = 256 - XAP; + val_x += inc_x; + + Cx = XAP >> 16; + xap = XAP & 0xffff; + + pix = qp + xpoint; + ypoint++; + + area_sample_part(pix, xap, Cx, yap, &rgbset); + r = rgbset.r; + g = rgbset.g; + b = rgbset.b; + + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + if ((qp = (*store_line)(ypoint, args)) == NULL) + goto out; + + pix = qp + xpoint; + ypoint++; + + area_sample_part(pix, xap, Cx, Cy, &rgbset); + r += rgbset.r; + g += rgbset.g; + b += rgbset.b; + } + if (j > 0) { + + if ((qp = (*store_line)(ypoint, args)) == NULL) + goto out; + + pix = qp + xpoint; + ypoint++; + + area_sample_part(pix, xap, Cx, j, &rgbset); + r += rgbset.r; + g += rgbset.g; + b += rgbset.b; + } + + r = PACKRED(r, delta); + g = PACKGREEN(g, delta); + b = PACKBLUE(b, delta); + *dest = LCD_RGBPACK_LCD(r >> 5, g >> 5, b >> 5); + dest++; + } + } + return true; + +out: + return false; + +} + +/* + * This algorithm is taken from apps/plugins/lib/bmp.c simple_resize_bitmap(). + * And that algorithm is "Nearest Neighbour". + */ +static inline bool nearest(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; + + for (y = rowstart; y != rowstop; y += rowstep) { + fb_data *dest = (fb_data *)bitmap + y * dw; + 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; + int delta = 127; + if (dither) + delta = dither_mat(y & 0xf, x & 0xf); + xpoint = (val_x >> 8); + pix = qp + xpoint; + + int r = PACKRED(pix->red, delta); + int g = PACKGREEN(pix->green, delta); + int b = PACKBLUE(pix->blue, delta); + + *dest++ = LCD_RGBPACK_LCD(r, g, b); + val_x += inc_x; + } + val_y += inc_y; + } + return true; +} + +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) +{ + const int sw = src->width; + const int sh = src->height; + const int dw = bm->width; + const int dh = bm->height; + unsigned int xup_yup; + int format = bm->format; + + if (resize & IMG_BILINEAR) + xup_yup = 3; /* always use bilinear */ + else if (resize & IMG_NEAREST) + xup_yup = 4; /* always use nearest neighbour */ + else + xup_yup = (dw >= sw) + ((dh >= sh) << 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 (xup_yup == 0) { + /* downscaling both way use "Area Sampling" */ + if (!area_sample(bm, dither, src, rset, store_line, args)) + goto out; + } else if (xup_yup == 4) { + /* downscaling "Nearest Neighbour" */ + if (!nearest(bm, dither, src, rset, store_line, args)) + goto out; + } else { + /* + * xup_yup == 3 then upscaling both way + * xup_yup == 2 then downscaling horizontal + * xup_yup == 1 then downscaling vertically + * use "Biliner" + */ + if (!bilinear(bm, dither, src, rset, 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..55f042d --- /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..99d68f2 --- /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)