Index: apps/recorder/bmp.c =================================================================== --- apps/recorder/bmp.c (revision 18715) +++ apps/recorder/bmp.c (working copy) @@ -588,3 +588,855 @@ int read_bmp_fd(int fd, return totalsize; /* return the used buffer size. */ } + +#ifdef HAVE_ALBUMART +int get_bmp_size(int fd, struct bitmap *bmp) +{ + struct bmp_header bmph; + + int ret = read(fd, &bmph, sizeof(struct bmp_header)); + + if (ret < 0) + return ret * 10 - 2; + + if (ret != sizeof(struct bmp_header)) { + DEBUGF("get_bmp_size: can't read BMP header."); + return -3; + } + + bmp->width = readlong(&bmph.width); + bmp->height = readlong(&bmph.height); + + /* Top-down BMP file */ + if (bmp->height < 0) + bmp->height = -bmp->height; + + if (-1 == lseek(fd, -ret, SEEK_CUR)) + return 0; + + return get_bmp_file_size(bmp); +} + +int read_one_line(int fd, uint32_t *bmpb, int y, int padded_width, + int width, int depth, uint32_t *palette, off_t offset) +{ + unsigned data, mask; + unsigned char *p; + uint16_t *p2; + uint32_t *rp; + int ret; + union rgb_union q0, q1; + +#if 0 + /* Search to the beginning of the image data */ + lseek(fd, offset, SEEK_SET); + /* read one row */ + lseek(fd, y * padded_width, SEEK_CUR); +#else + (void)y; + (void)offset; +#endif + ret = read(fd, bmpb, padded_width); + if (ret != padded_width) { + DEBUGF("read_bmp_fd: error reading image, read returned: %d " + "expected: %d\n", ret, padded_width); + return -9; + } + + /* convert whole line in-place to XRGB8888 (little endian) */ + rp = bmpb + width; + switch (depth) { + case 1: + q0.raw = palette[0]; + q1.raw = palette[1]; + p = (unsigned char*)bmpb + ((width + 7) >> 3); + mask = 0x80 >> ((width + 7) & 7); + while (p > (unsigned char*)bmpb) { + data = *(--p); + for (; mask <= 0x80; mask <<= 1) + *(--rp) = (data & mask) ? q1.raw : q0.raw; + mask = 0x01; + } + break; + + case 4: + if (width & 1) + rp++; + p = (unsigned char*)bmpb + ((width + 1) >> 1); + while (p > (unsigned char*)bmpb) { + data = *(--p); + *(--rp) = palette[data & 0x0f]; + *(--rp) = palette[data >> 4]; + } + break; + + case 8: + p = (unsigned char*)bmpb + width; + while (p > (unsigned char*)bmpb) + *(--rp) = palette[*(--p)]; + break; + + case 15: + case 16: + p2 = (uint16_t *)bmpb + width; + while (p2 > (uint16_t *)bmpb) { + 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; + + case 24: + p = (unsigned char*)bmpb + 3 * width; + while (p > (unsigned char*)bmpb) { + data = *(--p); + data = (data << 8) | *(--p); + data = (data << 8) | *(--p); + *(--rp) = htole32(data); + } + break; + + case 32: /* already in desired format */ + break; + } + return 0; +} + +#define LIMIT_WIDTH (500) +#if (LCD_WIDTH < LIMIT_WIDTH) +#define MAX_WIDTH LIMIT_WIDTH +#else +#define MAX_WIDTH LCD_WIDTH +#endif + +struct cache_line_t { + uint32_t bmpbuf[MAX_WIDTH]; + int y; +}; + +#define CACHE_SIZE 5 +static struct cache_line_t cache_line[CACHE_SIZE]; +static int cache_line_index = 0; +#define ARRAY_SIZE(array) (int)(sizeof(array)/(sizeof(array[0]))) + +/****************************************************************************** + * read_bmp_fd_resize() + * + * Reads a BMP file in an open file descriptor and puts the data in rockbox + * format in *bitmap. + * + *****************************************************************************/ +int read_bmp_fd_resize(int fd, + struct bitmap *bm, + int maxsize, + int format) +{ + struct bmp_header bmph; + int width, height, padded_width; + int dst_height, dst_width; + int depth, numcolors, compression, totalsize; + int row, col, ret; + int rowstart, rowstop, rowstep, tmp_format = format; + + unsigned char *bitmap = bm->data; + uint32_t palette[256]; +#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; + + 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_resize: can't read BMP header."); + return -3; + } + + width = readlong(&bmph.width); + if (width > MAX_WIDTH) { + DEBUGF("read_bmp_fd_resize: Bitmap too wide (%d pixels, max is %d)\n", + width, MAX_WIDTH); + return -4; + } + + height = readlong(&bmph.height); + if (height < 0) { /* Top-down BMP file */ + height = -height; + rowstart = 0; + rowstop = height; + rowstep = 1; + } else { /* normal BMP */ + rowstart = height - 1; + rowstop = -1; + rowstep = -1; + } + + depth = readshort(&bmph.bit_count); + padded_width = ((width * depth + 31) >> 3) & ~3; /* 4-byte boundary aligned */ + +#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) */ +#if 0 + /* returning image size */ + bm->width = width; + bm->height = height; +#else + if ((bm->width * CACHE_SIZE < width) || + (bm->height * CACHE_SIZE < height)) { + DEBUGF("This bitmap is too large to resize!\n"); + lseek(fd, 0, SEEK_SET); + return read_bmp_fd(fd, bm, maxsize, tmp_format); + } +#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; + dst_height = (height + 7) >> 3; +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + //totalsize = dst_width * dst_height * sizeof(fb_remote_data); + totalsize = bm->width * bm->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); + totalsize = bm->width * bm->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 = bm->width * bm->height; + } + + /* Check if this fits the buffer */ + if (totalsize > maxsize) { + DEBUGF("read_bmp_fd_resize: 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) { + if (read(fd, palette, numcolors * sizeof(uint32_t)) + != numcolors * (int)sizeof(uint32_t)) + { + DEBUGF("read_bmp_fd_resize: Can't read color palette\n"); + return -7; + } + } + + 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 */ + if (!memcmp(palette, bitfields[0], 12)) { /* 15 bit */ + depth = 15; + break; + } + if (!memcmp(palette, bitfields[1], 12) /* 16 bit */ + || !memcmp(palette, bitfields[2], 12)) /* 32 bit */ + { + break; + } + } /* else fall through */ + + default: + if (compression != 0) { /* not BI_RGB */ + DEBUGF("read_bmp_fd_resize: 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); + + const int sw = width; + const int sh = 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; + const int xup_yup = (dw >= sw) + ((dh >= sh) << 1); + const int dow = dw; +// const int sow = sw; + int XAP, YAP, INV_YAP, INV_XAP; + int end = dw, x, y, val_y = 0, val_x, xpoint; + union rgb_union *ypoint; + int cli = -1; + bool find = false; + + if (rowstep == -1) { + rowstart = dh - 1; + rowstop = -1; + } else { + rowstart = 0; + rowstop = dh; + } + + cache_line_index=0; + for (cli = 0; cli < ARRAY_SIZE(cache_line); cli++) + cache_line[cli].y = -1; + + /* Convert to destination format */ + for (y = rowstart; y != rowstop; y += rowstep) { + union rgb_union *qp = (union rgb_union *)cache_line[0].bmpbuf, *qp2; + row = y; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + if (format == FORMAT_NATIVE) { +#if 0 +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + if (remote) { +#if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) + /* iAudio X5/M5 remote */ + fb_remote_data *dest = (fb_remote_data *)bitmap + + dst_width * (row >> 3); + int shift = row & 7; + int delta = 127; + unsigned bright; + + for (col = 0; col < width; col++) { + if (dither) + delta = dither_matrix[row & 0xf][col & 0xf]; + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pattern[bright] << shift; + } +#endif /* LCD_REMOTE_DEPTH / LCD_REMOTE_PIXELFORMAT */ + } else +#endif /* defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 */ +#endif + { +#if 0 +//#if LCD_DEPTH == 2 +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + /* greyscale iPods */ + fb_data *dest = (fb_data *)bitmap + dst_width * row; + int shift = 6; + int delta = 127; + unsigned bright; + unsigned data = 0; + + for (col = 0; col < width; col++) { + if (dither) + delta = dither_matrix[row & 0xf][col & 0xf]; + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + data |= (~bright & 3) << shift; + shift -= 2; + if (shift < 0) { + *dest++ = data; + data = 0; + shift = 6; + } + } + if (shift < 6) + *dest++ = data; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + /* iriver H1x0 */ + fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 2); + int shift = 2 * (row & 3); + int delta = 127; + unsigned bright; + + for (col = 0; col < width; col++) { + if (dither) + delta = dither_matrix[row & 0xf][col & 0xf]; + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= (~bright & 3) << shift; + } +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + /* iAudio M3 */ + fb_data *dest = (fb_data *)bitmap + dst_width * (row >> 3); + int shift = row & 7; + int delta = 127; + unsigned bright; + + for (col = 0; col < width; col++) { + if (dither) + delta = dither_matrix[row & 0xf][col & 0xf]; + bright = brightness(*qp++); + bright = (3 * bright + (bright >> 6) + delta) >> 8; + *dest++ |= vi_pattern[bright] << shift; + } +#endif /* LCD_PIXELFORMAT */ +#elif LCD_DEPTH == 16 + /* iriver h300, colour iPods, X5 */ + int delta = 127; + if (xup_yup == 3) { + /* upscaling both */ + int tmp_y=val_y>>16; + fb_data *dest = (fb_data *)bitmap + y * dow; + + for (find=false,cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if (tmp_y == cache_line[cli].y) { + qp = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find=true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, tmp_y, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = tmp_y; + qp = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + + ypoint = qp; + 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; + union rgb_union *pix; + if (dither) + delta = dither_matrix[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; + +#define PACKRED(q, delta) ((31 * q->red + (q->red >> 3) + delta) >> 8) +#define PACKGREEN(q, delta) ((63 * q->green + (q->green >> 2) + delta) >> 8) +#define PACKBLUE(q, delta) ((31 * q->blue + (q->blue >> 3) + delta) >> 8) + if (XAP > 0) { + pix = qp + xpoint; + r = PACKRED(pix, delta) * INV_XAP; + g = PACKGREEN(pix, delta) * INV_XAP; + b = PACKBLUE(pix, delta) * INV_XAP; + pix++; + r += PACKRED(pix, delta) * XAP; + g += PACKGREEN(pix, delta) * XAP; + b += PACKBLUE(pix, delta) * XAP; + + for (find=false,cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if (tmp_y+1 == cache_line[cli].y) { + qp2 = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find = true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, tmp_y+1, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = tmp_y+1; + qp2 = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + + pix = qp2 + xpoint; + rr = PACKRED(pix, delta) * INV_XAP; + gg = PACKGREEN(pix, delta) * INV_XAP; + bb = PACKBLUE(pix, delta) * INV_XAP; + pix++; + rr += PACKRED(pix, delta) * XAP; + gg += PACKGREEN(pix, delta) * XAP; + bb += PACKBLUE(pix, delta) * XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } else { + pix = qp + xpoint; + r = PACKRED(pix, delta) * INV_YAP; + g = PACKGREEN(pix, delta) * INV_YAP; + b = PACKBLUE(pix, delta) * INV_YAP; + + for (find=false,cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if (tmp_y+1 == cache_line[cli].y) { + qp2 = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find = true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, tmp_y+1, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = tmp_y+1; + qp2 = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + pix = qp2 + xpoint; + r += PACKRED(pix, delta) * YAP; + g += PACKGREEN(pix, delta) * YAP; + b += PACKBLUE(pix, delta) * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } + } + } else { + for (x = 0; x < dw; x++) { + int r = 0, g = 0, b = 0; + union rgb_union *pix; + if (dither) + delta = dither_matrix[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 = PACKRED(pix, delta) * INV_XAP; + g = PACKGREEN(pix, delta) * INV_XAP; + b = PACKBLUE(pix, delta) * INV_XAP; + pix++; + r += PACKRED(pix, delta) * XAP; + g += PACKGREEN(pix, delta) * XAP; + b += PACKBLUE(pix, delta) * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } else { + pix = qp; + r = PACKRED(pix, delta); + g = PACKGREEN(pix, delta); + b = PACKBLUE(pix, delta); + *dest++ = LCD_RGBPACK_LCD(r, g, b); + } + } + } + } else if (xup_yup == 0) { + /* downscaling both */ + int Cx, Cy, i, j; + union rgb_union *pix; + int r, g, b, rx, gx, bx; + int xap, yap; + fb_data *dest = (fb_data *)bitmap + y * dow; + int cli; + for (find=false,cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if ((val_y >> 16) == cache_line[cli].y) { + qp = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find=true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, val_y >> 16, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = val_y >> 16; + qp = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + ypoint = qp; + 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 < end; x++) { + int tmp_y = (val_y-inc_y) >> 16; + if (dither) + delta = dither_matrix[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; + tmp_y++; + + rx = (PACKRED(pix, delta) * xap) >> 9; + gx = (PACKGREEN(pix, delta) * xap) >> 9; + bx = (PACKBLUE(pix, delta) * xap) >>9; + pix++; + for (i = (1 << 14) - xap; i > Cx; i -= Cx) { + rx += (PACKRED(pix, delta) * Cx) >> 9; + gx += (PACKGREEN(pix, delta) * Cx) >> 9; + bx += (PACKBLUE(pix, delta) * Cx) >>9; + pix++; + } + if (i > 0) { + rx += (PACKRED(pix, delta) * i) >> 9; + gx += (PACKGREEN(pix, delta) * i) >> 9; + bx += (PACKBLUE(pix, delta) * i) >> 9; + } + + r = (rx * yap) >> 14; + g = (gx * yap) >> 14; + b = (bx * yap) >> 14; + + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + for (find=false, cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if (tmp_y == cache_line[cli].y) { + qp = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find=true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, tmp_y, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = tmp_y; + qp = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + pix = qp + xpoint; + tmp_y++; + + rx = (PACKRED(pix, delta) * xap) >> 9; + gx = (PACKGREEN(pix, delta) * xap) >> 9; + bx = (PACKBLUE(pix, delta) * xap) >> 9; + pix++; + for (i = (1 << 14) - xap; i > Cx; i -= Cx) { + rx += (PACKRED(pix, delta) * Cx) >> 9; + gx += (PACKGREEN(pix, delta) * Cx) >> 9; + bx += (PACKBLUE(pix, delta) * Cx) >> 9; + pix++; + } + if (i > 0) { + rx += (PACKRED(pix, delta) * i) >> 9; + gx += (PACKGREEN(pix, delta) * i) >> 9; + bx += (PACKBLUE(pix, delta) * i) >> 9; + } + + r += (rx * Cy) >> 14; + g += (gx * Cy) >> 14; + b += (bx * Cy) >> 14; + } + if (j > 0) { + + for (find=false,cli = 0; cli < ARRAY_SIZE(cache_line); cli++) { + if (tmp_y == cache_line[cli].y) { + qp = (union rgb_union *)cache_line[cli].bmpbuf; + cache_line_index = cli+1; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + find =true; + break; + } + } + if (find==false) { + if (0 != read_one_line(fd, (uint32_t *)cache_line[cache_line_index].bmpbuf, tmp_y, + padded_width, width, depth, palette, (off_t)readlong(&bmph.off_bits))) + goto out; + cache_line[cache_line_index].y = tmp_y; + qp = (union rgb_union *)cache_line[cache_line_index].bmpbuf; + cache_line_index++; + if (cache_line_index == ARRAY_SIZE(cache_line)) + cache_line_index = 0; + } + + pix = qp + xpoint; + tmp_y++; + + rx = (PACKRED(pix, delta) * xap) >> 9; + gx = (PACKGREEN(pix, delta) * xap) >> 9; + bx = (PACKBLUE(pix, delta) * xap) >> 9; + pix++; + for (i = (1 << 14) - xap; i > Cx; i -= Cx) { + rx += (PACKRED(pix, delta) * Cx) >> 9; + gx += (PACKGREEN(pix, delta) * Cx) >> 9; + bx += (PACKBLUE(pix, delta) * Cx) >> 9; + pix++; + } + if (i > 0) { + rx += (PACKRED(pix, delta) * i) >> 9; + gx += (PACKGREEN(pix, delta) * i) >> 9; + bx += (PACKBLUE(pix, delta) * i) >> 9; + } + + r += (rx * j) >> 14; + g += (gx * j) >> 14; + b += (bx * j) >> 14; + } + + *dest = LCD_RGBPACK_LCD(r >> 5, g >> 5, b >> 5); + dest++; + } + } else { + DEBUGF("error: xup_yup = %d\n", xup_yup); + goto out; + } +#endif /* LCD_DEPTH */ + } + } else +#endif /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) */ + { + unsigned char *p; + unsigned mask; + p = bitmap + dst_width * (row >> 3); + mask = 1 << (row & 7); + + for (col = 0; col < width; col++, p++) + if (brightness(*qp++) < 128) + *p |= mask; + } + } + + return totalsize; /* return the used buffer size. */ + +out: + return -1; +} + +#endif /* HAVE_ALBUMART */ Index: apps/recorder/bmp.h =================================================================== --- apps/recorder/bmp.h (revision 18715) +++ apps/recorder/bmp.h (working copy) @@ -23,6 +23,7 @@ #include "config.h" #include "lcd.h" +#include "debug.h" /********************************************************************* * read_bmp_file() @@ -41,4 +42,38 @@ int read_bmp_fd(int fd, struct bitmap *bm, int maxsize, int format); + +#ifdef HAVE_ALBUMART +int read_bmp_fd_resize(int fd, + struct bitmap *bm, + int maxsize, + int format); + +static inline int get_bmp_file_size(struct bitmap *bmp) +{ + int width = bmp->width, height = bmp->height; + int dst_width, dst_height; + +#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 */ + + return dst_width * dst_height * sizeof(fb_data); +} + +int get_bmp_size(int fd, + struct bitmap *bmp); +#endif /* HAVE_ALBUMART */ #endif Index: apps/recorder/albumart.c =================================================================== --- apps/recorder/albumart.c (revision 18715) +++ apps/recorder/albumart.c (working copy) @@ -30,7 +30,9 @@ #include "debug.h" #include "misc.h" #include "settings.h" +#include "bmp.h" +#define SCALE 1000 /* Strip filename from a full path * @@ -297,3 +299,41 @@ void draw_album_art(struct gui_wps *gwps gwps->display->set_drawmode(DRMODE_SOLID); } } + +int get_albumart_size(struct bitmap *dst_bmp, struct bitmap *src_bmp) +{ + struct wps_data *data = gui_wps[0].data; + + short aa_max_width = data->albumart_max_width; + short aa_max_height = data->albumart_max_height; + const short width = src_bmp->width; + const short height = src_bmp->height; + + if (aa_max_width < 0 || aa_max_height < 0 || width <= 0 || height <= 0) + return 0; + + if (aa_max_width == 0) + aa_max_width = width; + if (aa_max_height == 0) + aa_max_height = height; + + if ((aa_max_width == width) && (aa_max_width == width)) { + dst_bmp->width = width; + dst_bmp->height = height; + } else { + /* + * keep aspect ratio + * because resize fanction doesn't keep aspect rate + */ + const unsigned short factx = aa_max_width * SCALE / width; + const unsigned short facty = aa_max_height * SCALE / height; + const unsigned short fact = MIN(factx, facty); + + dst_bmp->width = (width * fact + SCALE/2) / SCALE; /* round off */ + dst_bmp->height = (height * fact + SCALE/2) / SCALE; /* round off */ + } + + //DEBUGF("get_albumart_size: %d\n",get_bmp_file_size(dst_bmp)); + + return get_bmp_file_size(dst_bmp); +} Index: apps/recorder/albumart.h =================================================================== --- apps/recorder/albumart.h (revision 18715) +++ apps/recorder/albumart.h (working copy) @@ -40,6 +40,8 @@ void draw_album_art(struct gui_wps *gwps bool search_albumart_files(const struct mp3entry *id3, const char *size_string, char *buf, int buflen); +int get_albumart_size(struct bitmap *dst_bmp, struct bitmap *src_bmp); + #endif /* HAVE_ALBUMART */ #endif /* _ALBUMART_H_ */ Index: apps/gui/gwps.h =================================================================== --- apps/gui/gwps.h (revision 18715) +++ apps/gui/gwps.h (working copy) @@ -47,13 +47,11 @@ #define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ #define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ -#define WPS_ALBUMART_ALIGN_RIGHT WPS_ALIGN_RIGHT /* x align: right */ -#define WPS_ALBUMART_ALIGN_CENTER WPS_ALIGN_CENTER /* x/y align: center */ -#define WPS_ALBUMART_ALIGN_LEFT WPS_ALIGN_LEFT /* x align: left */ -#define WPS_ALBUMART_ALIGN_TOP WPS_ALIGN_RIGHT /* y align: top */ -#define WPS_ALBUMART_ALIGN_BOTTOM WPS_ALIGN_LEFT /* y align: bottom */ -#define WPS_ALBUMART_INCREASE 8 /* increase if smaller */ -#define WPS_ALBUMART_DECREASE 16 /* decrease if larger */ +#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ #endif /* HAVE_ALBUMART */ @@ -379,10 +377,8 @@ struct wps_data unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ short albumart_x; short albumart_y; - unsigned short albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT, - + .._INCREASE, + .._DECREASE */ - unsigned short albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM, - + .._INCREASE, + .._DECREASE */ + unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ + unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ short albumart_max_width; short albumart_max_height; @@ -508,4 +504,6 @@ void gui_sync_wps_screen_init(void); bool gui_sync_wps_uses_albumart(void); #endif +void bmp_init(void); + #endif Index: apps/gui/wps_parser.c =================================================================== --- apps/gui/wps_parser.c (revision 18715) +++ apps/gui/wps_parser.c (working copy) @@ -897,13 +897,6 @@ static int parse_albumart_load(const cha { const char *_pos, *newline; bool parsing; - const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_RIGHT; - const short yalign_mask = WPS_ALBUMART_ALIGN_TOP | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_BOTTOM; - (void)token; /* silence warning */ /* reset albumart info in wps */ @@ -913,7 +906,7 @@ static int parse_albumart_load(const cha wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ - /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */ + /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */ newline = strchr(wps_bufptr, '\n'); @@ -948,35 +941,24 @@ static int parse_albumart_load(const cha case 'l': case 'L': case '+': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_LEFT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT; break; case 'c': case 'C': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'r': case 'R': case '-': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_RIGHT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT; break; case 'd': case 'D': - wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE; - break; case 'i': case 'I': - wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE; - break; case 's': case 'S': - wps_data->albumart_xalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + /* simply ignored */ break; default: parsing = false; @@ -1008,35 +990,24 @@ static int parse_albumart_load(const cha case 't': case 'T': case '-': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_TOP; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP; break; case 'c': case 'C': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'b': case 'B': case '+': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_BOTTOM; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM; break; case 'd': case 'D': - wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE; - break; case 'i': case 'I': - wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE; - break; case 's': case 'S': - wps_data->albumart_yalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + /* simply ignored */ break; default: parsing = false; Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 18715) +++ apps/buffering.c (working copy) @@ -50,6 +50,9 @@ #include "pcmbuf.h" #include "buffer.h" #include "bmp.h" +#ifdef HAVE_ALBUMART +#include "albumart.h" +#endif #include "events.h" #include "metadata.h" @@ -843,18 +846,37 @@ static bool fill_buffer(void) Return value is the total size (struct + data). */ static int load_bitmap(int fd) { - int rc; - struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + struct bitmap *aa_bmp, orig_bmp; + int orig_sz, aa_sz, free; /* bytes */ + + orig_sz = get_bmp_size(fd, &orig_bmp); + + if (orig_sz <= 0) + return orig_sz; + + aa_bmp = (struct bitmap *)&buffer[buf_widx]; + free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx) + - sizeof(struct bitmap); + + aa_sz = get_albumart_size(aa_bmp, &orig_bmp); + + if ((aa_sz > free) || aa_sz <= 0) + return 0; + /* FIXME: alignment may be needed for the data buffer. */ - bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; + aa_bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - bmp->maskdata = NULL; + aa_bmp->maskdata = NULL; #endif - int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx); - rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_DITHER); - return rc + (rc > 0 ? sizeof(struct bitmap) : 0); + if (aa_bmp->width == orig_bmp.width && aa_bmp->height == orig_bmp.height) { + aa_sz = read_bmp_fd(fd, aa_bmp, free, FORMAT_ANY|FORMAT_DITHER); + } else { + aa_sz = read_bmp_fd_resize(fd, aa_bmp, free, FORMAT_ANY|FORMAT_DITHER); + } + + return aa_sz + (aa_sz > 0 ? sizeof(struct bitmap) : 0); } #endif