| /*************************************************************************** |
| * __________ __ ___. |
| * 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 |
| 2008-11-02 Akio Idehara: refactor for scaler frontend |
| 2008-12-08 Andrew Mahone: partial-line reading, scaler frontend |
| - read_part_line does the actual source BMP reading, return columns read |
| and updates fields in a struct bmp_args with the new data and current |
| reader state |
| - skip_lines_bmp and store_part_bmp implement the scaler callbacks to skip |
| ahead by whole lines, or read the next chunk of the current line |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "inttypes.h" |
| #include "system.h" |
| #ifndef PLUGIN |
| #include "debug.h" |
| #endif |
| #include "lcd.h" |
| #include "file.h" |
| #include "bmp.h" |
| #ifdef HAVE_REMOTE_LCD |
| #include "lcd-remote.h" |
| #endif |
| #ifdef ROCKBOX_DEBUG_BMP_LOADER |
| #define BDEBUGF DEBUGF |
| #else |
| #define BDEBUGF(...) |
| #endif |
| #ifndef __PCTOOL__ |
| #include "config.h" |
| #include "resize.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; |
| |
| /* masks for supported BI_BITFIELDS encodings (16/32 bit) */ |
| static const struct uint8_rgb bitfields[][4] = { |
| /* 15bit */ |
| { |
| { .blue = 0x00, .green = 0x7c, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0xe0, .green = 0x03, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0x1f, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0x00, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| }, |
| /* 16bit */ |
| { |
| { .blue = 0x00, .green = 0xf8, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0xe0, .green = 0x07, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0x1f, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0x00, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| }, |
| /* 32bit BGRA */ |
| { |
| { .blue = 0x00, .green = 0x00, .red = 0xff, .alpha = 0x00 }, |
| { .blue = 0x00, .green = 0xff, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0xff, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0x00, .green = 0x00, .red = 0x00, .alpha = 0xff }, |
| }, |
| /* 32bit ABGR */ |
| { |
| { .blue = 0x00, .green = 0x00, .red = 0x00, .alpha = 0xff }, |
| { .blue = 0x00, .green = 0x00, .red = 0xff, .alpha = 0x00 }, |
| { .blue = 0x00, .green = 0xff, .red = 0x00, .alpha = 0x00 }, |
| { .blue = 0xff, .green = 0x00, .red = 0x00, .alpha = 0x00 }, |
| }, |
| }; |
| |
| #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) |
| /* the full 16x16 Bayer dither matrix may be calculated quickly with this table |
| */ |
| const unsigned char dither_table[16] = |
| { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }; |
| #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 |
| |
| /****************************************************************************** |
| * 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, |
| int format, |
| const struct custom_format *cformat) |
| { |
| 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; |
| } |
| |
| BDEBUGF("read_bmp_file: '%s' remote: %d resize: %d keep_aspect: %d\n", |
| filename, !!(format & FORMAT_REMOTE), !!(format & FORMAT_RESIZE), |
| !!(format & FORMAT_KEEP_ASPECT)); |
| ret = read_bmp_fd(fd, bm, maxsize, format, cformat); |
| close(fd); |
| return ret; |
| } |
| |
| enum color_order { |
| /* only used for different types of 32bpp images */ |
| BGRA, /* should be most common */ |
| ABGR /* generated by some GIMP versions */ |
| }; |
| |
| struct bmp_args { |
| /* needs to be at least 2byte aligned for faster 16bit reads. |
| * but aligning to cache should be even faster */ |
| unsigned char buf[BM_MAX_WIDTH * 4] CACHEALIGN_AT_LEAST_ATTR(2); |
| int fd; |
| short padded_width; |
| short read_width; |
| short width; |
| short depth; |
| enum color_order order; |
| struct uint8_rgb *palette; |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| int cur_row; |
| int cur_col; |
| struct img_part part; |
| #endif |
| /* as read_part_line() goes through the rows it'll set this to true |
| * if it finds transparency. Initialize to 0 before calling */ |
| int alpha_detected; |
| /* for checking transparency it checks the against the very first byte |
| * of the bitmap. Initalize to 0x80 before calling */ |
| unsigned char first_alpha_byte; |
| }; |
| |
| static unsigned int read_part_line(struct bmp_args *ba) |
| { |
| const int padded_width = ba->padded_width; |
| const int read_width = ba->read_width; |
| const int width = ba->width; |
| int depth = ba->depth; |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| int cur_row = ba->cur_row; |
| int cur_col = ba->cur_col; |
| #endif |
| const int fd = ba->fd; |
| uint8_t *ibuf; |
| struct uint8_rgb *buf = (struct uint8_rgb *)(ba->buf); |
| const struct uint8_rgb *palette = ba->palette; |
| uint32_t component, data; |
| int ret; |
| int i, cols, len; |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| cols = MIN(width - cur_col,(int)BM_MAX_WIDTH); |
| BDEBUGF("reading %d cols (width: %d, max: %d)\n",cols,width,BM_MAX_WIDTH); |
| len = (cols * (depth == 15 ? 16 : depth) + 7) >> 3; |
| #else |
| cols = width; |
| len = read_width; |
| #endif |
| ibuf = ((unsigned char *)buf) + (BM_MAX_WIDTH << 2) - len; |
| BDEBUGF("read_part_line: cols=%d len=%d\n",cols,len); |
| ret = read(fd, ibuf, len); |
| if (ret != len) |
| { |
| DEBUGF("read_part_line: error reading image, read returned %d " |
| "expected %d\n", ret, len); |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| BDEBUGF("cur_row: %d cur_col: %d cols: %d len: %d\n", cur_row, cur_col, |
| cols, len); |
| #endif |
| return 0; |
| } |
| |
| /* detect if the image has useful alpha information. |
| * if all alpha bits are 0xff or 0x00 discard the information. |
| * if it has other bits, or is mixed with 0x00 and 0xff then interpret |
| * as alpha. assume no alpha until the opposite is proven. as mixed |
| * is alpha, compare to the first byte instead of 0xff and 0x00 separately |
| */ |
| if (depth == 32 && ba->first_alpha_byte == 0x80) |
| ba->first_alpha_byte = ibuf[3] ? 0xff : 0x0; |
| |
| /* select different color orders within the switch-case to avoid |
| * nested if/switch */ |
| if (depth == 32) |
| depth += ba->order; |
| |
| while (ibuf < ba->buf + (BM_MAX_WIDTH << 2)) |
| { |
| switch (depth) |
| { |
| case 1: |
| data = *ibuf++; |
| for (i = 0; i < 8; i++) |
| { |
| *buf++ = palette[data & 0x80 ? 1 : 0]; |
| data <<= 1; |
| } |
| break; |
| case 4: |
| data = *ibuf++; |
| *buf++ = palette[data >> 4]; |
| *buf++ = palette[data & 0xf]; |
| break; |
| case 8: |
| *buf++ = palette[*ibuf++]; |
| break; |
| case 15: |
| case 16: |
| data = letoh16(*(uint16_t*)ibuf); |
| component = (data << 3) & 0xf8; |
| component |= component >> 5; |
| buf->blue = component; |
| if (depth == 15) |
| { |
| data >>= 2; |
| component = data & 0xf8; |
| component |= component >> 5; |
| } else { |
| data >>= 3; |
| component = data & 0xfc; |
| component |= component >> 6; |
| } |
| buf->green = component; |
| data >>= 5; |
| component = data & 0xf8; |
| component |= component >> 5; |
| buf->red = component; |
| buf->alpha = 0xff; |
| buf++; |
| ibuf += 2; |
| break; |
| case 24: |
| buf->blue = *ibuf++; |
| buf->green = *ibuf++; |
| buf->red = *ibuf++; |
| buf->alpha = 0xff; |
| buf++; |
| break; |
| case 32 + BGRA: |
| buf->blue = *ibuf++; |
| buf->green = *ibuf++; |
| buf->red = *ibuf++; |
| buf->alpha = *ibuf++; |
| ba->alpha_detected |= (buf->alpha != ba->first_alpha_byte); |
| buf++; |
| break; |
| case 32 + ABGR: |
| buf->alpha = *ibuf++; |
| buf->blue = *ibuf++; |
| buf->green = *ibuf++; |
| buf->red = *ibuf++; |
| ba->alpha_detected |= (buf->alpha != ba->first_alpha_byte); |
| buf++; |
| break; |
| } |
| } |
| #if !defined(HAVE_LCD_COLOR) && \ |
| ((LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ |
| defined(PLUGIN)) |
| ibuf = ba->buf; |
| buf = (struct uint8_rgb*)ba->buf; |
| while (ibuf < ba->buf + cols) |
| *ibuf++ = brightness(*buf++); |
| #endif |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| cur_col += cols; |
| if (cur_col == width) |
| { |
| #endif |
| int pad = padded_width - read_width; |
| if (pad > 0) |
| { |
| BDEBUGF("seeking %d bytes to next line\n",pad); |
| lseek(fd, pad, SEEK_CUR); |
| } |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| cur_col = 0; |
| BDEBUGF("read_part_line: completed row %d\n", cur_row); |
| cur_row += 1; |
| } |
| |
| ba->cur_row = cur_row; |
| ba->cur_col = cur_col; |
| #endif |
| return cols; |
| } |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| static struct img_part *store_part_bmp(void *args) |
| { |
| struct bmp_args *ba = (struct bmp_args *)args; |
| |
| ba->part.len = read_part_line(ba); |
| #ifdef HAVE_LCD_COLOR |
| ba->part.buf = (struct uint8_rgb *)ba->buf; |
| #else |
| ba->part.buf = (uint8_t *)ba->buf; |
| #endif |
| if (ba->part.len) |
| return &(ba->part); |
| else |
| return NULL; |
| } |
| #endif |
| |
| static inline int rgbcmp(const struct uint8_rgb *rgb1, const struct uint8_rgb *rgb2) |
| { |
| return memcmp(rgb1, rgb2, sizeof(struct uint8_rgb)); |
| } |
| |
| #if LCD_DEPTH > 1 |
| #if !defined(PLUGIN) && !defined(HAVE_JPEG) && !defined(HAVE_BMP_SCALING) |
| static inline |
| #endif |
| void output_row_8_native(uint32_t row, void * row_in, |
| struct scaler_context *ctx) |
| { |
| int col; |
| int fb_width = BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0); |
| uint8_t dy = DITHERY(row); |
| #ifdef HAVE_LCD_COLOR |
| struct uint8_rgb *qp = (struct uint8_rgb*)row_in; |
| #else |
| uint8_t *qp = (uint8_t*)row_in; |
| #endif |
| BDEBUGF("output_row: y: %lu in: %p\n",row, row_in); |
| #if LCD_DEPTH == 2 |
| #if LCD_PIXELFORMAT == HORIZONTAL_PACKING |
| /* greyscale iPods */ |
| fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row; |
| int shift = 6; |
| int delta = 127; |
| unsigned bright; |
| unsigned data = 0; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = *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 *)ctx->bm->data + fb_width * |
| (row >> 2); |
| int shift = 2 * (row & 3); |
| int delta = 127; |
| unsigned bright; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = *qp++; |
| bright = (3 * bright + (bright >> 6) + delta) >> 8; |
| *dest++ |= (~bright & 3) << shift; |
| } |
| #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED |
| /* iAudio M3 */ |
| fb_data *dest = (fb_data *)ctx->bm->data + fb_width * |
| (row >> 3); |
| int shift = row & 7; |
| int delta = 127; |
| unsigned bright; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = *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 */ |
| (void)fb_width; |
| fb_data *dest = STRIDE_MAIN((fb_data *)ctx->bm->data + fb_width * row, |
| (fb_data *)ctx->bm->data + row); |
| int delta = 127; |
| unsigned r, g, b; |
| /* setup alpha channel buffer */ |
| unsigned char *bm_alpha = NULL; |
| if (ctx->bm->alpha_offset > 0) |
| bm_alpha = ctx->bm->data + ctx->bm->alpha_offset; |
| if (bm_alpha) |
| bm_alpha += ALIGN_UP(ctx->bm->width, 2) * row/2; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| r = qp->red; |
| g = qp->green; |
| b = qp->blue; |
| r = (31 * r + (r >> 3) + delta) >> 8; |
| g = (63 * g + (g >> 2) + delta) >> 8; |
| b = (31 * b + (b >> 3) + delta) >> 8; |
| *dest = LCD_RGBPACK_LCD(r, g, b); |
| dest += STRIDE_MAIN(1, ctx->bm->height); |
| if (bm_alpha) { |
| /* pack alpha channel for 2 pixels into 1 byte and negate |
| * according to the interal alpha channel format */ |
| uint8_t alpha = ~qp->alpha; |
| if (col%2) |
| *bm_alpha++ |= alpha&0xf0; |
| else |
| *bm_alpha = alpha>>4; |
| } |
| qp++; |
| } |
| #endif /* LCD_DEPTH */ |
| } |
| #endif |
| |
| /****************************************************************************** |
| * 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, |
| int format, |
| const struct custom_format *cformat) |
| { |
| struct bmp_header bmph; |
| int padded_width; |
| int read_width; |
| int depth, numcolors, compression, totalsize; |
| int ret; |
| bool return_size = format & FORMAT_RETURN_SIZE; |
| bool read_alpha = format & FORMAT_TRANSPARENT; |
| enum color_order order = BGRA; |
| |
| unsigned char *bitmap = bm->data; |
| struct uint8_rgb palette[256]; |
| struct rowset rset; |
| struct dim src_dim; |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ |
| defined(PLUGIN) |
| bool dither = false; |
| #endif |
| |
| #ifdef HAVE_REMOTE_LCD |
| bool remote = false; |
| if (format & FORMAT_REMOTE) { |
| remote = true; |
| #if LCD_REMOTE_DEPTH == 1 |
| format = FORMAT_MONO; |
| #endif |
| } |
| #endif /* HAVE_REMOTE_LCD */ |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| unsigned int resize = IMG_NORESIZE; |
| |
| if (format & FORMAT_RESIZE) { |
| resize = IMG_RESIZE; |
| } |
| |
| #else |
| |
| (void)format; |
| #endif /*(LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)*/ |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ |
| defined(PLUGIN) |
| if (format & FORMAT_DITHER) { |
| dither = true; |
| } |
| #endif |
| /* 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 = letoh32(bmph.width); |
| src_dim.height = letoh32(bmph.height); |
| if (src_dim.height < 0) { /* Top-down BMP file */ |
| src_dim.height = -src_dim.height; |
| rset.rowstep = 1; |
| } else { /* normal BMP */ |
| rset.rowstep = -1; |
| } |
| |
| depth = letoh16(bmph.bit_count); |
| /* 4-byte boundary aligned */ |
| read_width = ((src_dim.width * (depth == 15 ? 16 : depth) + 7) >> 3); |
| padded_width = (read_width + 3) & ~3; |
| |
| BDEBUGF("width: %d height: %d depth: %d padded_width: %d\n", src_dim.width, |
| src_dim.height, depth, padded_width); |
| |
| #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) |
| if ((format & 3) == FORMAT_ANY) { |
| if (depth == 1) |
| format = (format & ~3); |
| else |
| format = (format & ~3) | FORMAT_NATIVE; |
| } |
| bm->format = format & 1; |
| if ((format & 1) == FORMAT_MONO) |
| { |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| resize &= ~IMG_RESIZE; |
| resize |= IMG_NORESIZE; |
| #endif |
| #ifdef HAVE_REMOTE_LCD |
| remote = false; |
| #endif |
| } |
| #elif !defined(PLUGIN) |
| if (src_dim.width > BM_MAX_WIDTH) |
| return -6; |
| #endif /*(LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)*/ |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| if (resize & IMG_RESIZE) { |
| if(format & FORMAT_KEEP_ASPECT) { |
| /* keep aspect ratio.. */ |
| struct dim resize_dim = { |
| .width = bm->width, |
| .height = bm->height, |
| }; |
| if (recalc_dimension(&resize_dim, &src_dim)) |
| resize = IMG_NORESIZE; |
| bm->width = resize_dim.width; |
| bm->height = resize_dim.height; |
| } |
| } |
| |
| if (!(resize & IMG_RESIZE)) { |
| #endif |
| /* returning image size */ |
| bm->width = src_dim.width; |
| bm->height = src_dim.height; |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| } |
| #endif |
| #if LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) |
| format &= 1; |
| #endif |
| if (rset.rowstep > 0) { /* Top-down BMP file */ |
| rset.rowstart = 0; |
| rset.rowstop = bm->height; |
| } else { /* normal BMP */ |
| rset.rowstart = bm->height - 1; |
| rset.rowstop = -1; |
| } |
| |
| /* need even rows (see lcd-16bit-common.c for details) */ |
| int alphasize = ALIGN_UP(bm->width, 2) * bm->height / 2; |
| if (cformat) |
| totalsize = cformat->get_size(bm); |
| else { |
| totalsize = BM_SIZE(bm->width,bm->height,format,remote); |
| #ifdef HAVE_REMOTE_LCD |
| if (!remote) |
| #endif |
| if (depth == 32 && read_alpha) /* account for possible 4bit alpha per pixel */ |
| totalsize += alphasize; |
| } |
| |
| if(return_size) |
| { |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| if(resize) |
| totalsize += BM_SCALED_SIZE(bm->width, 0, 0, 0); |
| else if (bm->width > BM_MAX_WIDTH) |
| totalsize += bm->width*4; |
| #endif |
| return totalsize; |
| } |
| |
| /* 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 = letoh32(bmph.compression); |
| if (depth <= 8) { |
| numcolors = letoh32(bmph.clr_used); |
| if (numcolors == 0) |
| numcolors = BIT_N(depth); |
| } else { |
| int hdr_size = letoh32(bmph.struct_size); |
| numcolors = 0; |
| if (compression == 3) { |
| if (hdr_size >= 56) |
| numcolors = 4; |
| else /* hdr_size == 52 */ |
| numcolors = 3; |
| } |
| } |
| |
| /* read color tables. for BI_BITFIELDS this actually |
| * reads the color masks */ |
| if (numcolors > 0 && numcolors <= 256) { |
| int i; |
| for (i = 0; i < numcolors; i++) { |
| if (read(fd, &palette[i], sizeof(struct uint8_rgb)) |
| != (int)sizeof(struct uint8_rgb)) { |
| DEBUGF("read_bmp_fd: 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 */ |
| bool found = false; |
| int i, j; |
| |
| /* (i == 0) is 15bit, (i == 1) is 16bit, (i == {2,3}) is 32bit */ |
| for (i = 0; i < ARRAY_SIZE(bitfields) && !found; i++) { |
| /* for 15bpp and higher numcolors has the number of color masks */ |
| for (j = 0; j < numcolors; j++) { |
| if (!rgbcmp(&palette[j], &bitfields[i][j])) { |
| found = true; |
| } else { |
| found = false; |
| break; |
| } |
| } |
| } |
| if (found) { |
| if (i == 1) /* 15bit */ |
| depth = 15; |
| else if (i == 4) /* 32bit, ABGR bitmap */ |
| order = ABGR; |
| 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)letoh32(bmph.off_bits), SEEK_SET); |
| |
| memset(bitmap, 0, totalsize); |
| |
| #ifdef HAVE_LCD_COLOR |
| if (read_alpha && depth == 32) |
| bm->alpha_offset = totalsize - alphasize; |
| else |
| bm->alpha_offset = 0; |
| #endif |
| |
| struct bmp_args ba = { |
| .fd = fd, .padded_width = padded_width, .read_width = read_width, |
| .width = src_dim.width, .depth = depth, .palette = palette, |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| .cur_row = 0, .cur_col = 0, .part = {0,0}, |
| #endif |
| .alpha_detected = false, .first_alpha_byte = 0x80, |
| .order = order, |
| }; |
| |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| if (resize) |
| { |
| if (resize_on_load(bm, dither, &src_dim, &rset, |
| bitmap + totalsize, maxsize - totalsize, |
| cformat, IF_PIX_FMT(0,) store_part_bmp, &ba)) |
| return totalsize; |
| else |
| return 0; |
| } |
| #endif /* LCD_DEPTH */ |
| |
| #if LCD_DEPTH > 1 || defined(PLUGIN) |
| struct scaler_context ctx = { |
| .bm = bm, |
| .dither = dither, |
| }; |
| #endif |
| #if defined(PLUGIN) || defined(HAVE_JPEG) || defined(HAVE_BMP_SCALING) |
| #if LCD_DEPTH > 1 |
| void (*output_row_8)(uint32_t, void*, struct scaler_context*) = |
| output_row_8_native; |
| #elif defined(PLUGIN) |
| void (*output_row_8)(uint32_t, void*, struct scaler_context*) = NULL; |
| #endif |
| #if LCD_DEPTH > 1 || defined(PLUGIN) |
| if (cformat) |
| output_row_8 = cformat->output_row_8; |
| #endif |
| #endif |
| |
| unsigned char *buf = ba.buf; |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ |
| defined(PLUGIN) |
| if (bm->width > BM_MAX_WIDTH) |
| { |
| #if defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| unsigned int len = maxsize - totalsize; |
| buf = bitmap + totalsize; |
| ALIGN_BUFFER(buf, len, sizeof(uint32_t)); |
| if (bm->width*4 > (int)len) |
| #endif |
| return -6; |
| } |
| #endif |
| int row; |
| /* loop to read rows and put them to buffer */ |
| for (row = rset.rowstart; row != rset.rowstop; row += rset.rowstep) { |
| #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ |
| defined(HAVE_BMP_SCALING) || defined(PLUGIN) |
| if (bm->width > BM_MAX_WIDTH) |
| { |
| #if defined(HAVE_LCD_COLOR) |
| struct uint8_rgb *p = (struct uint8_rgb *)buf; |
| #else |
| uint8_t* p = buf; |
| #endif |
| do { |
| int len = read_part_line(&ba); |
| if (!len) |
| return -9; |
| memcpy(p, ba.buf, len*sizeof(*p)); |
| p += len; |
| } while (ba.cur_col); |
| } |
| else |
| #endif |
| if (!read_part_line(&ba)) |
| return -9; |
| #ifndef PLUGIN |
| #if !defined(HAVE_LCD_COLOR) && \ |
| (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) |
| uint8_t* qp = buf; |
| #else |
| struct uint8_rgb *qp = (struct uint8_rgb *)buf; |
| #endif |
| #endif |
| /* Convert to destination format */ |
| #if ((LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) && \ |
| !defined(PLUGIN) |
| if (format == FORMAT_NATIVE) { |
| #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 |
| if (remote) { |
| unsigned char dy = DITHERY(row); |
| #if (LCD_REMOTE_DEPTH == 2) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) |
| /* iAudio X5/M5 remote */ |
| fb_remote_data *dest = (fb_remote_data *)bitmap |
| + bm->width * (row >> 3); |
| int shift = row & 7; |
| int delta = 127; |
| unsigned bright; |
| |
| int col; |
| for (col = 0; col < bm->width; col++) { |
| if (dither) |
| delta = DITHERXDY(col,dy); |
| #if !defined(HAVE_LCD_COLOR) && \ |
| (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) |
| bright = *qp++; |
| #else |
| bright = brightness(*qp++); |
| #endif |
| 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 /* (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && |
| (LCD_REMOTE_DEPTH > 1) */ |
| #if LCD_DEPTH > 1 || defined(PLUGIN) |
| { |
| #if !defined(PLUGIN) && !defined(HAVE_JPEG) && !defined(HAVE_BMP_SCALING) |
| output_row_8_native(row, buf, &ctx); |
| #else |
| output_row_8(row, buf, &ctx); |
| #endif |
| } |
| #endif |
| #if ((LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) && \ |
| !defined(PLUGIN) |
| } |
| #ifndef PLUGIN |
| else |
| #endif |
| #endif |
| #ifndef PLUGIN |
| { |
| unsigned char *p = bitmap + bm->width * (row >> 3); |
| unsigned char mask = BIT_N(row & 7); |
| int col; |
| for (col = 0; col < bm->width; col++, p++) |
| #if !defined(HAVE_LCD_COLOR) && \ |
| (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) |
| if (*qp++ < 128) |
| *p |= mask; |
| #else |
| if (brightness(*qp++) < 128) |
| *p |= mask; |
| #endif |
| } |
| #endif |
| } |
| #ifdef HAVE_LCD_COLOR |
| if (!ba.alpha_detected) |
| { /* if this has an alpha channel, totalsize accounts for it as well |
| * subtract if no actual alpha information was found */ |
| if (bm->alpha_offset > 0) |
| totalsize -= alphasize; |
| bm->alpha_offset = 0; |
| } |
| #endif |
| return totalsize; /* return the used buffer size. */ |
| } |