forked from len0rd/rockbox
		
	
		
			
				
	
	
		
			938 lines
		
	
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			938 lines
		
	
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   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++) {
 | |
|                     (void) delta;
 | |
|                     if (ctx->dither)
 | |
|                         delta = DITHERXDY(col,dy);
 | |
|                     r = qp->red;
 | |
|                     g = qp->green;
 | |
|                     b = qp->blue;
 | |
| #if LCD_DEPTH < 24
 | |
|                     r = (31 * r + (r >> 3) + delta) >> 8;
 | |
|                     g = (63 * g + (g >> 2) + delta) >> 8;
 | |
|                     b = (31 * b + (b >> 3) + delta) >> 8;
 | |
| #endif
 | |
|                     *dest = FB_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, hdr_size;
 | |
|     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
 | |
| 
 | |
|     bool remote = false;
 | |
| #ifdef HAVE_REMOTE_LCD
 | |
|     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
 | |
|         remote = false;
 | |
|     }
 | |
| #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);
 | |
|         if (!remote)
 | |
|             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;
 | |
|     }
 | |
| 
 | |
|     hdr_size = letoh32(bmph.struct_size);
 | |
|     compression = letoh32(bmph.compression);
 | |
|     if (depth <= 8) {
 | |
|         numcolors = letoh32(bmph.clr_used);
 | |
|         if (numcolors == 0)
 | |
|             numcolors = BIT_N(depth);
 | |
|         /* forward to the color table */
 | |
|         lseek(fd, 14+hdr_size, SEEK_SET);
 | |
|     } else {
 | |
|         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 */
 | |
|         if (!remote)
 | |
|             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;
 | |
|     }
 | |
| 
 | |
| #if LCD_DEPTH >= 24
 | |
|     /* Never dither 24/32 bit BMP to 24 bit LCDs */
 | |
|     if (depth >= 24 && !remote)
 | |
|         dither = false;
 | |
| #endif
 | |
| 
 | |
|     /* 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. */
 | |
| }
 |