summaryrefslogtreecommitdiff
path: root/src/libnsbmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnsbmp.c')
-rw-r--r--src/libnsbmp.c1898
1 files changed, 949 insertions, 949 deletions
diff --git a/src/libnsbmp.c b/src/libnsbmp.c
index 4e31b93..5ce6a7b 100644
--- a/src/libnsbmp.c
+++ b/src/libnsbmp.c
@@ -10,6 +10,17 @@
/**
* \file
* BMP decoding implementation
+ *
+ * This library decode windows bitmaps and icons from their disc images.
+ *
+ * The image format is described in several documents:
+ * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx
+ * http://www.fileformat.info/format/bmp/egff.htm
+ * https://en.wikipedia.org/wiki/BMP_file_format
+ *
+ * Despite the format being clearly defined many bitmaps found on the web are
+ * not compliant and this implementation attempts to cope with as many issues
+ * as possible rather than simply failing.
*/
#include <assert.h>
@@ -33,458 +44,270 @@
#ifdef WE_NEED_INT8_READING_NOW
static inline int8_t read_int8(uint8_t *data, unsigned int o) {
- return (int8_t) data[o];
+ return (int8_t) data[o];
}
#endif
static inline uint8_t read_uint8(uint8_t *data, unsigned int o) {
- return (uint8_t) data[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));
+ return (int16_t) (data[o] | (data[o+1] << 8));
}
static inline uint16_t read_uint16(uint8_t *data, unsigned int o) {
- return (uint16_t) (data[o] | (data[o+1] << 8));
+ 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));
+ 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, 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);
-
-
-
-/** Initialises necessary bmp_image members.
-*/
-void bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *bitmap_callbacks) {
- memset(bmp, 0, sizeof(bmp_image));
- bmp->bitmap_callbacks = *bitmap_callbacks;
-}
-
-
-/** Initialises necessary ico_collection members.
-*/
-void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_callbacks) {
- memset(ico, 0, sizeof(ico_collection));
- ico->bitmap_callbacks = *bitmap_callbacks;
-}
-
-
-/**
- * Analyse a BMP prior to decoding.
- *
- * This function will scan the data provided and perform simple checks to
- * ensure the data is a valid BMP.
- *
- * This function must be called before bmp_decode() and sets up all the
- * relevant values in the bmp structure.
- *
- * \param bmp the BMP image to analyse
- * \return BMP_OK on success
- */
-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 */
- bmp->buffer_size = size;
- bmp->bmp_data = data;
-
- /* standard 14-byte BMP file header is:
- *
- * +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 < BMP_FILE_HEADER_SIZE)
- return BMP_INSUFFICIENT_DATA;
- if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M'))
- return BMP_DATA_ERROR;
- bmp->bitmap_offset = read_uint32(data, 10);
- data += BMP_FILE_HEADER_SIZE;
-
- /* boundary checking */
- if (bmp->bitmap_offset >= size)
- return BMP_INSUFFICIENT_DATA;
-
- /* decode the BMP header */
- return bmp_analyse_header(bmp, data);
-}
-
-
-/**
- * Analyse an ICO prior to decoding.
- *
- * This function will scan the data provided and perform simple checks to
- * ensure the data is a valid ICO.
- *
- * This function must be called before ico_find().
- *
- * \param ico the ICO image to analyse
- * \return BMP_OK on success
- */
-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;
-
- /* ensure we aren't already initialised */
- if (ico->first)
- return BMP_OK;
-
- /* initialize values */
- ico->buffer_size = size;
- ico->ico_data = data;
-
- /* 6-byte ICO file header is:
- *
- * +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 < ICO_FILE_HEADER_SIZE)
- return BMP_INSUFFICIENT_DATA;
-// if (read_int16(data, 2) != 0x0000)
-// return BMP_DATA_ERROR;
- if (read_uint16(data, 2) != 0x0001)
- return BMP_DATA_ERROR;
- count = read_uint16(data, 4);
- if (count == 0)
- return BMP_DATA_ERROR;
- data += ICO_FILE_HEADER_SIZE;
-
- /* 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 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;
- image = calloc(1, sizeof(ico_image));
- if (!image)
- return BMP_INSUFFICIENT_MEMORY;
- result = next_ico_image(ico, image);
- if (result != BMP_OK)
- return result;
- image->bmp.width = read_uint8(data, 0);
- if (image->bmp.width == 0)
- image->bmp.width = 256;
- image->bmp.height = read_uint8(data, 1);
- if (image->bmp.height == 0)
- image->bmp.height = 256;
- image->bmp.buffer_size = read_uint32(data, 8);
- image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12);
- image->bmp.ico = true;
- data += ICO_DIR_ENTRY_SIZE;
-
- /* Ensure that the bitmap data resides in the buffer */
- if (image->bmp.bmp_data - ico->ico_data >= 0 &&
- (uint32_t)(image->bmp.bmp_data -
- ico->ico_data) >= ico->buffer_size)
- return BMP_DATA_ERROR;
-
- /* Ensure that we have sufficient data to read the bitmap */
- if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >=
- ico->buffer_size - (ico->ico_data - data))
- return BMP_INSUFFICIENT_DATA;
-
- 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;
- ico->height = image->bmp.height;
- max_area = area;
- }
- }
- return BMP_OK;
+ return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24));
}
/**
- * Allocates memory for the next BMP in an ICO collection
- *
- * Sets proper structure values
- *
- * \param ico the ICO collection to add the image to
- * \param image a pointer to the ICO image to be initialised
+ * Parse the bitmap info header
*/
-static bmp_result next_ico_image(ico_collection *ico, ico_image *image) {
- bmp_create(&image->bmp, &ico->bitmap_callbacks);
- image->next = ico->first;
- ico->first = image;
- return BMP_OK;
-}
-
+static bmp_result bmp_info_header_parse(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 = 0;
+
+ /* 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://msdn.microsoft.com/en-us/library/ms532301(VS.85).aspx
+ */
+ 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 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
+ */
+ width = read_int16(data, 4);
+ height = read_int16(data, 6);
+ if ((width <= 0) || (height == 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;
+ } else {
+ bmp->width = width;
+ bmp->height = height / 2;
+ }
+ } else {
+ bmp->width = width;
+ bmp->height = height;
+ }
+ if (read_uint16(data, 8) != 1)
+ return BMP_DATA_ERROR;
+ bmp->bpp = read_uint16(data, 10);
+ /**
+ * The bpp value should be in the range 1-32, but the only
+ * values considered legal are:
+ * RGB ENCODING: 1, 4, 8, 16, 24 and 32
+ */
+ if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
+ (bmp->bpp != 8) &&
+ (bmp->bpp != 16) &&
+ (bmp->bpp != 24) &&
+ (bmp->bpp != 32))
+ return BMP_DATA_ERROR;
+ bmp->colours = (1 << bmp->bpp);
+ palette_size = 3;
+ } else if (header_size < 40) {
+ return BMP_DATA_ERROR;
+ } else {
+ /* 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 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
+ */
+ width = read_int32(data, 4);
+ height = read_int32(data, 8);
+ if ((width <= 0) || (height == 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;
+ } else {
+ bmp->width = width;
+ bmp->height = height / 2;
+ }
+ } else {
+ bmp->width = width;
+ bmp->height = height;
+ }
+ if (read_uint16(data, 12) != 1)
+ return BMP_DATA_ERROR;
+ bmp->bpp = read_uint16(data, 14);
+ if (bmp->bpp == 0)
+ bmp->bpp = 8;
+ bmp->encoding = read_uint32(data, 16);
+ /**
+ * The bpp value should be in the range 1-32, but the only
+ * values considered legal are:
+ * RGB ENCODING: 1, 4, 8, 16, 24 and 32
+ * RLE4 ENCODING: 4
+ * RLE8 ENCODING: 8
+ * BITFIELD ENCODING: 16 and 32
+ */
+ switch (bmp->encoding) {
+ case BMP_ENCODING_RGB:
+ if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
+ (bmp->bpp != 8) &&
+ (bmp->bpp != 16) &&
+ (bmp->bpp != 24) &&
+ (bmp->bpp != 32))
+ return BMP_DATA_ERROR;
+ break;
+ case BMP_ENCODING_RLE8:
+ if (bmp->bpp != 8)
+ return BMP_DATA_ERROR;
+ break;
+ case BMP_ENCODING_RLE4:
+ if (bmp->bpp != 4)
+ return BMP_DATA_ERROR;
+ break;
+ case BMP_ENCODING_BITFIELDS:
+ if ((bmp->bpp != 16) && (bmp->bpp != 32))
+ return BMP_DATA_ERROR;
+ break;
+ /* invalid encoding */
+ default:
+ return BMP_DATA_ERROR;
+ break;
+ }
+ /* Bitfield encoding means we have red, green, blue, and alpha masks.
+ * Here we acquire 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 (header_size == 40) {
+ header_size += 12;
+ if (bmp->buffer_size < (14 + header_size))
+ return BMP_INSUFFICIENT_DATA;
+ for (i = 0; i < 3; i++)
+ bmp->mask[i] = read_uint32(data, 40 + (i << 2));
+ } else {
+ for (i = 0; i < 4; i++)
+ bmp->mask[i] = read_uint32(data, 40 + (i << 2));
+ }
+ for (i = 0; i < 4; i++) {
+ if (bmp->mask[i] == 0)
+ break;
+ for (j = 31; j > 0; j--)
+ if (bmp->mask[i] & (1 << j)) {
+ if ((j - 7) > 0)
+ bmp->mask[i] &= 0xff << (j - 7);
+ else
+ bmp->mask[i] &= 0xff >> (-(j - 7));
+ bmp->shift[i] = (i << 3) - (j - 7);
+ break;
+ }
+ }
+ }
+ 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:
+ *
+ * +0 BYTE blue
+ * +1 BYTE green
+ * +2 BYTE red
+ *
+ * if the palette is from an OS/2 or Win2.x file then the entries
+ * are padded with an extra byte.
+ */
-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 = 0;
-
- /* 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://msdn.microsoft.com/en-us/library/ms532301(VS.85).aspx
- */
- 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 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
- */
- width = read_int16(data, 4);
- height = read_int16(data, 6);
- if ((width <= 0) || (height == 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;
- } else {
- bmp->width = width;
- bmp->height = height / 2;
- }
- } else {
- bmp->width = width;
- bmp->height = height;
- }
- if (read_uint16(data, 8) != 1)
- return BMP_DATA_ERROR;
- bmp->bpp = read_uint16(data, 10);
- /**
- * The bpp value should be in the range 1-32, but the only
- * values considered legal are:
- * RGB ENCODING: 1, 4, 8, 16, 24 and 32
- */
- if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
- (bmp->bpp != 8) &&
- (bmp->bpp != 16) &&
- (bmp->bpp != 24) &&
- (bmp->bpp != 32))
- return BMP_DATA_ERROR;
- bmp->colours = (1 << bmp->bpp);
- palette_size = 3;
- } else if (header_size < 40) {
- return BMP_DATA_ERROR;
- } else {
- /* 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 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
- */
- width = read_int32(data, 4);
- height = read_int32(data, 8);
- if ((width <= 0) || (height == 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;
- } else {
- bmp->width = width;
- bmp->height = height / 2;
- }
- } else {
- bmp->width = width;
- bmp->height = height;
- }
- if (read_uint16(data, 12) != 1)
- return BMP_DATA_ERROR;
- bmp->bpp = read_uint16(data, 14);
- if (bmp->bpp == 0)
- bmp->bpp = 8;
- bmp->encoding = read_uint32(data, 16);
- /**
- * The bpp value should be in the range 1-32, but the only
- * values considered legal are:
- * RGB ENCODING: 1, 4, 8, 16, 24 and 32
- * RLE4 ENCODING: 4
- * RLE8 ENCODING: 8
- * BITFIELD ENCODING: 16 and 32
- */
- switch (bmp->encoding) {
- case BMP_ENCODING_RGB:
- if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
- (bmp->bpp != 8) &&
- (bmp->bpp != 16) &&
- (bmp->bpp != 24) &&
- (bmp->bpp != 32))
- return BMP_DATA_ERROR;
- break;
- case BMP_ENCODING_RLE8:
- if (bmp->bpp != 8)
- return BMP_DATA_ERROR;
- break;
- case BMP_ENCODING_RLE4:
- if (bmp->bpp != 4)
- return BMP_DATA_ERROR;
- break;
- case BMP_ENCODING_BITFIELDS:
- if ((bmp->bpp != 16) && (bmp->bpp != 32))
- return BMP_DATA_ERROR;
- break;
- /* invalid encoding */
- default:
- return BMP_DATA_ERROR;
- break;
- }
- /* Bitfield encoding means we have red, green, blue, and alpha masks.
- * Here we acquire 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 (header_size == 40) {
- header_size += 12;
- if (bmp->buffer_size < (14 + header_size))
- return BMP_INSUFFICIENT_DATA;
- for (i = 0; i < 3; i++)
- bmp->mask[i] = read_uint32(data, 40 + (i << 2));
- } else {
- for (i = 0; i < 4; i++)
- bmp->mask[i] = read_uint32(data, 40 + (i << 2));
- }
- for (i = 0; i < 4; i++) {
- if (bmp->mask[i] == 0)
- break;
- for (j = 31; j > 0; j--)
- if (bmp->mask[i] & (1 << j)) {
- if ((j - 7) > 0)
- bmp->mask[i] &= 0xff << (j - 7);
- else
- bmp->mask[i] &= 0xff >> (-(j - 7));
- bmp->shift[i] = (i << 3) - (j - 7);
- break;
- }
- }
- }
- 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:
- *
- * +0 BYTE blue
- * +1 BYTE green
- * +2 BYTE red
- *
- * if the palette is from an OS/2 or Win2.x file then the entries
- * are padded with an extra byte.
- */
-
- /* boundary checking */
- if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours)))
- return BMP_INSUFFICIENT_DATA;
-
- /* create the colour table */
- 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;
- bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0);
- }
+ /* boundary checking */
+ if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours)))
+ return BMP_INSUFFICIENT_DATA;
+
+ /* create the colour table */
+ 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;
+ bmp->colour_table[i] = read_uint32((uint8_t *)&bmp->colour_table[i],0);
+ }
/* some bitmaps have a bad offset if there is a pallete, work
* round this by fixing up the data offset to after the palette
@@ -495,131 +318,167 @@ static bmp_result bmp_analyse_header(bmp_image *bmp, uint8_t *data) {
((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) {
bmp->bitmap_offset = data - bmp->bmp_data;
}
- }
-
- /* create our bitmap */
- 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)
- free(bmp->colour_table);
- bmp->colour_table = NULL;
- return BMP_INSUFFICIENT_MEMORY;
- }
- /* 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;
- return BMP_OK;
+ }
+
+ /* create our bitmap */
+ 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)
+ free(bmp->colour_table);
+ bmp->colour_table = NULL;
+ return BMP_INSUFFICIENT_MEMORY;
+ }
+ /* 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;
+ return BMP_OK;
}
/**
- * Finds the closest BMP within an ICO collection
+ * Parse the bitmap file header
*
- * This function finds the BMP with dimensions as close to a specified set
- * as possible from the images in the collection.
- *
- * \param ico the ICO collection to examine
- * \param width the preferred width (0 to use ICO header width)
- * \param height the preferred height (0 to use ICO header height)
+ * \param bmp The bitmap.
+ * \param data The data for the file header
+ * \return BMP_OK on success or error code on faliure
*/
-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 ((image->bmp.width == width) && (image->bmp.height == height))
- return &image->bmp;
- x = image->bmp.width - width;
- y = image->bmp.height - height;
- cur = (x * x) + (y * y);
- if (cur < distance) {
- distance = cur;
- bmp = &image->bmp;
- }
- }
- return bmp;
+static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *data)
+{
+ /* standard 14-byte BMP file header is:
+ *
+ * +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 < BMP_FILE_HEADER_SIZE)
+ return BMP_INSUFFICIENT_DATA;
+
+ if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M'))
+ return BMP_DATA_ERROR;
+
+ bmp->bitmap_offset = read_uint32(data, 10);
+
+ /* check the offset to data lies within the file */
+ if (bmp->bitmap_offset >= bmp->buffer_size) {
+ return BMP_INSUFFICIENT_DATA;
+ }
+
+ return BMP_OK;
}
/**
- * Decode a BMP
+ * Allocates memory for the next BMP in an ICO collection
*
- * This function decodes the BMP data such that bmp->bitmap is a valid
- * image. The state of bmp->decoded is set to TRUE on exit such that it
- * can easily be identified which BMPs are in a fully decoded state.
+ * Sets proper structure values
*
- * \param bmp the BMP image to decode
- * \return BMP_OK on success
+ * \param ico the ICO collection to add the image to
+ * \param image a pointer to the ICO image to be initialised
*/
-bmp_result bmp_decode(bmp_image *bmp) {
- uint8_t *data;
- uint32_t bytes;
- bmp_result result = BMP_OK;
-
- assert(bmp->bitmap);
-
- data = bmp->bmp_data + bmp->bitmap_offset;
- bytes = bmp->buffer_size - bmp->bitmap_offset;
-
- switch (bmp->encoding) {
- case BMP_ENCODING_RGB:
- if ((bmp->bpp == 24) || (bmp->bpp == 32))
- result = bmp_decode_rgb24(bmp, &data, bytes);
- else if (bmp->bpp == 16)
- result = bmp_decode_rgb16(bmp, &data, bytes);
- else
- result = bmp_decode_rgb(bmp, &data, bytes);
- break;
- case BMP_ENCODING_RLE8:
- result = bmp_decode_rle(bmp, data, bytes, 8);
- break;
- case BMP_ENCODING_RLE4:
- result = bmp_decode_rle(bmp, data, bytes, 4);
- break;
- case BMP_ENCODING_BITFIELDS:
- if (bmp->bpp == 32)
- result = bmp_decode_rgb24(bmp, &data, bytes);
- else if (bmp->bpp == 16)
- result = bmp_decode_rgb16(bmp, &data, bytes);
- else
- return BMP_DATA_ERROR;
- }
-
- if ((!bmp->ico) || (result != BMP_OK))
- return result;
-
- bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data;
- return bmp_decode_mask(bmp, data, bytes);
+static bmp_result next_ico_image(ico_collection *ico, ico_image *image) {
+ bmp_create(&image->bmp, &ico->bitmap_callbacks);
+ image->next = ico->first;
+ ico->first = image;
+ return BMP_OK;
}
+
/**
- * Decode a BMP using "limited transparency"
+ * Parse the icon file header
*
- * 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
+ * \param ico The icon collection.
+ * \param data The header data to parse.
+ * \return BMP_OK on successful parse else error code
*/
-bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) {
- bmp->limited_trans = true;
- bmp->trans_colour = colour;
- return bmp_decode(bmp);
+static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data)
+{
+ uint16_t count, i;
+ bmp_result result;
+ int area, max_area = 0;
+
+ /* 6-byte ICO file header is:
+ *
+ * +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 < ICO_FILE_HEADER_SIZE)
+ return BMP_INSUFFICIENT_DATA;
+ // if (read_int16(data, 2) != 0x0000)
+ // return BMP_DATA_ERROR;
+ if (read_uint16(data, 2) != 0x0001)
+ return BMP_DATA_ERROR;
+ count = read_uint16(data, 4);
+ if (count == 0)
+ return BMP_DATA_ERROR;
+ data += ICO_FILE_HEADER_SIZE;
+
+ /* 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 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;
+ image = calloc(1, sizeof(ico_image));
+ if (!image)
+ return BMP_INSUFFICIENT_MEMORY;
+ result = next_ico_image(ico, image);
+ if (result != BMP_OK)
+ return result;
+ image->bmp.width = read_uint8(data, 0);
+ if (image->bmp.width == 0)
+ image->bmp.width = 256;
+ image->bmp.height = read_uint8(data, 1);
+ if (image->bmp.height == 0)
+ image->bmp.height = 256;
+ image->bmp.buffer_size = read_uint32(data, 8);
+ image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12);
+ image->bmp.ico = true;
+ data += ICO_DIR_ENTRY_SIZE;
+
+ /* Ensure that the bitmap data resides in the buffer */
+ if (image->bmp.bmp_data - ico->ico_data >= 0 &&
+ (uint32_t)(image->bmp.bmp_data -
+ ico->ico_data) >= ico->buffer_size)
+ return BMP_DATA_ERROR;
+
+ /* Ensure that we have sufficient data to read the bitmap */
+ if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >=
+ ico->buffer_size - (ico->ico_data - data))
+ return BMP_INSUFFICIENT_DATA;
+
+ result = bmp_info_header_parse(&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;
+ ico->height = image->bmp.height;
+ max_area = area;
+ }
+ }
+ return BMP_OK;
}
@@ -633,73 +492,74 @@ bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) {
* BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
* in this case, the image may be partially viewable
*/
-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;
- uint8_t i;
- uint32_t word;
-
- data = *start;
- swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
- top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
- if (!top)
- return BMP_INSUFFICIENT_MEMORY;
- bottom = top + (uint64_t)swidth * (bmp->height - 1);
- end = data + bytes;
- addr = ((intptr_t)data) & 3;
- skip = bmp->bpp >> 3;
- bmp->decoded = true;
-
- /* Determine transparent index */
- if (bmp->limited_trans) {
- if ((data + skip) > end)
- return BMP_INSUFFICIENT_DATA;
- 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);
- }
-
- 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 = (void *)(top + (y * swidth));
- else
- scanline = (void *)(bottom - (y * swidth));
- if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
- for (x = 0; x < bmp->width; x++) {
- 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]);
- else
- 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 += skip;
- scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
- }
- } else {
- for (x = 0; x < bmp->width; x++) {
- 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;
- scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
- }
- }
- }
- *start = data;
- return BMP_OK;
+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;
+ uint8_t i;
+ uint32_t word;
+
+ data = *start;
+ swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
+ top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
+ if (!top)
+ return BMP_INSUFFICIENT_MEMORY;
+ bottom = top + (uint64_t)swidth * (bmp->height - 1);
+ end = data + bytes;
+ addr = ((intptr_t)data) & 3;
+ skip = bmp->bpp >> 3;
+ bmp->decoded = true;
+
+ /* Determine transparent index */
+ if (bmp->limited_trans) {
+ if ((data + skip) > end)
+ return BMP_INSUFFICIENT_DATA;
+ 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);
+ }
+
+ 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 = (void *)(top + (y * swidth));
+ else
+ scanline = (void *)(bottom - (y * swidth));
+ if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
+ for (x = 0; x < bmp->width; x++) {
+ 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]);
+ else
+ 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 += skip;
+ scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
+ }
+ } else {
+ for (x = 0; x < bmp->width; x++) {
+ 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;
+ scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
+ }
+ }
+ }
+ *start = data;
+ return BMP_OK;
}
@@ -713,78 +573,79 @@ static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) {
* BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
* in this case, the image may be partially viewable
*/
-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;
- uint8_t i;
- uint16_t word;
-
- data = *start;
- swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
- top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
- if (!top)
- return BMP_INSUFFICIENT_MEMORY;
- bottom = top + (uint64_t)swidth * (bmp->height - 1);
- end = data + bytes;
- addr = ((intptr_t)data) & 3;
- bmp->decoded = true;
-
- /* Determine transparent index */
- if (bmp->limited_trans) {
- if ((data + 2) > end)
- return BMP_INSUFFICIENT_DATA;
- bmp->transparent_index = read_uint16(data, 0);
- }
-
- for (y = 0; y < bmp->height; y++) {
- while (addr != (((intptr_t)data) & 3))
- data += 2;
- if ((data + (2 * bmp->width)) > end)
- return BMP_INSUFFICIENT_DATA;
- if (bmp->reversed)
- scanline = (void *)(top + (y * swidth));
- else
- scanline = (void *)(bottom - (y * swidth));
- if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
- for (x = 0; x < bmp->width; x++) {
- 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;
- scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
- }
- } else {
- for (x = 0; x < bmp->width; x++) {
- 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;
- scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
- }
- }
- }
- *start = data;
- return BMP_OK;
+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;
+ uint8_t i;
+ uint16_t word;
+
+ data = *start;
+ swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
+ top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
+ if (!top)
+ return BMP_INSUFFICIENT_MEMORY;
+ bottom = top + (uint64_t)swidth * (bmp->height - 1);
+ end = data + bytes;
+ addr = ((intptr_t)data) & 3;
+ bmp->decoded = true;
+
+ /* Determine transparent index */
+ if (bmp->limited_trans) {
+ if ((data + 2) > end)
+ return BMP_INSUFFICIENT_DATA;
+ bmp->transparent_index = read_uint16(data, 0);
+ }
+
+ for (y = 0; y < bmp->height; y++) {
+ while (addr != (((intptr_t)data) & 3))
+ data += 2;
+ if ((data + (2 * bmp->width)) > end)
+ return BMP_INSUFFICIENT_DATA;
+ if (bmp->reversed)
+ scanline = (void *)(top + (y * swidth));
+ else
+ scanline = (void *)(bottom - (y * swidth));
+ if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
+ for (x = 0; x < bmp->width; x++) {
+ 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;
+ scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
+ }
+ } else {
+ for (x = 0; x < bmp->width; x++) {
+ 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;
+ scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
+ }
+ }
+ }
+ *start = data;
+ return BMP_OK;
}
@@ -798,63 +659,64 @@ static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) {
* BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
* in this case, the image may be partially viewable
*/
-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;
- 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);
-
- data = *start;
- swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
- top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
- if (!top)
- return BMP_INSUFFICIENT_MEMORY;
- bottom = top + (uint64_t)swidth * (bmp->height - 1);
- end = data + bytes;
- addr = ((intptr_t)data) & 3;
- bmp->decoded = true;
-
- /* Determine transparent index */
- if (bmp->limited_trans) {
- uint32_t idx = (*data >> bit_shifts[0]) & bit_mask;
- if (idx >= bmp->colours)
- return BMP_DATA_ERROR;
- bmp->transparent_index = bmp->colour_table[idx];
- }
-
- for (y = 0; y < bmp->height; y++) {
- while (addr != (((intptr_t)data) & 3))
- data++;
- bit = 8;
- if ((data + (bmp->width / ppb)) > end)
- return BMP_INSUFFICIENT_DATA;
- if (bmp->reversed)
- scanline = (void *)(top + (y * swidth));
- else
- scanline = (void *)(bottom - (y * swidth));
- for (x = 0; x < bmp->width; x++) {
- uint32_t idx;
- if (bit >= ppb) {
- bit = 0;
- cur_byte = *data++;
- }
- idx = (cur_byte >> bit_shifts[bit++]) & bit_mask;
- if (idx >= bmp->colours)
- return BMP_DATA_ERROR;
- scanline[x] = bmp->colour_table[idx];
- if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index))
- scanline[x] = bmp->trans_colour;
- }
- }
- *start = data;
- return BMP_OK;
+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;
+ 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);
+
+ data = *start;
+ swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
+ top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
+ if (!top)
+ return BMP_INSUFFICIENT_MEMORY;
+ bottom = top + (uint64_t)swidth * (bmp->height - 1);
+ end = data + bytes;
+ addr = ((intptr_t)data) & 3;
+ bmp->decoded = true;
+
+ /* Determine transparent index */
+ if (bmp->limited_trans) {
+ uint32_t idx = (*data >> bit_shifts[0]) & bit_mask;
+ if (idx >= bmp->colours)
+ return BMP_DATA_ERROR;
+ bmp->transparent_index = bmp->colour_table[idx];
+ }
+
+ for (y = 0; y < bmp->height; y++) {
+ while (addr != (((intptr_t)data) & 3))
+ data++;
+ bit = 8;
+ if ((data + (bmp->width / ppb)) > end)
+ return BMP_INSUFFICIENT_DATA;
+ if (bmp->reversed)
+ scanline = (void *)(top + (y * swidth));
+ else
+ scanline = (void *)(bottom - (y * swidth));
+ for (x = 0; x < bmp->width; x++) {
+ uint32_t idx;
+ if (bit >= ppb) {
+ bit = 0;
+ cur_byte = *data++;
+ }
+ idx = (cur_byte >> bit_shifts[bit++]) & bit_mask;
+ if (idx >= bmp->colours)
+ return BMP_DATA_ERROR;
+ scanline[x] = bmp->colour_table[idx];
+ if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index))
+ scanline[x] = bmp->trans_colour;
+ }
+ }
+ *start = data;
+ return BMP_OK;
}
@@ -866,39 +728,40 @@ static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) {
* \param bytes the number of bytes of data available
* \return BMP_OK on success
*/
-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;
- 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);
- if (!top)
- return BMP_INSUFFICIENT_MEMORY;
- bottom = top + (uint64_t)swidth * (bmp->height - 1);
- end = data + bytes;
- addr = ((intptr_t)data) & 3;
-
- for (y = 0; y < bmp->height; y++) {
- while (addr != (((intptr_t)data) & 3))
- data++;
- if ((data + (bmp->width >> 3)) > end)
- return BMP_INSUFFICIENT_DATA;
- scanline = (void *)(bottom - (y * swidth));
- for (x = 0; x < bmp->width; x++) {
- if ((x & 7) == 0)
- cur_byte = *data++;
- if ((cur_byte & 128) == 0) {
- scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
- scanline[x] |= (0xff << 24);
- scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
- }
- cur_byte = cur_byte << 1;
- }
- }
- return BMP_OK;
+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;
+ 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);
+ if (!top)
+ return BMP_INSUFFICIENT_MEMORY;
+ bottom = top + (uint64_t)swidth * (bmp->height - 1);
+ end = data + bytes;
+ addr = ((intptr_t)data) & 3;
+
+ for (y = 0; y < bmp->height; y++) {
+ while (addr != (((intptr_t)data) & 3))
+ data++;
+ if ((data + (bmp->width >> 3)) > end)
+ return BMP_INSUFFICIENT_DATA;
+ scanline = (void *)(bottom - (y * swidth));
+ for (x = 0; x < bmp->width; x++) {
+ if ((x & 7) == 0)
+ cur_byte = *data++;
+ if ((cur_byte & 128) == 0) {
+ scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
+ scanline[x] |= (0xff << 24);
+ scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
+ }
+ cur_byte = cur_byte << 1;
+ }
+ }
+ return BMP_OK;
}
@@ -913,219 +776,356 @@ static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) {
* BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
* in this case, the image may be partially viewable
*/
-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;
-
- swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
- top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
- if (!top)
- return BMP_INSUFFICIENT_MEMORY;
- bottom = top + (uint64_t)swidth * (bmp->height - 1);
- end = data + bytes;
- bmp->decoded = true;
-
- do {
- if (data + 2 > end)
- return BMP_INSUFFICIENT_DATA;
- length = *data++;
- if (length == 0) {
- length = *data++;
- if (length == 0) {
- /* 00 - 00 means end of scanline */
- x = 0;
- if (last_y == y) {
+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;
+
+ swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width;
+ top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
+ if (!top)
+ return BMP_INSUFFICIENT_MEMORY;
+ bottom = top + (uint64_t)swidth * (bmp->height - 1);
+ end = data + bytes;
+ bmp->decoded = true;
+
+ do {
+ if (data + 2 > end)
+ return BMP_INSUFFICIENT_DATA;
+ length = *data++;
+ if (length == 0) {
+ length = *data++;
+ if (length == 0) {
+ /* 00 - 00 means end of scanline */
+ x = 0;
+ if (last_y == y) {
y++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
- }
- last_y = y;
- } else if (length == 1) {
- /* 00 - 01 means end of RLE data */
- return BMP_OK;
- } else if (length == 2) {
- /* 00 - 02 - XX - YY means move cursor */
- if (data + 2 > end)
- return BMP_INSUFFICIENT_DATA;
- x += *data++;
- if (x >= bmp->width)
- return BMP_DATA_ERROR;
- y += *data++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
- } else {
- /* 00 - NN means escape NN pixels */
- if (bmp->reversed) {
- pixels_left = (bmp->height - y) * bmp->width - x;
- scanline = (void *)(top + (y * swidth));
- } else {
- pixels_left = (y + 1) * bmp->width - x;
- scanline = (void *)(bottom - (y * swidth));
- }
- if (length > pixels_left)
- length = pixels_left;
- if ((size == 4 && data + ((length + 1) / 2) > end) ||
- (size == 8 && data + length > end))
- return BMP_INSUFFICIENT_DATA;
-
- /* the following code could be easily optimised by simply
- * checking the bounds on entry and using some simply copying
- * routines if so */
- if (size == 8) {
- for (i = 0; i < length; i++) {
- uint32_t idx = (uint32_t) *data++;
- if (x >= bmp->width) {
- x = 0;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
+ }
+ last_y = y;
+ } else if (length == 1) {
+ /* 00 - 01 means end of RLE data */
+ return BMP_OK;
+ } else if (length == 2) {
+ /* 00 - 02 - XX - YY means move cursor */
+ if (data + 2 > end)
+ return BMP_INSUFFICIENT_DATA;
+ x += *data++;
+ if (x >= bmp->width)
+ return BMP_DATA_ERROR;
+ y += *data++;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
+ } else {
+ /* 00 - NN means escape NN pixels */
+ if (bmp->reversed) {
+ pixels_left = (bmp->height - y) * bmp->width - x;
+ scanline = (void *)(top + (y * swidth));
+ } else {
+ pixels_left = (y + 1) * bmp->width - x;
+ scanline = (void *)(bottom - (y * swidth));
+ }
+ if (length > pixels_left)
+ length = pixels_left;
+ if ((size == 4 && data + ((length + 1) / 2) > end) ||
+ (size == 8 && data + length > end))
+ return BMP_INSUFFICIENT_DATA;
+
+ /* the following code could be easily optimised by simply
+ * checking the bounds on entry and using some simply copying
+ * routines if so */
+ if (size == 8) {
+ for (i = 0; i < length; i++) {
+ uint32_t idx = (uint32_t) *data++;
+ if (x >= bmp->width) {
+ x = 0;
y++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
if (bmp->reversed) {
scanline += bmp->width;
} else {
scanline -= bmp->width;
}
- }
- if (idx >= bmp->colours)
- return BMP_DATA_ERROR;
- scanline[x++] = bmp->colour_table[idx];
- }
- } else {
- for (i = 0; i < length; i++) {
- if (x >= bmp->width) {
- x = 0;
+ }
+ if (idx >= bmp->colours)
+ return BMP_DATA_ERROR;
+ scanline[x++] = bmp->colour_table[idx];
+ }
+ } else {
+ for (i = 0; i < length; i++) {
+ if (x >= bmp->width) {
+ x = 0;
y++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
if (bmp->reversed) {
scanline += bmp->width;
} else {
scanline -= bmp->width;
}
- }
- if ((i & 1) == 0) {
- pixel = *data++;
- if ((pixel >> 4) >= bmp->colours)
- return BMP_DATA_ERROR;
- scanline[x++] = bmp->colour_table
- [pixel >> 4];
- } else {
- if ((pixel & 0xf) >= bmp->colours)
- return BMP_DATA_ERROR;
- scanline[x++] = bmp->colour_table
- [pixel & 0xf];
- }
- }
- length = (length + 1) >> 1;
- }
- if ((length & 1) && (*data++ != 0x00))
- return BMP_DATA_ERROR;
-
- }
- } else {
- /* NN means perform RLE for NN pixels */
- if (bmp->reversed) {
- pixels_left = (bmp->height - y) * bmp->width - x;
- scanline = (void *)(top + (y * swidth));
- } else {
- pixels_left = (y + 1) * bmp->width - x;
- scanline = (void *)(bottom - (y * swidth));
- }
- if (length > pixels_left)
- length = pixels_left;
-
- /* boundary checking */
- if (data + 1 > end)
- return BMP_INSUFFICIENT_DATA;
-
- /* the following code could be easily optimised by simply
- * checking the bounds on entry and using some simply copying
- * routines if so */
- if (size == 8) {
- uint32_t idx = (uint32_t) *data++;
- if (idx >= bmp->colours)
- return BMP_DATA_ERROR;
- pixel = bmp->colour_table[idx];
- for (i = 0; i < length; i++) {
- if (x >= bmp->width) {
- x = 0;
+ }
+ if ((i & 1) == 0) {
+ pixel = *data++;
+ if ((pixel >> 4) >= bmp->colours)
+ return BMP_DATA_ERROR;
+ scanline[x++] = bmp->colour_table
+ [pixel >> 4];
+ } else {
+ if ((pixel & 0xf) >= bmp->colours)
+ return BMP_DATA_ERROR;
+ scanline[x++] = bmp->colour_table
+ [pixel & 0xf];
+ }
+ }
+ length = (length + 1) >> 1;
+ }
+ if ((length & 1) && (*data++ != 0x00))
+ return BMP_DATA_ERROR;
+
+ }
+ } else {
+ /* NN means perform RLE for NN pixels */
+ if (bmp->reversed) {
+ pixels_left = (bmp->height - y) * bmp->width - x;
+ scanline = (void *)(top + (y * swidth));
+ } else {
+ pixels_left = (y + 1) * bmp->width - x;
+ scanline = (void *)(bottom - (y * swidth));
+ }
+ if (length > pixels_left)
+ length = pixels_left;
+
+ /* boundary checking */
+ if (data + 1 > end)
+ return BMP_INSUFFICIENT_DATA;
+
+ /* the following code could be easily optimised by simply
+ * checking the bounds on entry and using some simply copying
+ * routines if so */
+ if (size == 8) {
+ uint32_t idx = (uint32_t) *data++;
+ if (idx >= bmp->colours)
+ return BMP_DATA_ERROR;
+ pixel = bmp->colour_table[idx];
+ for (i = 0; i < length; i++) {
+ if (x >= bmp->width) {
+ x = 0;
y++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
if (bmp->reversed) {
scanline += bmp->width;
} else {
scanline -= bmp->width;
}
- }
- scanline[x++] = pixel;
- }
- } else {
- pixel2 = *data++;
- if ((pixel2 >> 4) >= bmp->colours ||
- (pixel2 & 0xf) >= bmp->colours)
- return BMP_DATA_ERROR;
- pixel = bmp->colour_table[pixel2 >> 4];
- pixel2 = bmp->colour_table[pixel2 & 0xf];
- for (i = 0; i < length; i++) {
- if (x >= bmp->width) {
- x = 0;
+ }
+ scanline[x++] = pixel;
+ }
+ } else {
+ pixel2 = *data++;
+ if ((pixel2 >> 4) >= bmp->colours ||
+ (pixel2 & 0xf) >= bmp->colours)
+ return BMP_DATA_ERROR;
+ pixel = bmp->colour_table[pixel2 >> 4];
+ pixel2 = bmp->colour_table[pixel2 & 0xf];
+ for (i = 0; i < length; i++) {
+ if (x >= bmp->width) {
+ x = 0;
y++;
- if (y >= bmp->height)
- return BMP_DATA_ERROR;
+ if (y >= bmp->height)
+ return BMP_DATA_ERROR;
if (bmp->reversed) {
scanline += bmp->width;
} else {
scanline -= bmp->width;
}
- }
- if ((i & 1) == 0)
- scanline[x++] = pixel;
- else
- scanline[x++] = pixel2;
- }
- }
- }
- } while (data < end);
- return BMP_OK;
+ }
+ if ((i & 1) == 0)
+ scanline[x++] = pixel;
+ else
+ scanline[x++] = pixel2;
+ }
+ }
+ }
+ } while (data < end);
+ return BMP_OK;
}
-/**
- * Finalise a BMP prior to destruction.
- *
- * \param bmp the BMP image to finalise
- */
-void bmp_finalise(bmp_image *bmp) {
- if (bmp->bitmap)
- bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap);
- bmp->bitmap = NULL;
- if (bmp->colour_table)
- free(bmp->colour_table);
- bmp->colour_table = NULL;
+/* exported interface documented in libnsbmp.h */
+bmp_result
+bmp_create(bmp_image *bmp,
+ bmp_bitmap_callback_vt *bitmap_callbacks)
+{
+ memset(bmp, 0, sizeof(bmp_image));
+ bmp->bitmap_callbacks = *bitmap_callbacks;
+
+ return BMP_OK;
}
-/**
- * Finalise an ICO prior to destruction.
- *
- * \param ico the ICO image to finalise
- */
-void ico_finalise(ico_collection *ico) {
- ico_image *image;
-
- for (image = ico->first; image; image = image->next)
- bmp_finalise(&image->bmp);
- while (ico->first) {
- image = ico->first;
- ico->first = image->next;
- free(image);
- }
+/* exported interface documented in libnsbmp.h */
+bmp_result
+ico_collection_create(ico_collection *ico,
+ bmp_bitmap_callback_vt *bitmap_callbacks)
+{
+
+ memset(ico, 0, sizeof(ico_collection));
+ ico->bitmap_callbacks = *bitmap_callbacks;
+
+ return BMP_OK;
+}
+
+
+/* exported interface documented in libnsbmp.h */
+bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data)
+{
+ bmp_result res;
+
+ /* ensure we aren't already initialised */
+ if (bmp->bitmap) {
+ return BMP_OK;
+ }
+
+ /* initialize source data values */
+ bmp->buffer_size = size;
+ bmp->bmp_data = data;
+
+ res = bmp_file_header_parse(bmp, data);
+ if (res == BMP_OK) {
+ res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE);
+ }
+ return res;
+}
+
+
+/* exported interface documented in libnsbmp.h */
+bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data)
+{
+ /* ensure we aren't already initialised */
+ if (ico->first)
+ return BMP_OK;
+
+ /* initialize values */
+ ico->buffer_size = size;
+ ico->ico_data = data;
+
+ return ico_header_parse(ico, data);
+}
+
+
+/* exported interface documented in libnsbmp.h */
+bmp_result bmp_decode(bmp_image *bmp)
+{
+ uint8_t *data;
+ uint32_t bytes;
+ bmp_result result = BMP_OK;
+
+ assert(bmp->bitmap);
+
+ data = bmp->bmp_data + bmp->bitmap_offset;
+ bytes = bmp->buffer_size - bmp->bitmap_offset;
+
+ switch (bmp->encoding) {
+ case BMP_ENCODING_RGB:
+ if ((bmp->bpp == 24) || (bmp->bpp == 32))
+ result = bmp_decode_rgb24(bmp, &data, bytes);
+ else if (bmp->bpp == 16)
+ result = bmp_decode_rgb16(bmp, &data, bytes);
+ else
+ result = bmp_decode_rgb(bmp, &data, bytes);
+ break;
+ case BMP_ENCODING_RLE8:
+ result = bmp_decode_rle(bmp, data, bytes, 8);
+ break;
+ case BMP_ENCODING_RLE4:
+ result = bmp_decode_rle(bmp, data, bytes, 4);
+ break;
+ case BMP_ENCODING_BITFIELDS:
+ if (bmp->bpp == 32)
+ result = bmp_decode_rgb24(bmp, &data, bytes);
+ else if (bmp->bpp == 16)
+ result = bmp_decode_rgb16(bmp, &data, bytes);
+ else
+ return BMP_DATA_ERROR;
+ }
+
+ if ((!bmp->ico) || (result != BMP_OK))
+ return result;
+
+ bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data;
+ return bmp_decode_mask(bmp, data, bytes);
+}
+
+
+/* exported interface documented in libnsbmp.h */
+bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour)
+{
+ bmp->limited_trans = true;
+ bmp->trans_colour = colour;
+ return bmp_decode(bmp);
+}
+
+
+/* exported interface documented in libnsbmp.h */
+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 ((image->bmp.width == width) && (image->bmp.height == height))
+ return &image->bmp;
+ x = image->bmp.width - width;
+ y = image->bmp.height - height;
+ cur = (x * x) + (y * y);
+ if (cur < distance) {
+ distance = cur;
+ bmp = &image->bmp;
+ }
+ }
+ return bmp;
+}
+
+
+/* exported interface documented in libnsbmp.h */
+void bmp_finalise(bmp_image *bmp)
+{
+ if (bmp->bitmap)
+ bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap);
+ bmp->bitmap = NULL;
+ if (bmp->colour_table)
+ free(bmp->colour_table);
+ bmp->colour_table = NULL;
+}
+
+
+/* exported interface documented in libnsbmp.h */
+void ico_finalise(ico_collection *ico)
+{
+ ico_image *image;
+
+ for (image = ico->first; image; image = image->next)
+ bmp_finalise(&image->bmp);
+ while (ico->first) {
+ image = ico->first;
+ ico->first = image->next;
+ free(image);
+ }
}