diff options
author | Sean Fox <dyntryx@gmail.com> | 2008-07-01 09:10:20 +0000 |
---|---|---|
committer | Sean Fox <dyntryx@gmail.com> | 2008-07-01 09:10:20 +0000 |
commit | 46ace4776ee4bdb421810c492641aea987289310 (patch) | |
tree | d801105afa6ed0ee5edcc13f1319ea22ae7f7d6f | |
parent | 099d5a5fbb93e3995b32245ccfb2d99e7996a045 (diff) | |
download | libnsbmp-46ace4776ee4bdb421810c492641aea987289310.tar.gz libnsbmp-46ace4776ee4bdb421810c492641aea987289310.tar.bz2 |
Changed several char/int types to proper types from <stdint.h>; corrected decoding errors for 32-bit and 16-bit bitmaps; more corrections to ico handling; added support for 'limited transparency'; added documentation to assist with future editing; cleaned up functions that read in data
svn path=/branches/dynis/libnsbmp/; revision=4484
-rw-r--r-- | examples/decode_bmp.c | 24 | ||||
-rw-r--r-- | examples/decode_ico.c | 57 | ||||
-rw-r--r-- | libnsbmp.c | 548 | ||||
-rw-r--r-- | libnsbmp.h | 45 |
4 files changed, 437 insertions, 237 deletions
diff --git a/examples/decode_bmp.c b/examples/decode_bmp.c index d838f79..e09f187 100644 --- a/examples/decode_bmp.c +++ b/examples/decode_bmp.c @@ -21,12 +21,14 @@ #include <errno.h> #include <stdbool.h> #include <stdlib.h> +#include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <libnsbmp.h> -#define BITMAP_BYTES_PER_PIXEL 4 +#define BYTES_PER_PIXEL 4 +#define TRANSPARENT_COLOR 0xffffffff unsigned char *load_file(const char *path, size_t *data_size); void warning(const char *context, bmp_result code); @@ -78,21 +80,21 @@ int main(int argc, char *argv[]) /* decode the image */ code = bmp_decode(&bmp); + /* code = bmp_decode_trans(&bmp, TRANSPARENT_COLOR); */ if (code != BMP_OK) { warning("bmp_decode", code); exit(1); } { - unsigned int row, col; - unsigned char *image; - image = (unsigned char *) bmp.bitmap; + uint16_t row, col; + uint8_t *image; + image = (uint8_t *) bmp.bitmap; for (row = 0; row != bmp.height; row++) { for (col = 0; col != bmp.width; col++) { - size_t z = (row * bmp.width + col) * BITMAP_BYTES_PER_PIXEL; - printf("%u %u %u ", - (unsigned char) image[z], - (unsigned char) image[z + 1], - (unsigned char) image[z + 2]); + size_t z = (row * bmp.width + col) * BYTES_PER_PIXEL; + printf("%u %u %u ", image[z], + image[z + 1], + image[z + 2]); } printf("\n"); } @@ -170,7 +172,7 @@ void warning(const char *context, bmp_result code) void *bitmap_create(int width, int height, unsigned int state) { (void) state; /* unused */ - return calloc(width * height, BITMAP_BYTES_PER_PIXEL); + return calloc(width * height, BYTES_PER_PIXEL); } @@ -200,7 +202,7 @@ unsigned char *bitmap_get_buffer(void *bitmap) size_t bitmap_get_bpp(void *bitmap) { (void) bitmap; /* unused */ - return BITMAP_BYTES_PER_PIXEL; + return BYTES_PER_PIXEL; } diff --git a/examples/decode_ico.c b/examples/decode_ico.c index 124aab3..a1cc181 100644 --- a/examples/decode_ico.c +++ b/examples/decode_ico.c @@ -21,12 +21,18 @@ #include <errno.h> #include <stdbool.h> #include <stdlib.h> +#include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <libnsbmp.h> -#define BITMAP_BYTES_PER_PIXEL 4 +/* Currently the library returns the data in RGBA format, + * so there are 4 bytes per pixel */ +#define BYTES_PER_PIXEL 4 + +/* White with alpha masking. */ +#define TRANSPARENT_COLOR 0xffffffff unsigned char *load_file(const char *path, size_t *data_size); void warning(const char *context, bmp_result code); @@ -48,15 +54,18 @@ int main(int argc, char *argv[]) bitmap_get_buffer, bitmap_get_bpp }; - bmp_result code; + uint16_t width, height; ico_collection ico; + bmp_result code; struct bmp_image *bmp; size_t size; - if (argc != 2) { - fprintf(stderr, "Usage: %s collection.ico\n", argv[0]); + if ((argc < 2) || (argc > 4)) { + fprintf(stderr, "Usage: %s collection.ico [width=255] [height=255]\n", argv[0]); return 1; } + width = (argc >= 3) ? atoi(argv[2]) : 255; + height = (argc == 4) ? atoi(argv[3]) : 255; /* create our bmp image */ ico_collection_create(&ico, &bitmap_callbacks); @@ -71,34 +80,38 @@ int main(int argc, char *argv[]) exit(1); } + /* decode the image */ + bmp = ico_find(&ico, width, height); + assert(bmp); + printf("P3\n"); printf("# %s\n", argv[1]); - printf("# width %u \n", ico.width); - printf("# height %u \n", ico.height); - printf("%u %u 256\n", ico.width, ico.height); + printf("# width %u \n", bmp->width); + printf("# height %u \n", bmp->height); + printf("%u %u 256\n", bmp->width, bmp->height); - /* decode the image */ - bmp = ico_find(&ico, 255, 255); - assert(bmp); - bmp_decode(bmp); + code = bmp_decode(bmp); + /* code = bmp_decode_trans(bmp, TRANSPARENT_COLOR); */ + if (code != BMP_OK) { + warning("bmp_decode", code); + exit(1); + } { - unsigned int row, col; - unsigned char *image; - image = (unsigned char *) bmp->bitmap; + uint16_t row, col; + uint8_t *image; + image = (uint8_t *) bmp->bitmap; for (row = 0; row != bmp->height; row++) { for (col = 0; col != bmp->width; col++) { - size_t z = (row * bmp->width + col) * BITMAP_BYTES_PER_PIXEL; - printf("%u %u %u ", - (unsigned char) image[z], - (unsigned char) image[z + 1], - (unsigned char) image[z + 2]); + size_t z = (row * bmp->width + col) * BYTES_PER_PIXEL; + printf("%u %u %u ", image[z], + image[z + 1], + image[z + 2]); } printf("\n"); } } /* clean up */ - bmp_finalise(bmp); ico_finalise(&ico); free(data); @@ -170,7 +183,7 @@ void warning(const char *context, bmp_result code) void *bitmap_create(int width, int height, unsigned int state) { (void) state; /* unused */ - return calloc(width * height, BITMAP_BYTES_PER_PIXEL); + return calloc(width * height, BYTES_PER_PIXEL); } @@ -200,7 +213,7 @@ unsigned char *bitmap_get_buffer(void *bitmap) size_t bitmap_get_bpp(void *bitmap) { (void) bitmap; /* unused */ - return BITMAP_BYTES_PER_PIXEL; + return BYTES_PER_PIXEL; } @@ -26,24 +26,103 @@ #include "libnsbmp.h" #include "utils/log.h" +/* The functions provided by this file allow for the decoding of + Microsoft's BMP and ICO image file formats. + + READING BMP FILES + ================= + + To begin decoding a BMP, the caller should initialise a + 'bmp_bitmap_callback_vt' structure with the appropriate values necessary + to handle bitmap images. Next, a 'bmp_image' structure should be + initialised by calling bmp_create(). This structure should then be + passed to bmp_analyse() along with the BMP data to process and the size + of this data. + + Once the analysis has begun, the decoder completes the width and height + variables. + + To decode the image, the caller must use bmp_decode() which selects the + proper decoding method based on the BMP info header and assigns the + decoded bitmap image to the 'bitmap' member of the 'bmp_image' + structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA + format. + + It should be noted that bmp_finalise() should always be called, even if + the image was never decoded. It is also the responsibility of the + caller to free 'bmp_data'. + + READING ICO FILES + ================= + + To begin decoding an ICO, the caller should initialise a + 'bmp_bitmap_callback_vt' structure with the appropriate values necessary + to handle bitmap images. Next, an 'ico_collection' structure should be + initialised by calling ico_create(). This structure should then be + passed to ico_analyse() along with the ICO data to process and the size + of this data. + + Once the analysis has begun, the decoder completes the width and height + variables. Because ICO collections contain multiple bitmap images, the + width and height will contain the values of the largest available image. + + The caller then obtains a BMP from the ICO collection by calling + ico_find() with the requested width and height. + + To decode the image, the caller must use bmp_decode() which selects the + proper decoding method based on the BMP info header and assigns the + decoded bitmap image to the 'bitmap' member of the 'bmp_image' + structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA + format. + + It should be noted that ico_finalise() should always be called, even if + no images were decoded. Because ico_finalise() calls bmp_finalise() for + each bitmap within the collection, the caller is not required to perform + this function. However, it is the responsibility of the caller to free + 'ico_data'. + + [dynis] - Tue 1st July 2008 +*/ + /* squashes unused variable compiler warnings */ #define UNUSED(x) ((x)=(x)) -static inline int read_short(unsigned char *data, unsigned int o) { - return (data[o] | ((unsigned char)data[o+1] << 8)); +/* BMP flags */ +#define BMP_FILE_HEADER_SIZE 14 +#define ICO_FILE_HEADER_SIZE 6 +#define ICO_DIR_ENTRY_SIZE 16 + +static inline int8_t read_int8(uint8_t *data, unsigned int o) { + return (int8_t) data[o]; +} + +static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { + return (uint8_t) data[o]; +} + +static inline int16_t read_int16(uint8_t *data, unsigned int o) { + return (int16_t) (data[o] | (data[o+1] << 8)); } -static inline int read_int(unsigned char *data, unsigned int o) { - return ((unsigned char)data[o] | ((unsigned char)data[o+1] << 8) | ((unsigned char)data[o+2] << 16) | ((unsigned char)data[o+3] << 24)); +static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { + return (uint16_t) (data[o] | (data[o+1] << 8)); +} + +static inline int32_t read_int32(uint8_t *data, unsigned int o) { + return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); +} + +static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { + return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); } static bmp_result next_ico_image(ico_collection *ico, ico_image *image); static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data); -static bmp_result bmp_decode_rgb24(bmp_image *bmp, unsigned char **start, int bytes); -static bmp_result bmp_decode_rgb16(bmp_image *bmp, unsigned char **start, int bytes); -static bmp_result bmp_decode_rgb(bmp_image *bmp, unsigned char **start, int bytes); -static bmp_result bmp_decode_mask(bmp_image *bmp, unsigned char *data, int bytes); -static bmp_result bmp_decode_rle(bmp_image *bmp, unsigned char *data, int bytes, int size); +static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes); +static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size); static void bmp_invalidate(void *bitmap, void *private_word); @@ -76,32 +155,34 @@ void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_c * \param bmp the BMP image to analyse * \return BMP_OK on success */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *data) { +bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *cdata) { + uint8_t *data = (uint8_t *)cdata; + /* ensure we aren't already initialised */ if (bmp->bitmap) return BMP_OK; - /* Initialize values - */ + /* initialize values */ bmp->buffer_size = size; bmp->bmp_data = data; /* standard 14-byte BMP file header is: * - * +0 SHORT 'BM' - * +2 INT size of file (in bytes) - * +6 SHORT reserved field (1) - * +8 SHORT reserved field (2) - * +10 INT starting position of image data (in bytes) + * +0 UINT16 File Type ('BM') + * +2 UINT32 Size of File (in bytes) + * +6 INT16 Reserved Field (1) + * +8 INT16 Reserved Field (2) + * +10 UINT32 Starting Position of Image Data (offset in bytes) */ - if (bmp->buffer_size < 14) + if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) return BMP_INSUFFICIENT_DATA; - if ((data[0] != 'B') || (data[1] != 'M')) + if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) return BMP_DATA_ERROR; - bmp->bitmap_offset = read_int(data, 10); + bmp->bitmap_offset = read_uint32(data, 10); + data += BMP_FILE_HEADER_SIZE; /* decode the BMP header */ - return bmp_analyse_header(bmp, data + 14); + return bmp_analyse_header(bmp, data); } @@ -116,8 +197,8 @@ bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *data) { * \param ico the ICO image to analyse * \return BMP_OK on success */ -bmp_result ico_analyse(ico_collection *ico, size_t size, unsigned char *data) { - unsigned int count, i; +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) { + uint16_t count, i; bmp_result result; int area, max_area = 0; @@ -125,44 +206,43 @@ bmp_result ico_analyse(ico_collection *ico, size_t size, unsigned char *data) { if (ico->first) return BMP_OK; - /* Initialize values - */ + /* initialize values */ ico->buffer_size = size; ico->ico_data = data; /* 6-byte ICO file header is: * - * +0 SHORT Reserved (should be 0) - * +2 SHORT Type (1 for ICO, 2 for CUR) - * +4 SHORT Number of BMPs to follow + * +0 INT16 Reserved (should be 0) + * +2 UINT16 Type (1 for ICO, 2 for CUR) + * +4 UINT16 Number of BMPs to follow */ - if (ico->buffer_size < 6) + if (ico->buffer_size < ICO_FILE_HEADER_SIZE) return BMP_INSUFFICIENT_DATA; - if (read_int(data, 0) != 0x00010000) +// if (read_int16(data, 2) != 0x0000) +// return BMP_DATA_ERROR; + if (read_uint16(data, 2) != 0x0001) return BMP_DATA_ERROR; - count = read_short(data, 4); + count = read_uint16(data, 4); if (count == 0) return BMP_DATA_ERROR; - data += 6; + data += ICO_FILE_HEADER_SIZE; - /* Check if we have enough data for the directory - * 6-byte header + 16-byte directory entry for each BMP - */ - if (ico->buffer_size < 6 + (16 * count)) + /* check if we have enough data for the directory */ + if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) return BMP_INSUFFICIENT_DATA; /* Decode the BMP files. * * 16-byte ICO directory entry is: * - * +0 CHAR Width (0 for 256 pixels) - * +1 CHAR Height (0 for 256 pixels) - * +2 CHAR Colour count (0 if more than 256 colours) - * +3 CHAR Reserved (should be 0, but may not be) - * +4 SHORT Colour Planes (should be 0 or 1) - * +6 SHORT Bits Per Pixel - * +8 INT Size of the bitmap data in bytes - * +12 INT Offset (bitmap data address in file) + * +0 UINT8 Width (0 for 256 pixels) + * +1 UINT8 Height (0 for 256 pixels) + * +2 UINT8 Colour count (0 if more than 256 colours) + * +3 INT8 Reserved (should be 0, but may not be) + * +4 UINT16 Colour Planes (should be 0 or 1) + * +6 UINT16 Bits Per Pixel + * +8 UINT32 Size of BMP info header + bitmap data in bytes + * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) */ for (i = 0; i < count; i++) { ico_image *image; @@ -172,19 +252,20 @@ bmp_result ico_analyse(ico_collection *ico, size_t size, unsigned char *data) { result = next_ico_image(ico, image); if (result != BMP_OK) return result; - image->bmp.width = (unsigned int)data[0]; + image->bmp.width = read_uint8(data, 0); if (image->bmp.width == 0) image->bmp.width = 256; - image->bmp.height = (unsigned int)data[1]; + image->bmp.height = read_uint8(data, 1); if (image->bmp.height == 0) image->bmp.height = 256; - image->bmp.buffer_size = read_int(data, 8) + 40; - image->bmp.bmp_data = ico->ico_data + read_int(data, 12); + image->bmp.buffer_size = read_uint32(data, 8); + image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); image->bmp.ico = true; - data += 16; + data += ICO_DIR_ENTRY_SIZE; result = bmp_analyse_header(&image->bmp, image->bmp.bmp_data); if (result != BMP_OK) return result; + /* adjust the size based on the images available */ area = image->bmp.width * image->bmp.height; if (area > max_area) { ico->width = image->bmp.width; @@ -212,44 +293,58 @@ static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { } -static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { - unsigned int header_size; - unsigned int i; - int width, height, j; - int palette_size; +static bmp_result bmp_analyse_header(bmp_image *bmp, uint8_t *data) { + uint32_t header_size; + uint32_t i; + uint8_t j; + int32_t width, height; + uint8_t palette_size; unsigned int flags; /* a variety of different bitmap headers can follow, depending * on the BMP variant. A full description of the various headers * can be found at http://www.fileformat.info/format/bmp/ */ - header_size = read_int(data, 0); + header_size = read_uint32(data, 0); if (bmp->buffer_size < (14 + header_size)) return BMP_INSUFFICIENT_DATA; if (header_size == 12) { /* the following header is for os/2 and windows 2.x and consists of: * - * +0 INT size of this header (in bytes) - * +4 SHORT image width (in pixels) - * +6 SHORT image height (in pixels) - * +8 SHORT number of color planes (always 1) - * +10 SHORT number of bits per pixel + * +0 UINT32 size of this header (in bytes) + * +4 INT16 image width (in pixels) + * +6 INT16 image height (in pixels) + * +8 UINT16 number of colour planes (always 1) + * +10 UINT16 number of bits per pixel */ - if (!bmp->ico) { - width = read_short(data, 4); - height = read_short(data, 6); - if (width < 0) + width = read_int16(data, 4); + height = read_int16(data, 6); + if (width < 0) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - height = -height; + } else { + bmp->width = width; + bmp->height = height / 2; } + } else { bmp->width = width; bmp->height = height; } - if (read_short(data, 8) != 1) + if (read_uint16(data, 8) != 1) return BMP_DATA_ERROR; - bmp->bpp = read_short(data, 10); + bmp->bpp = read_uint16(data, 10); bmp->colours = (1 << bmp->bpp); palette_size = 3; } else if (header_size < 40) { @@ -258,55 +353,72 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { /* the following header is for windows 3.x and onwards. it is a * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. * - * +0 INT size of this header (in bytes) - * +4 INT image width (in pixels) - * +8 INT image height (in pixels) - * +12 SHORT number of color planes (always 1) - * +14 SHORT number of bits per pixel - * +16 INT compression methods used - * +20 INT size of bitmap (in bytes) - * +24 INT horizontal resolution (in pixels per meter) - * +28 INT vertical resolution (in pixels per meter) - * +32 INT number of colors in the image - * +36 INT number of important colors - * +40 INT mask identifying bits of red component - * +44 INT mask identifying bits of green component - * +48 INT mask identifying bits of blue component - * +52 INT mask identifying bits of alpha component - * +56 INT color space type - * +60 INT x coordinate of red endpoint - * +64 INT y coordinate of red endpoint - * +68 INT z coordinate of red endpoint - * +72 INT x coordinate of green endpoint - * +76 INT y coordinate of green endpoint - * +80 INT z coordinate of green endpoint - * +84 INT x coordinate of blue endpoint - * +88 INT y coordinate of blue endpoint - * +92 INT z coordinate of blue endpoint - * +96 INT gamma red coordinate scale value - * +100 INT gamma green coordinate scale value - * +104 INT gamma blue coordinate scale value + * +0 UINT32 size of this header (in bytes) + * +4 INT32 image width (in pixels) + * +8 INT32 image height (in pixels) + * +12 UINT16 number of colour planes (always 1) + * +14 UINT16 number of bits per pixel + * +16 UINT32 compression methods used + * +20 UINT32 size of bitmap (in bytes) + * +24 UINT32 horizontal resolution (in pixels per meter) + * +28 UINT32 vertical resolution (in pixels per meter) + * +32 UINT32 number of colours in the image + * +36 UINT32 number of important colours + * +40 UINT32 mask identifying bits of red component + * +44 UINT32 mask identifying bits of green component + * +48 UINT32 mask identifying bits of blue component + * +52 UINT32 mask identifying bits of alpha component + * +56 UINT32 color space type + * +60 UINT32 x coordinate of red endpoint + * +64 UINT32 y coordinate of red endpoint + * +68 UINT32 z coordinate of red endpoint + * +72 UINT32 x coordinate of green endpoint + * +76 UINT32 y coordinate of green endpoint + * +80 UINT32 z coordinate of green endpoint + * +84 UINT32 x coordinate of blue endpoint + * +88 UINT32 y coordinate of blue endpoint + * +92 UINT32 z coordinate of blue endpoint + * +96 UINT32 gamma red coordinate scale value + * +100 UINT32 gamma green coordinate scale value + * +104 UINT32 gamma blue coordinate scale value */ - if (!bmp->ico) { - width = read_int(data, 4); - height = read_int(data, 8); - if (width < 0) + width = read_int32(data, 4); + height = read_int32(data, 8); + if (width < 0) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - height = -height; + } else { + bmp->width = width; + bmp->height = height / 2; } + } else { bmp->width = width; bmp->height = height; } - if (read_short(data, 12) != 1) + if (read_uint16(data, 12) != 1) return BMP_DATA_ERROR; - bmp->bpp = read_short(data, 14); + bmp->bpp = read_uint16(data, 14); if (bmp->bpp == 0) bmp->bpp = 8; - bmp->encoding = read_int(data, 16); + bmp->encoding = read_uint32(data, 16); if (bmp->encoding > BMP_ENCODING_BITFIELDS) return BMP_DATA_ERROR; + /* Bitfield encoding means we have red, green, blue, and alpha masks. + * Here we aquire the masks and determine the required bit shift to + * align them in our 24-bit color 8-bit alpha format. + */ if (bmp->encoding == BMP_ENCODING_BITFIELDS) { if ((bmp->bpp != 16) && (bmp->bpp != 32)) return BMP_DATA_ERROR; @@ -315,10 +427,10 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { if (bmp->buffer_size < (14 + header_size)) return BMP_INSUFFICIENT_DATA; for (i = 0; i < 3; i++) - bmp->mask[i] = read_int(data, 40 + (i << 2)); + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); } else { for (i = 0; i < 4; i++) - bmp->mask[i] = read_int(data, 40 + (i << 2)); + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); } for (i = 0; i < 4; i++) { if (bmp->mask[i] == 0) @@ -334,13 +446,19 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { } } } - bmp->colours = read_int(data, 32); + bmp->colours = read_uint32(data, 32); if (bmp->colours == 0) bmp->colours = (1 << bmp->bpp); palette_size = 4; } data += header_size; + /* if there's no alpha mask, flag the bmp opaque */ + if ((!bmp->ico) && (bmp->mask[3] == 0)) { + flags |= BMP_OPAQUE; + bmp->opaque = true; + } + /* we only have a palette for <16bpp */ if (bmp->bpp < 16) { /* we now have a series of palette entries of the format: @@ -354,19 +472,19 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { */ if (bmp->buffer_size < (14 + header_size + (4 * bmp->colours))) return BMP_INSUFFICIENT_DATA; - bmp->colour_table = (unsigned int *)malloc(bmp->colours * sizeof(int)); + bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); if (!bmp->colour_table) return BMP_INSUFFICIENT_MEMORY; for (i = 0; i < bmp->colours; i++) { bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); + if (bmp->opaque) + bmp->colour_table[i] |= (0xff << 24); data += palette_size; } } /* create our bitmap */ - flags = BMP_NEW | BMP_CLEAR_MEMORY; - if ((!bmp->ico) && (bmp->mask[3] == 0)) - flags |= BMP_OPAQUE; + flags |= BMP_NEW | BMP_CLEAR_MEMORY; bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); if (!bmp->bitmap) { if (bmp->colour_table) @@ -374,7 +492,11 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { bmp->colour_table = NULL; return BMP_INSUFFICIENT_MEMORY; } - bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; + /* BMPs within ICOs don't have BMP file headers, so the image data should + * always be right after the colour table. + */ + if (bmp->ico) + bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; bmp->bitmap_callbacks.bitmap_set_suspendable(bmp->bitmap, bmp, bmp_invalidate); return BMP_OK; } @@ -387,16 +509,20 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data) { * as possible from the images in the collection. * * \param ico the ICO collection to examine - * \param width the preferred width - * \param height the preferred height + * \param width the preferred width (0 to use ICO header width) + * \param height the preferred height (0 to use ICO header height) */ -bmp_image *ico_find(ico_collection *ico, int width, int height) { +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) { bmp_image *bmp = NULL; ico_image *image; int x, y, cur, distance = (1 << 24); + if (width == 0) + width = ico->width; + if (height == 0) + height = ico->height; for (image = ico->first; image; image = image->next) { - if (((int)image->bmp.width == width) && ((int)image->bmp.height == height)) + if ((image->bmp.width == width) && (image->bmp.height == height)) return &image->bmp; x = image->bmp.width - width; y = image->bmp.height - height; @@ -437,8 +563,8 @@ static void bmp_invalidate(void *bitmap, void *private_word) { * \return BMP_OK on success */ bmp_result bmp_decode(bmp_image *bmp) { - unsigned char *data; - int bytes; + uint8_t *data; + uint32_t bytes; bmp_result result = BMP_OK; assert(bmp->bitmap); @@ -473,10 +599,32 @@ bmp_result bmp_decode(bmp_image *bmp) { if ((!bmp->ico) || (result != BMP_OK)) return result; - bytes = (intptr_t)bmp->bmp_data + bmp->buffer_size - (intptr_t)data; + bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; return bmp_decode_mask(bmp, data, bytes); } +/** + * Decode a BMP using "limited transparency" + * + * Bitmaps do not have native transparency support. However, there is a + * "trick" that is used in some instances in which the first pixel of the + * bitmap becomes the "transparency index". The decoding application can + * replace this index with whatever background colour it chooses to + * create the illusion of transparency. + * + * When to use transparency is at the discretion of the decoding + * application. + * + * \param bmp the BMP image to decode + * \param colour the colour to use as "transparent" + * \return BMP_OK on success + */ +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) { + bmp->limited_trans = true; + bmp->trans_colour = colour; + return bmp_decode(bmp); +} + /** * Decode BMP data stored in 24bpp colour. @@ -486,12 +634,14 @@ bmp_result bmp_decode(bmp_image *bmp) { * \param bytes the number of bytes of data available * \return BMP_OK on success */ -static bmp_result bmp_decode_rgb24(bmp_image *bmp, unsigned char **start, int bytes) { - unsigned char *top, *bottom, *end, *data; - unsigned int *scanline; - unsigned int x, y, swidth, skip; +static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y; + uint32_t swidth, skip; intptr_t addr; - unsigned int i, word; + uint8_t i; + uint32_t word; data = *start; swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; @@ -504,32 +654,41 @@ static bmp_result bmp_decode_rgb24(bmp_image *bmp, unsigned char **start, int by skip = bmp->bpp >> 3; bmp->decoded = true; + /* Determine transparent index */ + if (bmp->limited_trans) { + if (bmp->encoding == BMP_ENCODING_BITFIELDS) + bmp->transparent_index = read_uint32(data, 0); + else + bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16) | (data[3] << 24); + } + for (y = 0; y < bmp->height; y++) { while (addr != (((intptr_t)data) & 3)) data++; - if ((data + (skip * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; if (bmp->reversed) - scanline = (unsigned int *)(top + (y * swidth)); + scanline = (uint32_t *)(top + (y * swidth)); else - scanline = (unsigned int *)(bottom - (y * swidth)); + scanline = (uint32_t *)(bottom - (y * swidth)); if (bmp->encoding == BMP_ENCODING_BITFIELDS) { for (x = 0; x < bmp->width; x++) { - word = data[0] | (data[1] << 8) | (data[2] << 16) | - (data[3] << 24); - scanline[x] = (0xff << 24); + word = read_uint32(data, 0); for (i = 0; i < 4; i++) if (bmp->shift[i] > 0) - scanline[x] ^= ((word & bmp->mask[i]) << - bmp->shift[i]); + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); else - scanline[x] ^= ((word & bmp->mask[i]) >> - (-bmp->shift[i])); + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ + if (bmp->opaque) + scanline[x] |= (0xff << 24); data += 4; } } else { for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16) | (0xff << 24); + scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + if (bmp->opaque) + scanline[x] |= (0xff << 24); data += skip; } } @@ -547,12 +706,13 @@ static bmp_result bmp_decode_rgb24(bmp_image *bmp, unsigned char **start, int by * \param bytes the number of bytes of data available * \return BMP_OK on success */ -static bmp_result bmp_decode_rgb16(bmp_image *bmp, unsigned char **start, int bytes) { - unsigned char *top, *bottom, *end, *data; - unsigned int *scanline; - unsigned int x, y, swidth; +static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y, swidth; intptr_t addr; - unsigned int word, i; + uint8_t i; + uint16_t word; data = *start; swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; @@ -564,35 +724,49 @@ static bmp_result bmp_decode_rgb16(bmp_image *bmp, unsigned char **start, int by addr = ((intptr_t)data) & 3; bmp->decoded = true; + /* Determine transparent index */ + if (bmp->limited_trans) + bmp->transparent_index = read_uint16(data, 0); + for (y = 0; y < bmp->height; y++) { if (addr != (((intptr_t)data) & 3)) data += 2; if ((data + (2 * bmp->width)) > end) return BMP_INSUFFICIENT_DATA; if (bmp->reversed) - scanline = (unsigned int *)(top + (y * swidth)); + scanline = (uint32_t *)(top + (y * swidth)); else - scanline = (unsigned int *)(bottom - (y * swidth)); + scanline = (uint32_t *)(bottom - (y * swidth)); if (bmp->encoding == BMP_ENCODING_BITFIELDS) { for (x = 0; x < bmp->width; x++) { - word = data[0] | (data[1] << 8); - scanline[x] = (0xff << 24); - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] ^= ((word & bmp->mask[i]) << - bmp->shift[i]); - else - scanline[x] ^= ((word & bmp->mask[i]) >> - (-bmp->shift[i])); + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + scanline[x] = 0; + for (i = 0; i < 4; i++) + if (bmp->shift[i] > 0) + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); + else + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + if (bmp->opaque) + scanline[x] |= (0xff << 24); + } data += 2; } } else { for (x = 0; x < bmp->width; x++) { - word = data[0] | (data[1] << 8) | (0xff << 16) | (0xff << 24); - scanline[x] = ((word & (31 << 0)) << 19) | - ((word & (31 << 5)) << 6) | - ((word & (31 << 10)) >> 7) | - (word & (0xff << 24)); + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + /* 16-bit RGB defaults to RGB555 */ + scanline[x] = ((word & (31 << 0)) << 19) | + ((word & (31 << 5)) << 6) | + ((word & (31 << 10)) >> 7); + } + if (bmp->opaque) + scanline[x] |= (0xff << 24); data += 2; } } @@ -610,16 +784,15 @@ static bmp_result bmp_decode_rgb16(bmp_image *bmp, unsigned char **start, int by * \param bytes the number of bytes of data available * \return BMP_OK on success */ -static bmp_result bmp_decode_rgb(bmp_image *bmp, unsigned char **start, int bytes) { - unsigned char *top, *bottom, *end, *data; - unsigned int *scanline; +static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; intptr_t addr; - unsigned int x, y, swidth; - int i; - int bit_shifts[8]; - int ppb = 8 / bmp->bpp; - int bit_mask = (1 << bmp->bpp) - 1; - int cur_byte = 0, bit; + uint32_t x, y, swidth; + uint8_t bit_shifts[8]; + uint8_t ppb = 8 / bmp->bpp; + uint8_t bit_mask = (1 << bmp->bpp) - 1; + uint8_t cur_byte = 0, bit, i; for (i = 0; i < ppb; i++) bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); @@ -634,10 +807,14 @@ static bmp_result bmp_decode_rgb(bmp_image *bmp, unsigned char **start, int byte addr = ((intptr_t)data) & 3; bmp->decoded = true; + /* Determine transparent index */ + if (bmp->limited_trans) + bmp->transparent_index = bmp->colour_table[(*data >> bit_shifts[0]) & bit_mask]; + for (y = 0; y < bmp->height; y++) { while (addr != (((intptr_t)data) & 3)) data++; - bit = 32; + bit = 8; if ((data + (bmp->width / ppb)) > end) return BMP_INSUFFICIENT_DATA; if (bmp->reversed) @@ -649,8 +826,9 @@ static bmp_result bmp_decode_rgb(bmp_image *bmp, unsigned char **start, int byte bit = 0; cur_byte = *data++; } - scanline[x] = bmp->colour_table[(cur_byte >> - bit_shifts[bit++]) & bit_mask]; + scanline[x] = bmp->colour_table[(cur_byte >> bit_shifts[bit++]) & bit_mask]; + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; } } *start = data; @@ -666,12 +844,12 @@ static bmp_result bmp_decode_rgb(bmp_image *bmp, unsigned char **start, int byte * \param bytes the number of bytes of data available * \return BMP_OK on success */ -static bmp_result bmp_decode_mask(bmp_image *bmp, unsigned char *data, int bytes) { - unsigned char *top, *bottom, *end; - unsigned int *scanline; +static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) { + uint8_t *top, *bottom, *end; + uint32_t *scanline; intptr_t addr; - unsigned int x, y, swidth; - int cur_byte = 0; + uint32_t x, y, swidth; + uint32_t cur_byte = 0; swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); @@ -686,7 +864,7 @@ static bmp_result bmp_decode_mask(bmp_image *bmp, unsigned char *data, int bytes data++; if ((data + (bmp->width >> 3)) > end) return BMP_INSUFFICIENT_DATA; - scanline = (unsigned int *)(bottom - (y * swidth)); + scanline = (uint32_t *)(bottom - (y * swidth)); for (x = 0; x < bmp->width; x++) { if ((x & 7) == 0) cur_byte = *data++; @@ -708,13 +886,13 @@ static bmp_result bmp_decode_mask(bmp_image *bmp, unsigned char *data, int bytes * \param size the size of the RLE tokens (4 or 8) * \return BMP_OK on success */ -static bmp_result bmp_decode_rle(bmp_image *bmp, unsigned char *data, int bytes, int size) { - unsigned char *top, *bottom, *end; - unsigned int *scanline; - unsigned int swidth; - int i, length, pixels_left; - unsigned int x = 0, y = 0, last_y = 0; - unsigned int pixel = 0, pixel2; +static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size) { + uint8_t *top, *bottom, *end; + uint32_t *scanline; + uint32_t swidth; + uint32_t i, length, pixels_left; + uint32_t x = 0, y = 0, last_y = 0; + uint32_t pixel = 0, pixel2; if (bmp->ico) return BMP_DATA_ERROR; @@ -25,10 +25,11 @@ #define _NETSURF_IMAGE_BMPREAD_H_ #include <stdbool.h> +#include <stdint.h> /* bmp flags */ -#define BMP_NEW 0 -#define BMP_OPAQUE (1 << 0) /** image is opaque */ +#define BMP_NEW 0 +#define BMP_OPAQUE (1 << 0) /** image is opaque (as opposed to having an alpha mask) */ #define BMP_CLEAR_MEMORY (1 << 1) /** memory should be wiped */ /* error return values */ @@ -68,23 +69,28 @@ typedef struct bmp_bitmap_callback_vt_s { typedef struct bmp_image { bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ - unsigned int width; /** width of BMP (valid after _analyse) */ - unsigned int height; /** heigth of BMP (valid after _analyse) */ + uint16_t width; /** width of BMP (valid after _analyse) */ + uint16_t height; /** heigth of BMP (valid after _analyse) */ bool decoded; /** whether the image has been decoded */ void *bitmap; /** decoded image */ + uint8_t *bmp_data; /** pointer to BMP data */ /** Internal members are listed below */ - unsigned char *bmp_data; /** pointer to BMP data */ - unsigned int buffer_size; /** total number of bytes of BMP data available */ + uint32_t buffer_size; /** total number of bytes of BMP data available */ bmp_encoding encoding; /** pixel encoding type */ - unsigned int bitmap_offset; /** offset of bitmap data */ - unsigned int bpp; /** bits per pixel */ - unsigned int colours; /** number of colours */ - unsigned int *colour_table; /** colour table */ + uint32_t bitmap_offset; /** offset of bitmap data */ + uint16_t bpp; /** bits per pixel */ + uint32_t colours; /** number of colours */ + uint32_t *colour_table; /** colour table */ + bool limited_trans; /** whether to use bmp's limited transparency */ + uint32_t trans_colour; /** colour to display for "transparent" pixels when + * using limited transparency */ bool reversed; /** scanlines are top to bottom */ bool ico; /** image is part of an ICO, mask follows */ - unsigned int mask[4]; /** four bitwise mask */ - int shift[4]; /** four bitwise shifts */ + bool opaque; /** true if the bitmap does not contain an alpha channel */ + uint32_t mask[4]; /** four bitwise mask */ + int32_t shift[4]; /** four bitwise shifts */ + uint32_t transparent_index; /** colour representing "transparency" in the bitmap */ } bmp_image; typedef struct ico_image { @@ -94,23 +100,24 @@ typedef struct ico_image { typedef struct ico_collection { bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ - unsigned int width; /** width of largest BMP */ - unsigned int height; /** heigth of largest BMP */ + uint16_t width; /** width of largest BMP */ + uint16_t height; /** heigth of largest BMP */ /** Internal members are listed below */ - unsigned char *ico_data; /** pointer to ICO data */ - unsigned int buffer_size; /** total number of bytes of ICO data available */ + uint8_t *ico_data; /** pointer to ICO data */ + uint32_t buffer_size; /** total number of bytes of ICO data available */ ico_image *first; } ico_collection; void bmp_create(bmp_image *gif, bmp_bitmap_callback_vt *bitmap_callbacks); void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_callbacks); -bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *data); +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); bmp_result bmp_decode(bmp_image *bmp); +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); void bmp_finalise(bmp_image *bmp); -bmp_result ico_analyse(ico_collection *ico, size_t size, unsigned char *data); -bmp_image *ico_find(ico_collection *ico, int width, int height); +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); void ico_finalise(ico_collection *ico); #endif |