1*803618f4SDamon Ding /*
2*803618f4SDamon Ding * Copyright 2006 Richard Wilson <richard.wilson@netsurf-browser.org>
3*803618f4SDamon Ding * Copyright 2008 Sean Fox <dyntryx@gmail.com>
4*803618f4SDamon Ding *
5*803618f4SDamon Ding * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/
6*803618f4SDamon Ding * Licenced under the MIT License,
7*803618f4SDamon Ding * http://www.opensource.org/licenses/mit-license.php
8*803618f4SDamon Ding */
9*803618f4SDamon Ding
10*803618f4SDamon Ding /**
11*803618f4SDamon Ding * \file
12*803618f4SDamon Ding * BMP decoding implementation
13*803618f4SDamon Ding *
14*803618f4SDamon Ding * This library decode windows bitmaps and icons from their disc images.
15*803618f4SDamon Ding *
16*803618f4SDamon Ding * The image format is described in several documents:
17*803618f4SDamon Ding * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx
18*803618f4SDamon Ding * http://www.fileformat.info/format/bmp/egff.htm
19*803618f4SDamon Ding * https://en.wikipedia.org/wiki/BMP_file_format
20*803618f4SDamon Ding *
21*803618f4SDamon Ding * Despite the format being clearly defined many bitmaps found on the web are
22*803618f4SDamon Ding * not compliant and this implementation attempts to cope with as many issues
23*803618f4SDamon Ding * as possible rather than simply failing.
24*803618f4SDamon Ding */
25*803618f4SDamon Ding
26*803618f4SDamon Ding #include "libnsbmp.h"
27*803618f4SDamon Ding
28*803618f4SDamon Ding /* squashes unused variable compiler warnings */
29*803618f4SDamon Ding #define UNUSED(x) ((x)=(x))
30*803618f4SDamon Ding
31*803618f4SDamon Ding /* BMP entry sizes */
32*803618f4SDamon Ding #define BMP_FILE_HEADER_SIZE 14
33*803618f4SDamon Ding #define ICO_FILE_HEADER_SIZE 6
34*803618f4SDamon Ding #define ICO_DIR_ENTRY_SIZE 16
35*803618f4SDamon Ding
36*803618f4SDamon Ding /* the bitmap information header types (encoded as lengths) */
37*803618f4SDamon Ding #define BITMAPCOREHEADER 12
38*803618f4SDamon Ding
39*803618f4SDamon Ding #ifdef WE_NEED_INT8_READING_NOW
read_int8(uint8_t * data,unsigned int o)40*803618f4SDamon Ding static inline int8_t read_int8(uint8_t *data, unsigned int o) {
41*803618f4SDamon Ding return (int8_t) data[o];
42*803618f4SDamon Ding }
43*803618f4SDamon Ding #endif
44*803618f4SDamon Ding
read_uint8(uint8_t * data,unsigned int o)45*803618f4SDamon Ding static inline uint8_t read_uint8(uint8_t *data, unsigned int o) {
46*803618f4SDamon Ding return (uint8_t) data[o];
47*803618f4SDamon Ding }
48*803618f4SDamon Ding
read_int16(uint8_t * data,unsigned int o)49*803618f4SDamon Ding static inline int16_t read_int16(uint8_t *data, unsigned int o) {
50*803618f4SDamon Ding return (int16_t) (data[o] | (data[o+1] << 8));
51*803618f4SDamon Ding }
52*803618f4SDamon Ding
read_uint16(uint8_t * data,unsigned int o)53*803618f4SDamon Ding static inline uint16_t read_uint16(uint8_t *data, unsigned int o) {
54*803618f4SDamon Ding return (uint16_t) (data[o] | (data[o+1] << 8));
55*803618f4SDamon Ding }
56*803618f4SDamon Ding
read_int32(uint8_t * data,unsigned int o)57*803618f4SDamon Ding static inline int32_t read_int32(uint8_t *data, unsigned int o) {
58*803618f4SDamon Ding return (int32_t) ((unsigned)data[o] |
59*803618f4SDamon Ding ((unsigned)data[o+1] << 8) |
60*803618f4SDamon Ding ((unsigned)data[o+2] << 16) |
61*803618f4SDamon Ding ((unsigned)data[o+3] << 24));
62*803618f4SDamon Ding }
63*803618f4SDamon Ding
read_uint32(uint8_t * data,unsigned int o)64*803618f4SDamon Ding static inline uint32_t read_uint32(uint8_t *data, unsigned int o) {
65*803618f4SDamon Ding return (uint32_t) ((unsigned)data[o] |
66*803618f4SDamon Ding ((unsigned)data[o+1] << 8) |
67*803618f4SDamon Ding ((unsigned)data[o+2] << 16) |
68*803618f4SDamon Ding ((unsigned)data[o+3] << 24));
69*803618f4SDamon Ding }
70*803618f4SDamon Ding
71*803618f4SDamon Ding
72*803618f4SDamon Ding /**
73*803618f4SDamon Ding * Parse the bitmap info header
74*803618f4SDamon Ding */
bmp_info_header_parse(bmp_image * bmp,uint8_t * data)75*803618f4SDamon Ding static bmp_result bmp_info_header_parse(bmp_image *bmp, uint8_t *data)
76*803618f4SDamon Ding {
77*803618f4SDamon Ding uint32_t header_size;
78*803618f4SDamon Ding uint32_t i;
79*803618f4SDamon Ding uint8_t j;
80*803618f4SDamon Ding int32_t width, height;
81*803618f4SDamon Ding uint8_t palette_size;
82*803618f4SDamon Ding unsigned int flags = 0;
83*803618f4SDamon Ding
84*803618f4SDamon Ding /* must be at least enough data for a core header */
85*803618f4SDamon Ding if (bmp->buffer_size < (BMP_FILE_HEADER_SIZE + BITMAPCOREHEADER)) {
86*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
87*803618f4SDamon Ding }
88*803618f4SDamon Ding
89*803618f4SDamon Ding header_size = read_uint32(data, 0);
90*803618f4SDamon Ding
91*803618f4SDamon Ding /* ensure there is enough data for the declared header size*/
92*803618f4SDamon Ding if ((bmp->buffer_size - BMP_FILE_HEADER_SIZE) < header_size) {
93*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
94*803618f4SDamon Ding }
95*803618f4SDamon Ding
96*803618f4SDamon Ding /* a variety of different bitmap headers can follow, depending
97*803618f4SDamon Ding * on the BMP variant. The header length field determines the type.
98*803618f4SDamon Ding */
99*803618f4SDamon Ding if (header_size == BITMAPCOREHEADER) {
100*803618f4SDamon Ding /* the following header is for os/2 and windows 2.x and consists of:
101*803618f4SDamon Ding *
102*803618f4SDamon Ding * +0 UINT32 size of this header (in bytes)
103*803618f4SDamon Ding * +4 INT16 image width (in pixels)
104*803618f4SDamon Ding * +6 INT16 image height (in pixels)
105*803618f4SDamon Ding * +8 UINT16 number of colour planes (always 1)
106*803618f4SDamon Ding * +10 UINT16 number of bits per pixel
107*803618f4SDamon Ding */
108*803618f4SDamon Ding width = read_int16(data, 4);
109*803618f4SDamon Ding height = read_int16(data, 6);
110*803618f4SDamon Ding if ((width <= 0) || (height == 0))
111*803618f4SDamon Ding return BMP_DATA_ERROR;
112*803618f4SDamon Ding if (height < 0) {
113*803618f4SDamon Ding bmp->reversed = true;
114*803618f4SDamon Ding height = -height;
115*803618f4SDamon Ding }
116*803618f4SDamon Ding /* ICOs only support 256*256 resolutions
117*803618f4SDamon Ding * In the case of the ICO header, the height is actually the added
118*803618f4SDamon Ding * height of XOR-Bitmap and AND-Bitmap (double the visible height)
119*803618f4SDamon Ding * Technically we could remove this check and ICOs with bitmaps
120*803618f4SDamon Ding * of any size could be processed; this is to conform to the spec.
121*803618f4SDamon Ding */
122*803618f4SDamon Ding if (bmp->ico) {
123*803618f4SDamon Ding if ((width > 256) || (height > 512)) {
124*803618f4SDamon Ding return BMP_DATA_ERROR;
125*803618f4SDamon Ding } else {
126*803618f4SDamon Ding bmp->width = width;
127*803618f4SDamon Ding bmp->height = height / 2;
128*803618f4SDamon Ding }
129*803618f4SDamon Ding } else {
130*803618f4SDamon Ding bmp->width = width;
131*803618f4SDamon Ding bmp->height = height;
132*803618f4SDamon Ding }
133*803618f4SDamon Ding if (read_uint16(data, 8) != 1)
134*803618f4SDamon Ding return BMP_DATA_ERROR;
135*803618f4SDamon Ding bmp->bpp = read_uint16(data, 10);
136*803618f4SDamon Ding /**
137*803618f4SDamon Ding * The bpp value should be in the range 1-32, but the only
138*803618f4SDamon Ding * values considered legal are:
139*803618f4SDamon Ding * RGB ENCODING: 1, 4, 8, 16, 24 and 32
140*803618f4SDamon Ding */
141*803618f4SDamon Ding if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
142*803618f4SDamon Ding (bmp->bpp != 8) &&
143*803618f4SDamon Ding (bmp->bpp != 16) &&
144*803618f4SDamon Ding (bmp->bpp != 24) &&
145*803618f4SDamon Ding (bmp->bpp != 32))
146*803618f4SDamon Ding return BMP_DATA_ERROR;
147*803618f4SDamon Ding if (bmp->bpp < 16)
148*803618f4SDamon Ding bmp->colours = (1 << bmp->bpp);
149*803618f4SDamon Ding palette_size = 3;
150*803618f4SDamon Ding } else if (header_size < 40) {
151*803618f4SDamon Ding return BMP_DATA_ERROR;
152*803618f4SDamon Ding } else {
153*803618f4SDamon Ding /* the following header is for windows 3.x and onwards. it is a
154*803618f4SDamon Ding * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes.
155*803618f4SDamon Ding *
156*803618f4SDamon Ding * +0 UINT32 size of this header (in bytes)
157*803618f4SDamon Ding * +4 INT32 image width (in pixels)
158*803618f4SDamon Ding * +8 INT32 image height (in pixels)
159*803618f4SDamon Ding * +12 UINT16 number of colour planes (always 1)
160*803618f4SDamon Ding * +14 UINT16 number of bits per pixel
161*803618f4SDamon Ding * +16 UINT32 compression methods used
162*803618f4SDamon Ding * +20 UINT32 size of bitmap (in bytes)
163*803618f4SDamon Ding * +24 UINT32 horizontal resolution (in pixels per meter)
164*803618f4SDamon Ding * +28 UINT32 vertical resolution (in pixels per meter)
165*803618f4SDamon Ding * +32 UINT32 number of colours in the image
166*803618f4SDamon Ding * +36 UINT32 number of important colours
167*803618f4SDamon Ding * +40 UINT32 mask identifying bits of red component
168*803618f4SDamon Ding * +44 UINT32 mask identifying bits of green component
169*803618f4SDamon Ding * +48 UINT32 mask identifying bits of blue component
170*803618f4SDamon Ding * +52 UINT32 mask identifying bits of alpha component
171*803618f4SDamon Ding * +56 UINT32 color space type
172*803618f4SDamon Ding * +60 UINT32 x coordinate of red endpoint
173*803618f4SDamon Ding * +64 UINT32 y coordinate of red endpoint
174*803618f4SDamon Ding * +68 UINT32 z coordinate of red endpoint
175*803618f4SDamon Ding * +72 UINT32 x coordinate of green endpoint
176*803618f4SDamon Ding * +76 UINT32 y coordinate of green endpoint
177*803618f4SDamon Ding * +80 UINT32 z coordinate of green endpoint
178*803618f4SDamon Ding * +84 UINT32 x coordinate of blue endpoint
179*803618f4SDamon Ding * +88 UINT32 y coordinate of blue endpoint
180*803618f4SDamon Ding * +92 UINT32 z coordinate of blue endpoint
181*803618f4SDamon Ding * +96 UINT32 gamma red coordinate scale value
182*803618f4SDamon Ding * +100 UINT32 gamma green coordinate scale value
183*803618f4SDamon Ding * +104 UINT32 gamma blue coordinate scale value
184*803618f4SDamon Ding */
185*803618f4SDamon Ding width = read_int32(data, 4);
186*803618f4SDamon Ding height = read_int32(data, 8);
187*803618f4SDamon Ding if ((width <= 0) || (height == 0))
188*803618f4SDamon Ding return BMP_DATA_ERROR;
189*803618f4SDamon Ding if (height < 0) {
190*803618f4SDamon Ding bmp->reversed = true;
191*803618f4SDamon Ding if (height <= -INT32_MAX) {
192*803618f4SDamon Ding height = INT32_MAX;
193*803618f4SDamon Ding } else {
194*803618f4SDamon Ding height = -height;
195*803618f4SDamon Ding }
196*803618f4SDamon Ding }
197*803618f4SDamon Ding /* ICOs only support 256*256 resolutions
198*803618f4SDamon Ding * In the case of the ICO header, the height is actually the added
199*803618f4SDamon Ding * height of XOR-Bitmap and AND-Bitmap (double the visible height)
200*803618f4SDamon Ding * Technically we could remove this check and ICOs with bitmaps
201*803618f4SDamon Ding * of any size could be processed; this is to conform to the spec.
202*803618f4SDamon Ding */
203*803618f4SDamon Ding if (bmp->ico) {
204*803618f4SDamon Ding if ((width > 256) || (height > 512)) {
205*803618f4SDamon Ding return BMP_DATA_ERROR;
206*803618f4SDamon Ding } else {
207*803618f4SDamon Ding bmp->width = width;
208*803618f4SDamon Ding bmp->height = height / 2;
209*803618f4SDamon Ding }
210*803618f4SDamon Ding } else {
211*803618f4SDamon Ding bmp->width = width;
212*803618f4SDamon Ding bmp->height = height;
213*803618f4SDamon Ding }
214*803618f4SDamon Ding if (read_uint16(data, 12) != 1)
215*803618f4SDamon Ding return BMP_DATA_ERROR;
216*803618f4SDamon Ding bmp->bpp = read_uint16(data, 14);
217*803618f4SDamon Ding if (bmp->bpp == 0)
218*803618f4SDamon Ding bmp->bpp = 8;
219*803618f4SDamon Ding bmp->encoding = read_uint32(data, 16);
220*803618f4SDamon Ding /**
221*803618f4SDamon Ding * The bpp value should be in the range 1-32, but the only
222*803618f4SDamon Ding * values considered legal are:
223*803618f4SDamon Ding * RGB ENCODING: 1, 4, 8, 16, 24 and 32
224*803618f4SDamon Ding * RLE4 ENCODING: 4
225*803618f4SDamon Ding * RLE8 ENCODING: 8
226*803618f4SDamon Ding * BITFIELD ENCODING: 16 and 32
227*803618f4SDamon Ding */
228*803618f4SDamon Ding switch (bmp->encoding) {
229*803618f4SDamon Ding case BMP_ENCODING_RGB:
230*803618f4SDamon Ding if ((bmp->bpp != 1) && (bmp->bpp != 4) &&
231*803618f4SDamon Ding (bmp->bpp != 8) &&
232*803618f4SDamon Ding (bmp->bpp != 16) &&
233*803618f4SDamon Ding (bmp->bpp != 24) &&
234*803618f4SDamon Ding (bmp->bpp != 32))
235*803618f4SDamon Ding return BMP_DATA_ERROR;
236*803618f4SDamon Ding break;
237*803618f4SDamon Ding case BMP_ENCODING_RLE8:
238*803618f4SDamon Ding if (bmp->bpp != 8)
239*803618f4SDamon Ding return BMP_DATA_ERROR;
240*803618f4SDamon Ding break;
241*803618f4SDamon Ding case BMP_ENCODING_RLE4:
242*803618f4SDamon Ding if (bmp->bpp != 4)
243*803618f4SDamon Ding return BMP_DATA_ERROR;
244*803618f4SDamon Ding break;
245*803618f4SDamon Ding case BMP_ENCODING_BITFIELDS:
246*803618f4SDamon Ding if ((bmp->bpp != 16) && (bmp->bpp != 32))
247*803618f4SDamon Ding return BMP_DATA_ERROR;
248*803618f4SDamon Ding break;
249*803618f4SDamon Ding /* invalid encoding */
250*803618f4SDamon Ding default:
251*803618f4SDamon Ding return BMP_DATA_ERROR;
252*803618f4SDamon Ding break;
253*803618f4SDamon Ding }
254*803618f4SDamon Ding /* Bitfield encoding means we have red, green, blue, and alpha masks.
255*803618f4SDamon Ding * Here we acquire the masks and determine the required bit shift to
256*803618f4SDamon Ding * align them in our 24-bit color 8-bit alpha format.
257*803618f4SDamon Ding */
258*803618f4SDamon Ding if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
259*803618f4SDamon Ding if (header_size == 40) {
260*803618f4SDamon Ding header_size += 12;
261*803618f4SDamon Ding if (bmp->buffer_size < (14 + header_size))
262*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
263*803618f4SDamon Ding for (i = 0; i < 3; i++)
264*803618f4SDamon Ding bmp->mask[i] = read_uint32(data, 40 + (i << 2));
265*803618f4SDamon Ding } else {
266*803618f4SDamon Ding if (header_size < 56)
267*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
268*803618f4SDamon Ding for (i = 0; i < 4; i++)
269*803618f4SDamon Ding bmp->mask[i] = read_uint32(data, 40 + (i << 2));
270*803618f4SDamon Ding }
271*803618f4SDamon Ding for (i = 0; i < 4; i++) {
272*803618f4SDamon Ding if (bmp->mask[i] == 0)
273*803618f4SDamon Ding break;
274*803618f4SDamon Ding for (j = 31; j > 0; j--)
275*803618f4SDamon Ding if (bmp->mask[i] & ((unsigned)1 << j)) {
276*803618f4SDamon Ding if ((j - 7) > 0)
277*803618f4SDamon Ding bmp->mask[i] &= (unsigned)0xff << (j - 7);
278*803618f4SDamon Ding else
279*803618f4SDamon Ding bmp->mask[i] &= 0xff >> (-(j - 7));
280*803618f4SDamon Ding bmp->shift[i] = (i << 3) - (j - 7);
281*803618f4SDamon Ding break;
282*803618f4SDamon Ding }
283*803618f4SDamon Ding }
284*803618f4SDamon Ding }
285*803618f4SDamon Ding bmp->colours = read_uint32(data, 32);
286*803618f4SDamon Ding if (bmp->colours == 0 && bmp->bpp < 16)
287*803618f4SDamon Ding bmp->colours = (1 << bmp->bpp);
288*803618f4SDamon Ding palette_size = 4;
289*803618f4SDamon Ding }
290*803618f4SDamon Ding data += header_size;
291*803618f4SDamon Ding
292*803618f4SDamon Ding /* if there's no alpha mask, flag the bmp opaque */
293*803618f4SDamon Ding if ((!bmp->ico) && (bmp->mask[3] == 0)) {
294*803618f4SDamon Ding flags |= BMP_OPAQUE;
295*803618f4SDamon Ding bmp->opaque = true;
296*803618f4SDamon Ding }
297*803618f4SDamon Ding
298*803618f4SDamon Ding /* we only have a palette for <16bpp */
299*803618f4SDamon Ding if (bmp->bpp < 16) {
300*803618f4SDamon Ding /* we now have a series of palette entries of the format:
301*803618f4SDamon Ding *
302*803618f4SDamon Ding * +0 BYTE blue
303*803618f4SDamon Ding * +1 BYTE green
304*803618f4SDamon Ding * +2 BYTE red
305*803618f4SDamon Ding *
306*803618f4SDamon Ding * if the palette is from an OS/2 or Win2.x file then the entries
307*803618f4SDamon Ding * are padded with an extra byte.
308*803618f4SDamon Ding */
309*803618f4SDamon Ding
310*803618f4SDamon Ding /* boundary checking */
311*803618f4SDamon Ding if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours)))
312*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
313*803618f4SDamon Ding
314*803618f4SDamon Ding /* create the colour table */
315*803618f4SDamon Ding bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4);
316*803618f4SDamon Ding if (!bmp->colour_table)
317*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
318*803618f4SDamon Ding for (i = 0; i < bmp->colours; i++) {
319*803618f4SDamon Ding uint32_t colour = data[2] | (data[1] << 8) | (data[0] << 16);
320*803618f4SDamon Ding if (bmp->opaque)
321*803618f4SDamon Ding colour |= ((uint32_t)0xff << 24);
322*803618f4SDamon Ding data += palette_size;
323*803618f4SDamon Ding bmp->colour_table[i] = read_uint32((uint8_t *)&colour,0);
324*803618f4SDamon Ding }
325*803618f4SDamon Ding
326*803618f4SDamon Ding /* some bitmaps have a bad offset if there is a pallete, work
327*803618f4SDamon Ding * round this by fixing up the data offset to after the palette
328*803618f4SDamon Ding * but only if there is data following the palette as some
329*803618f4SDamon Ding * bitmaps encode data in the palette!
330*803618f4SDamon Ding */
331*803618f4SDamon Ding if ((bmp->bitmap_offset < (uint32_t)(data - bmp->bmp_data)) &&
332*803618f4SDamon Ding ((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) {
333*803618f4SDamon Ding bmp->bitmap_offset = data - bmp->bmp_data;
334*803618f4SDamon Ding }
335*803618f4SDamon Ding }
336*803618f4SDamon Ding
337*803618f4SDamon Ding /* create our bitmap */
338*803618f4SDamon Ding flags |= BMP_NEW | BMP_CLEAR_MEMORY;
339*803618f4SDamon Ding bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags);
340*803618f4SDamon Ding if (!bmp->bitmap) {
341*803618f4SDamon Ding if (bmp->colour_table)
342*803618f4SDamon Ding free(bmp->colour_table);
343*803618f4SDamon Ding bmp->colour_table = NULL;
344*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
345*803618f4SDamon Ding }
346*803618f4SDamon Ding /* BMPs within ICOs don't have BMP file headers, so the image data should
347*803618f4SDamon Ding * always be right after the colour table.
348*803618f4SDamon Ding */
349*803618f4SDamon Ding if (bmp->ico)
350*803618f4SDamon Ding bmp->bitmap_offset = (uintptr_t)data - (uintptr_t)bmp->bmp_data;
351*803618f4SDamon Ding return BMP_OK;
352*803618f4SDamon Ding }
353*803618f4SDamon Ding
354*803618f4SDamon Ding
355*803618f4SDamon Ding /**
356*803618f4SDamon Ding * Parse the bitmap file header
357*803618f4SDamon Ding *
358*803618f4SDamon Ding * \param bmp The bitmap.
359*803618f4SDamon Ding * \param data The data for the file header
360*803618f4SDamon Ding * \return BMP_OK on success or error code on faliure
361*803618f4SDamon Ding */
bmp_file_header_parse(bmp_image * bmp,uint8_t * data)362*803618f4SDamon Ding static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *data)
363*803618f4SDamon Ding {
364*803618f4SDamon Ding /* standard 14-byte BMP file header is:
365*803618f4SDamon Ding *
366*803618f4SDamon Ding * +0 UINT16 File Type ('BM')
367*803618f4SDamon Ding * +2 UINT32 Size of File (in bytes)
368*803618f4SDamon Ding * +6 INT16 Reserved Field (1)
369*803618f4SDamon Ding * +8 INT16 Reserved Field (2)
370*803618f4SDamon Ding * +10 UINT32 Starting Position of Image Data (offset in bytes)
371*803618f4SDamon Ding */
372*803618f4SDamon Ding if (bmp->buffer_size < BMP_FILE_HEADER_SIZE)
373*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
374*803618f4SDamon Ding
375*803618f4SDamon Ding if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M'))
376*803618f4SDamon Ding return BMP_DATA_ERROR;
377*803618f4SDamon Ding
378*803618f4SDamon Ding bmp->bitmap_offset = read_uint32(data, 10);
379*803618f4SDamon Ding
380*803618f4SDamon Ding /* check the offset to data lies within the file */
381*803618f4SDamon Ding if (bmp->bitmap_offset >= bmp->buffer_size) {
382*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
383*803618f4SDamon Ding }
384*803618f4SDamon Ding
385*803618f4SDamon Ding return BMP_OK;
386*803618f4SDamon Ding }
387*803618f4SDamon Ding
388*803618f4SDamon Ding
389*803618f4SDamon Ding /**
390*803618f4SDamon Ding * Allocates memory for the next BMP in an ICO collection
391*803618f4SDamon Ding *
392*803618f4SDamon Ding * Sets proper structure values
393*803618f4SDamon Ding *
394*803618f4SDamon Ding * \param ico the ICO collection to add the image to
395*803618f4SDamon Ding * \param image a pointer to the ICO image to be initialised
396*803618f4SDamon Ding */
next_ico_image(ico_collection * ico,ico_image * image)397*803618f4SDamon Ding static bmp_result next_ico_image(ico_collection *ico, ico_image *image) {
398*803618f4SDamon Ding bmp_create(&image->bmp, &ico->bitmap_callbacks);
399*803618f4SDamon Ding image->next = ico->first;
400*803618f4SDamon Ding ico->first = image;
401*803618f4SDamon Ding return BMP_OK;
402*803618f4SDamon Ding }
403*803618f4SDamon Ding
404*803618f4SDamon Ding
405*803618f4SDamon Ding /**
406*803618f4SDamon Ding * Parse the icon file header
407*803618f4SDamon Ding *
408*803618f4SDamon Ding * \param ico The icon collection.
409*803618f4SDamon Ding * \param data The header data to parse.
410*803618f4SDamon Ding * \return BMP_OK on successful parse else error code
411*803618f4SDamon Ding */
ico_header_parse(ico_collection * ico,uint8_t * data)412*803618f4SDamon Ding static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data)
413*803618f4SDamon Ding {
414*803618f4SDamon Ding uint16_t count, i;
415*803618f4SDamon Ding bmp_result result;
416*803618f4SDamon Ding int area, max_area = 0;
417*803618f4SDamon Ding
418*803618f4SDamon Ding /* 6-byte ICO file header is:
419*803618f4SDamon Ding *
420*803618f4SDamon Ding * +0 INT16 Reserved (should be 0)
421*803618f4SDamon Ding * +2 UINT16 Type (1 for ICO, 2 for CUR)
422*803618f4SDamon Ding * +4 UINT16 Number of BMPs to follow
423*803618f4SDamon Ding */
424*803618f4SDamon Ding if (ico->buffer_size < ICO_FILE_HEADER_SIZE)
425*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
426*803618f4SDamon Ding // if (read_int16(data, 2) != 0x0000)
427*803618f4SDamon Ding // return BMP_DATA_ERROR;
428*803618f4SDamon Ding if (read_uint16(data, 2) != 0x0001)
429*803618f4SDamon Ding return BMP_DATA_ERROR;
430*803618f4SDamon Ding count = read_uint16(data, 4);
431*803618f4SDamon Ding if (count == 0)
432*803618f4SDamon Ding return BMP_DATA_ERROR;
433*803618f4SDamon Ding data += ICO_FILE_HEADER_SIZE;
434*803618f4SDamon Ding
435*803618f4SDamon Ding /* check if we have enough data for the directory */
436*803618f4SDamon Ding if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count)))
437*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
438*803618f4SDamon Ding
439*803618f4SDamon Ding /* Decode the BMP files.
440*803618f4SDamon Ding *
441*803618f4SDamon Ding * 16-byte ICO directory entry is:
442*803618f4SDamon Ding *
443*803618f4SDamon Ding * +0 UINT8 Width (0 for 256 pixels)
444*803618f4SDamon Ding * +1 UINT8 Height (0 for 256 pixels)
445*803618f4SDamon Ding * +2 UINT8 Colour count (0 if more than 256 colours)
446*803618f4SDamon Ding * +3 INT8 Reserved (should be 0, but may not be)
447*803618f4SDamon Ding * +4 UINT16 Colour Planes (should be 0 or 1)
448*803618f4SDamon Ding * +6 UINT16 Bits Per Pixel
449*803618f4SDamon Ding * +8 UINT32 Size of BMP info header + bitmap data in bytes
450*803618f4SDamon Ding * +12 UINT32 Offset (points to the BMP info header, not the bitmap data)
451*803618f4SDamon Ding */
452*803618f4SDamon Ding for (i = 0; i < count; i++) {
453*803618f4SDamon Ding ico_image *image;
454*803618f4SDamon Ding image = calloc(1, sizeof(ico_image));
455*803618f4SDamon Ding if (!image)
456*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
457*803618f4SDamon Ding result = next_ico_image(ico, image);
458*803618f4SDamon Ding if (result != BMP_OK)
459*803618f4SDamon Ding return result;
460*803618f4SDamon Ding image->bmp.width = read_uint8(data, 0);
461*803618f4SDamon Ding if (image->bmp.width == 0)
462*803618f4SDamon Ding image->bmp.width = 256;
463*803618f4SDamon Ding image->bmp.height = read_uint8(data, 1);
464*803618f4SDamon Ding if (image->bmp.height == 0)
465*803618f4SDamon Ding image->bmp.height = 256;
466*803618f4SDamon Ding image->bmp.buffer_size = read_uint32(data, 8);
467*803618f4SDamon Ding image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12);
468*803618f4SDamon Ding if (image->bmp.bmp_data + image->bmp.buffer_size >
469*803618f4SDamon Ding ico->ico_data + ico->buffer_size)
470*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
471*803618f4SDamon Ding image->bmp.ico = true;
472*803618f4SDamon Ding data += ICO_DIR_ENTRY_SIZE;
473*803618f4SDamon Ding
474*803618f4SDamon Ding /* Ensure that the bitmap data resides in the buffer */
475*803618f4SDamon Ding if (image->bmp.bmp_data - ico->ico_data >= 0 &&
476*803618f4SDamon Ding (uint32_t)(image->bmp.bmp_data -
477*803618f4SDamon Ding ico->ico_data) >= ico->buffer_size)
478*803618f4SDamon Ding return BMP_DATA_ERROR;
479*803618f4SDamon Ding
480*803618f4SDamon Ding /* Ensure that we have sufficient data to read the bitmap */
481*803618f4SDamon Ding if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >=
482*803618f4SDamon Ding ico->buffer_size - (ico->ico_data - data))
483*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
484*803618f4SDamon Ding
485*803618f4SDamon Ding result = bmp_info_header_parse(&image->bmp,
486*803618f4SDamon Ding image->bmp.bmp_data);
487*803618f4SDamon Ding if (result != BMP_OK)
488*803618f4SDamon Ding return result;
489*803618f4SDamon Ding
490*803618f4SDamon Ding /* adjust the size based on the images available */
491*803618f4SDamon Ding area = image->bmp.width * image->bmp.height;
492*803618f4SDamon Ding if (area > max_area) {
493*803618f4SDamon Ding ico->width = image->bmp.width;
494*803618f4SDamon Ding ico->height = image->bmp.height;
495*803618f4SDamon Ding max_area = area;
496*803618f4SDamon Ding }
497*803618f4SDamon Ding }
498*803618f4SDamon Ding return BMP_OK;
499*803618f4SDamon Ding }
500*803618f4SDamon Ding
501*803618f4SDamon Ding
502*803618f4SDamon Ding /**
503*803618f4SDamon Ding * Decode BMP data stored in 32bpp colour.
504*803618f4SDamon Ding *
505*803618f4SDamon Ding * \param bmp the BMP image to decode
506*803618f4SDamon Ding * \param start the data to decode, updated to last byte read on success
507*803618f4SDamon Ding * \param bytes the number of bytes of data available
508*803618f4SDamon Ding * \return BMP_OK on success
509*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
510*803618f4SDamon Ding * in this case, the image may be partially viewable
511*803618f4SDamon Ding */
bmp_decode_rgb32(bmp_image * bmp,uint8_t ** start,int bytes)512*803618f4SDamon Ding static bmp_result bmp_decode_rgb32(bmp_image *bmp, uint8_t **start, int bytes)
513*803618f4SDamon Ding {
514*803618f4SDamon Ding uint8_t *top, *bottom, *end, *data;
515*803618f4SDamon Ding uint32_t *scanline;
516*803618f4SDamon Ding uint32_t x, y;
517*803618f4SDamon Ding uint32_t swidth;
518*803618f4SDamon Ding uint8_t i;
519*803618f4SDamon Ding uint32_t word;
520*803618f4SDamon Ding
521*803618f4SDamon Ding data = *start;
522*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
523*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
524*803618f4SDamon Ding if (!top)
525*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
526*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
527*803618f4SDamon Ding end = data + bytes;
528*803618f4SDamon Ding bmp->decoded = true;
529*803618f4SDamon Ding
530*803618f4SDamon Ding /* Determine transparent index */
531*803618f4SDamon Ding if (bmp->limited_trans) {
532*803618f4SDamon Ding if ((data + 4) > end)
533*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
534*803618f4SDamon Ding if (bmp->encoding == BMP_ENCODING_BITFIELDS)
535*803618f4SDamon Ding bmp->transparent_index = read_uint32(data, 0);
536*803618f4SDamon Ding else
537*803618f4SDamon Ding bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16);
538*803618f4SDamon Ding }
539*803618f4SDamon Ding
540*803618f4SDamon Ding for (y = 0; y < bmp->height; y++) {
541*803618f4SDamon Ding if ((data + (4 * bmp->width)) > end)
542*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
543*803618f4SDamon Ding if (bmp->reversed)
544*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
545*803618f4SDamon Ding else
546*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
547*803618f4SDamon Ding if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
548*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
549*803618f4SDamon Ding word = read_uint32(data, 0);
550*803618f4SDamon Ding for (i = 0; i < 4; i++)
551*803618f4SDamon Ding if (bmp->shift[i] > 0)
552*803618f4SDamon Ding scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]);
553*803618f4SDamon Ding else
554*803618f4SDamon Ding scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i]));
555*803618f4SDamon Ding /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */
556*803618f4SDamon Ding if (bmp->opaque)
557*803618f4SDamon Ding scanline[x] |= ((unsigned)0xff << 24);
558*803618f4SDamon Ding data += 4;
559*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
560*803618f4SDamon Ding }
561*803618f4SDamon Ding } else {
562*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
563*803618f4SDamon Ding scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16);
564*803618f4SDamon Ding if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) {
565*803618f4SDamon Ding scanline[x] = bmp->trans_colour;
566*803618f4SDamon Ding }
567*803618f4SDamon Ding if (bmp->opaque) {
568*803618f4SDamon Ding scanline[x] |= ((unsigned)0xff << 24);
569*803618f4SDamon Ding } else {
570*803618f4SDamon Ding scanline[x] |= (unsigned)data[3] << 24;
571*803618f4SDamon Ding }
572*803618f4SDamon Ding data += 4;
573*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
574*803618f4SDamon Ding }
575*803618f4SDamon Ding }
576*803618f4SDamon Ding }
577*803618f4SDamon Ding *start = data;
578*803618f4SDamon Ding return BMP_OK;
579*803618f4SDamon Ding }
580*803618f4SDamon Ding
581*803618f4SDamon Ding
582*803618f4SDamon Ding /**
583*803618f4SDamon Ding * Decode BMP data stored in 24bpp colour.
584*803618f4SDamon Ding *
585*803618f4SDamon Ding * \param bmp the BMP image to decode
586*803618f4SDamon Ding * \param start the data to decode, updated to last byte read on success
587*803618f4SDamon Ding * \param bytes the number of bytes of data available
588*803618f4SDamon Ding * \return BMP_OK on success
589*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
590*803618f4SDamon Ding * in this case, the image may be partially viewable
591*803618f4SDamon Ding */
bmp_decode_rgb24(bmp_image * bmp,uint8_t ** start,int bytes)592*803618f4SDamon Ding static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes)
593*803618f4SDamon Ding {
594*803618f4SDamon Ding uint8_t *top, *bottom, *end, *data;
595*803618f4SDamon Ding uint32_t *scanline;
596*803618f4SDamon Ding uint32_t x, y;
597*803618f4SDamon Ding uint32_t swidth;
598*803618f4SDamon Ding uintptr_t addr;
599*803618f4SDamon Ding
600*803618f4SDamon Ding data = *start;
601*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
602*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
603*803618f4SDamon Ding if (!top) {
604*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
605*803618f4SDamon Ding }
606*803618f4SDamon Ding
607*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
608*803618f4SDamon Ding end = data + bytes;
609*803618f4SDamon Ding addr = ((uintptr_t)data) & 3;
610*803618f4SDamon Ding bmp->decoded = true;
611*803618f4SDamon Ding
612*803618f4SDamon Ding /* Determine transparent index */
613*803618f4SDamon Ding if (bmp->limited_trans) {
614*803618f4SDamon Ding if ((data + 3) > end) {
615*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
616*803618f4SDamon Ding }
617*803618f4SDamon Ding
618*803618f4SDamon Ding bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16);
619*803618f4SDamon Ding }
620*803618f4SDamon Ding
621*803618f4SDamon Ding for (y = 0; y < bmp->height; y++) {
622*803618f4SDamon Ding if ((data + (3 * bmp->width)) > end) {
623*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
624*803618f4SDamon Ding }
625*803618f4SDamon Ding
626*803618f4SDamon Ding if (bmp->reversed) {
627*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
628*803618f4SDamon Ding } else {
629*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
630*803618f4SDamon Ding }
631*803618f4SDamon Ding
632*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
633*803618f4SDamon Ding scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16);
634*803618f4SDamon Ding if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) {
635*803618f4SDamon Ding scanline[x] = bmp->trans_colour;
636*803618f4SDamon Ding } else {
637*803618f4SDamon Ding scanline[x] |= ((uint32_t)0xff << 24);
638*803618f4SDamon Ding }
639*803618f4SDamon Ding data += 3;
640*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
641*803618f4SDamon Ding }
642*803618f4SDamon Ding
643*803618f4SDamon Ding while (addr != (((uintptr_t)data) & 3)) {
644*803618f4SDamon Ding data++;
645*803618f4SDamon Ding }
646*803618f4SDamon Ding }
647*803618f4SDamon Ding *start = data;
648*803618f4SDamon Ding return BMP_OK;
649*803618f4SDamon Ding }
650*803618f4SDamon Ding
651*803618f4SDamon Ding
652*803618f4SDamon Ding /**
653*803618f4SDamon Ding * Decode BMP data stored in 16bpp colour.
654*803618f4SDamon Ding *
655*803618f4SDamon Ding * \param bmp the BMP image to decode
656*803618f4SDamon Ding * \param start the data to decode, updated to last byte read on success
657*803618f4SDamon Ding * \param bytes the number of bytes of data available
658*803618f4SDamon Ding * \return BMP_OK on success
659*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
660*803618f4SDamon Ding * in this case, the image may be partially viewable
661*803618f4SDamon Ding */
bmp_decode_rgb16(bmp_image * bmp,uint8_t ** start,int bytes)662*803618f4SDamon Ding static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes)
663*803618f4SDamon Ding {
664*803618f4SDamon Ding uint8_t *top, *bottom, *end, *data;
665*803618f4SDamon Ding uint32_t *scanline;
666*803618f4SDamon Ding uint32_t x, y, swidth;
667*803618f4SDamon Ding uintptr_t addr;
668*803618f4SDamon Ding uint8_t i;
669*803618f4SDamon Ding uint16_t word;
670*803618f4SDamon Ding
671*803618f4SDamon Ding data = *start;
672*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
673*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
674*803618f4SDamon Ding if (!top)
675*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
676*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
677*803618f4SDamon Ding end = data + bytes;
678*803618f4SDamon Ding addr = ((uintptr_t)data) & 3;
679*803618f4SDamon Ding bmp->decoded = true;
680*803618f4SDamon Ding
681*803618f4SDamon Ding /* Determine transparent index */
682*803618f4SDamon Ding if (bmp->limited_trans) {
683*803618f4SDamon Ding if ((data + 2) > end)
684*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
685*803618f4SDamon Ding bmp->transparent_index = read_uint16(data, 0);
686*803618f4SDamon Ding }
687*803618f4SDamon Ding
688*803618f4SDamon Ding for (y = 0; y < bmp->height; y++) {
689*803618f4SDamon Ding if ((data + (2 * bmp->width)) > end)
690*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
691*803618f4SDamon Ding if (bmp->reversed)
692*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
693*803618f4SDamon Ding else
694*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
695*803618f4SDamon Ding if (bmp->encoding == BMP_ENCODING_BITFIELDS) {
696*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
697*803618f4SDamon Ding word = read_uint16(data, 0);
698*803618f4SDamon Ding if ((bmp->limited_trans) && (word == bmp->transparent_index))
699*803618f4SDamon Ding scanline[x] = bmp->trans_colour;
700*803618f4SDamon Ding else {
701*803618f4SDamon Ding scanline[x] = 0;
702*803618f4SDamon Ding for (i = 0; i < 4; i++)
703*803618f4SDamon Ding if (bmp->shift[i] > 0)
704*803618f4SDamon Ding scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]);
705*803618f4SDamon Ding else
706*803618f4SDamon Ding scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i]));
707*803618f4SDamon Ding if (bmp->opaque)
708*803618f4SDamon Ding scanline[x] |= ((unsigned)0xff << 24);
709*803618f4SDamon Ding }
710*803618f4SDamon Ding data += 2;
711*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
712*803618f4SDamon Ding }
713*803618f4SDamon Ding } else {
714*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
715*803618f4SDamon Ding word = read_uint16(data, 0);
716*803618f4SDamon Ding if ((bmp->limited_trans) && (word == bmp->transparent_index))
717*803618f4SDamon Ding scanline[x] = bmp->trans_colour;
718*803618f4SDamon Ding else {
719*803618f4SDamon Ding /* 16-bit RGB defaults to RGB555 */
720*803618f4SDamon Ding scanline[x] = ((word & (31 << 0)) << 19) |
721*803618f4SDamon Ding ((word & (31 << 5)) << 6) |
722*803618f4SDamon Ding ((word & (31 << 10)) >> 7);
723*803618f4SDamon Ding }
724*803618f4SDamon Ding if (bmp->opaque)
725*803618f4SDamon Ding scanline[x] |= ((unsigned)0xff << 24);
726*803618f4SDamon Ding data += 2;
727*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x],0);
728*803618f4SDamon Ding }
729*803618f4SDamon Ding }
730*803618f4SDamon Ding while (addr != (((uintptr_t)data) & 3))
731*803618f4SDamon Ding data += 2;
732*803618f4SDamon Ding }
733*803618f4SDamon Ding *start = data;
734*803618f4SDamon Ding return BMP_OK;
735*803618f4SDamon Ding }
736*803618f4SDamon Ding
737*803618f4SDamon Ding
738*803618f4SDamon Ding /**
739*803618f4SDamon Ding * Decode BMP data stored with a palette and in 8bpp colour or less.
740*803618f4SDamon Ding *
741*803618f4SDamon Ding * \param bmp the BMP image to decode
742*803618f4SDamon Ding * \param start the data to decode, updated to last byte read on success
743*803618f4SDamon Ding * \param bytes the number of bytes of data available
744*803618f4SDamon Ding * \return BMP_OK on success
745*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
746*803618f4SDamon Ding * in this case, the image may be partially viewable
747*803618f4SDamon Ding */
bmp_decode_rgb(bmp_image * bmp,uint8_t ** start,int bytes)748*803618f4SDamon Ding static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes)
749*803618f4SDamon Ding {
750*803618f4SDamon Ding uint8_t *top, *bottom, *end, *data;
751*803618f4SDamon Ding uint32_t *scanline;
752*803618f4SDamon Ding uintptr_t addr;
753*803618f4SDamon Ding uint32_t x, y, swidth;
754*803618f4SDamon Ding uint8_t bit_shifts[8];
755*803618f4SDamon Ding uint8_t ppb = 8 / bmp->bpp;
756*803618f4SDamon Ding uint8_t bit_mask = (1 << bmp->bpp) - 1;
757*803618f4SDamon Ding uint8_t cur_byte = 0, bit, i;
758*803618f4SDamon Ding
759*803618f4SDamon Ding for (i = 0; i < ppb; i++)
760*803618f4SDamon Ding bit_shifts[i] = 8 - ((i + 1) * bmp->bpp);
761*803618f4SDamon Ding
762*803618f4SDamon Ding data = *start;
763*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
764*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
765*803618f4SDamon Ding if (!top)
766*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
767*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
768*803618f4SDamon Ding end = data + bytes;
769*803618f4SDamon Ding addr = ((uintptr_t)data) & 3;
770*803618f4SDamon Ding bmp->decoded = true;
771*803618f4SDamon Ding
772*803618f4SDamon Ding /* Determine transparent index */
773*803618f4SDamon Ding if (bmp->limited_trans) {
774*803618f4SDamon Ding uint32_t idx = (*data >> bit_shifts[0]) & bit_mask;
775*803618f4SDamon Ding if (idx >= bmp->colours)
776*803618f4SDamon Ding return BMP_DATA_ERROR;
777*803618f4SDamon Ding bmp->transparent_index = bmp->colour_table[idx];
778*803618f4SDamon Ding }
779*803618f4SDamon Ding
780*803618f4SDamon Ding for (y = 0; y < bmp->height; y++) {
781*803618f4SDamon Ding bit = 8;
782*803618f4SDamon Ding if ((data + ((bmp->width + ppb - 1) / ppb)) > end)
783*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
784*803618f4SDamon Ding if (bmp->reversed)
785*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
786*803618f4SDamon Ding else
787*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
788*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
789*803618f4SDamon Ding uint32_t idx;
790*803618f4SDamon Ding if (bit >= ppb) {
791*803618f4SDamon Ding bit = 0;
792*803618f4SDamon Ding cur_byte = *data++;
793*803618f4SDamon Ding }
794*803618f4SDamon Ding idx = (cur_byte >> bit_shifts[bit++]) & bit_mask;
795*803618f4SDamon Ding if (idx < bmp->colours) {
796*803618f4SDamon Ding /* ensure colour table index is in bounds */
797*803618f4SDamon Ding scanline[x] = bmp->colour_table[idx];
798*803618f4SDamon Ding if ((bmp->limited_trans) &&
799*803618f4SDamon Ding (scanline[x] == bmp->transparent_index)) {
800*803618f4SDamon Ding scanline[x] = bmp->trans_colour;
801*803618f4SDamon Ding }
802*803618f4SDamon Ding }
803*803618f4SDamon Ding }
804*803618f4SDamon Ding while (addr != (((uintptr_t)data) & 3))
805*803618f4SDamon Ding data++;
806*803618f4SDamon Ding }
807*803618f4SDamon Ding *start = data;
808*803618f4SDamon Ding return BMP_OK;
809*803618f4SDamon Ding }
810*803618f4SDamon Ding
811*803618f4SDamon Ding
812*803618f4SDamon Ding /**
813*803618f4SDamon Ding * Decode a 1bpp mask for an ICO
814*803618f4SDamon Ding *
815*803618f4SDamon Ding * \param bmp the BMP image to decode
816*803618f4SDamon Ding * \param data the data to decode
817*803618f4SDamon Ding * \param bytes the number of bytes of data available
818*803618f4SDamon Ding * \return BMP_OK on success
819*803618f4SDamon Ding */
bmp_decode_mask(bmp_image * bmp,uint8_t * data,int bytes)820*803618f4SDamon Ding static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes)
821*803618f4SDamon Ding {
822*803618f4SDamon Ding uint8_t *top, *bottom, *end;
823*803618f4SDamon Ding uint32_t *scanline;
824*803618f4SDamon Ding uintptr_t addr;
825*803618f4SDamon Ding uint32_t x, y, swidth;
826*803618f4SDamon Ding uint32_t cur_byte = 0;
827*803618f4SDamon Ding
828*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
829*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
830*803618f4SDamon Ding if (!top)
831*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
832*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
833*803618f4SDamon Ding end = data + bytes;
834*803618f4SDamon Ding
835*803618f4SDamon Ding addr = ((uintptr_t)data) & 3;
836*803618f4SDamon Ding
837*803618f4SDamon Ding for (y = 0; y < bmp->height; y++) {
838*803618f4SDamon Ding if ((data + (bmp->width >> 3)) > end)
839*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
840*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
841*803618f4SDamon Ding for (x = 0; x < bmp->width; x++) {
842*803618f4SDamon Ding if ((x & 7) == 0)
843*803618f4SDamon Ding cur_byte = *data++;
844*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
845*803618f4SDamon Ding if ((cur_byte & 128) == 0) {
846*803618f4SDamon Ding scanline[x] |= ((unsigned)0xff << 24);
847*803618f4SDamon Ding } else {
848*803618f4SDamon Ding scanline[x] &= 0xffffff;
849*803618f4SDamon Ding }
850*803618f4SDamon Ding scanline[x] = read_uint32((uint8_t *)&scanline[x], 0);
851*803618f4SDamon Ding cur_byte = cur_byte << 1;
852*803618f4SDamon Ding }
853*803618f4SDamon Ding while (addr != (((uintptr_t)data) & 3))
854*803618f4SDamon Ding data++;
855*803618f4SDamon Ding }
856*803618f4SDamon Ding return BMP_OK;
857*803618f4SDamon Ding }
858*803618f4SDamon Ding
859*803618f4SDamon Ding
860*803618f4SDamon Ding /**
861*803618f4SDamon Ding * Decode BMP data stored encoded in RLE8.
862*803618f4SDamon Ding *
863*803618f4SDamon Ding * \param bmp the BMP image to decode
864*803618f4SDamon Ding * \param data the data to decode
865*803618f4SDamon Ding * \param bytes the number of bytes of data available
866*803618f4SDamon Ding * \return BMP_OK on success
867*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
868*803618f4SDamon Ding * in this case, the image may be partially viewable
869*803618f4SDamon Ding */
870*803618f4SDamon Ding static bmp_result
bmp_decode_rle8(bmp_image * bmp,uint8_t * data,int bytes)871*803618f4SDamon Ding bmp_decode_rle8(bmp_image *bmp, uint8_t *data, int bytes)
872*803618f4SDamon Ding {
873*803618f4SDamon Ding uint8_t *top, *bottom, *end;
874*803618f4SDamon Ding uint32_t *scanline;
875*803618f4SDamon Ding uint32_t swidth;
876*803618f4SDamon Ding uint32_t i, length, pixels_left;
877*803618f4SDamon Ding uint32_t x = 0, y = 0, last_y = 0;
878*803618f4SDamon Ding uint32_t pixel = 0;
879*803618f4SDamon Ding
880*803618f4SDamon Ding if (bmp->ico)
881*803618f4SDamon Ding return BMP_DATA_ERROR;
882*803618f4SDamon Ding
883*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
884*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
885*803618f4SDamon Ding if (!top)
886*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
887*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
888*803618f4SDamon Ding end = data + bytes;
889*803618f4SDamon Ding bmp->decoded = true;
890*803618f4SDamon Ding
891*803618f4SDamon Ding do {
892*803618f4SDamon Ding if (data + 2 > end)
893*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
894*803618f4SDamon Ding length = *data++;
895*803618f4SDamon Ding if (length == 0) {
896*803618f4SDamon Ding length = *data++;
897*803618f4SDamon Ding switch (length) {
898*803618f4SDamon Ding case 0:
899*803618f4SDamon Ding /* 00 - 00 means end of scanline */
900*803618f4SDamon Ding x = 0;
901*803618f4SDamon Ding if (last_y == y) {
902*803618f4SDamon Ding y++;
903*803618f4SDamon Ding if (y >= bmp->height)
904*803618f4SDamon Ding return BMP_DATA_ERROR;
905*803618f4SDamon Ding }
906*803618f4SDamon Ding last_y = y;
907*803618f4SDamon Ding break;
908*803618f4SDamon Ding
909*803618f4SDamon Ding case 1:
910*803618f4SDamon Ding /* 00 - 01 means end of RLE data */
911*803618f4SDamon Ding return BMP_OK;
912*803618f4SDamon Ding
913*803618f4SDamon Ding case 2:
914*803618f4SDamon Ding /* 00 - 02 - XX - YY means move cursor */
915*803618f4SDamon Ding if (data + 2 > end)
916*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
917*803618f4SDamon Ding x += *data++;
918*803618f4SDamon Ding if (x >= bmp->width)
919*803618f4SDamon Ding return BMP_DATA_ERROR;
920*803618f4SDamon Ding y += *data++;
921*803618f4SDamon Ding if (y >= bmp->height)
922*803618f4SDamon Ding return BMP_DATA_ERROR;
923*803618f4SDamon Ding break;
924*803618f4SDamon Ding
925*803618f4SDamon Ding default:
926*803618f4SDamon Ding /* 00 - NN means escape NN pixels */
927*803618f4SDamon Ding if (bmp->reversed) {
928*803618f4SDamon Ding pixels_left = (bmp->height - y) * bmp->width - x;
929*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
930*803618f4SDamon Ding } else {
931*803618f4SDamon Ding pixels_left = (y + 1) * bmp->width - x;
932*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
933*803618f4SDamon Ding }
934*803618f4SDamon Ding if (length > pixels_left)
935*803618f4SDamon Ding length = pixels_left;
936*803618f4SDamon Ding if (data + length > end)
937*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
938*803618f4SDamon Ding
939*803618f4SDamon Ding /* the following code could be easily optimised
940*803618f4SDamon Ding * by simply checking the bounds on entry and
941*803618f4SDamon Ding * using some simple copying routines if so
942*803618f4SDamon Ding */
943*803618f4SDamon Ding for (i = 0; i < length; i++) {
944*803618f4SDamon Ding uint32_t idx = (uint32_t) *data++;
945*803618f4SDamon Ding if (x >= bmp->width) {
946*803618f4SDamon Ding x = 0;
947*803618f4SDamon Ding y++;
948*803618f4SDamon Ding if (y >= bmp->height)
949*803618f4SDamon Ding return BMP_DATA_ERROR;
950*803618f4SDamon Ding if (bmp->reversed) {
951*803618f4SDamon Ding scanline += bmp->width;
952*803618f4SDamon Ding } else {
953*803618f4SDamon Ding scanline -= bmp->width;
954*803618f4SDamon Ding }
955*803618f4SDamon Ding }
956*803618f4SDamon Ding if (idx >= bmp->colours)
957*803618f4SDamon Ding return BMP_DATA_ERROR;
958*803618f4SDamon Ding scanline[x++] = bmp->colour_table[idx];
959*803618f4SDamon Ding }
960*803618f4SDamon Ding
961*803618f4SDamon Ding if ((length & 1) && (*data++ != 0x00))
962*803618f4SDamon Ding return BMP_DATA_ERROR;
963*803618f4SDamon Ding
964*803618f4SDamon Ding break;
965*803618f4SDamon Ding }
966*803618f4SDamon Ding } else {
967*803618f4SDamon Ding uint32_t idx;
968*803618f4SDamon Ding
969*803618f4SDamon Ding /* NN means perform RLE for NN pixels */
970*803618f4SDamon Ding if (bmp->reversed) {
971*803618f4SDamon Ding pixels_left = (bmp->height - y) * bmp->width - x;
972*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
973*803618f4SDamon Ding } else {
974*803618f4SDamon Ding pixels_left = (y + 1) * bmp->width - x;
975*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
976*803618f4SDamon Ding }
977*803618f4SDamon Ding if (length > pixels_left)
978*803618f4SDamon Ding length = pixels_left;
979*803618f4SDamon Ding
980*803618f4SDamon Ding /* boundary checking */
981*803618f4SDamon Ding if (data + 1 > end)
982*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
983*803618f4SDamon Ding
984*803618f4SDamon Ding /* the following code could be easily optimised by
985*803618f4SDamon Ding * simply checking the bounds on entry and using some
986*803618f4SDamon Ding * simply copying routines if so
987*803618f4SDamon Ding */
988*803618f4SDamon Ding idx = (uint32_t) *data++;
989*803618f4SDamon Ding if (idx >= bmp->colours)
990*803618f4SDamon Ding return BMP_DATA_ERROR;
991*803618f4SDamon Ding
992*803618f4SDamon Ding pixel = bmp->colour_table[idx];
993*803618f4SDamon Ding for (i = 0; i < length; i++) {
994*803618f4SDamon Ding if (x >= bmp->width) {
995*803618f4SDamon Ding x = 0;
996*803618f4SDamon Ding y++;
997*803618f4SDamon Ding if (y >= bmp->height)
998*803618f4SDamon Ding return BMP_DATA_ERROR;
999*803618f4SDamon Ding if (bmp->reversed) {
1000*803618f4SDamon Ding scanline += bmp->width;
1001*803618f4SDamon Ding } else {
1002*803618f4SDamon Ding scanline -= bmp->width;
1003*803618f4SDamon Ding }
1004*803618f4SDamon Ding }
1005*803618f4SDamon Ding scanline[x++] = pixel;
1006*803618f4SDamon Ding }
1007*803618f4SDamon Ding }
1008*803618f4SDamon Ding } while (data < end);
1009*803618f4SDamon Ding
1010*803618f4SDamon Ding return BMP_OK;
1011*803618f4SDamon Ding }
1012*803618f4SDamon Ding
1013*803618f4SDamon Ding
1014*803618f4SDamon Ding /**
1015*803618f4SDamon Ding * Decode BMP data stored encoded in RLE4.
1016*803618f4SDamon Ding *
1017*803618f4SDamon Ding * \param bmp the BMP image to decode
1018*803618f4SDamon Ding * \param data the data to decode
1019*803618f4SDamon Ding * \param bytes the number of bytes of data available
1020*803618f4SDamon Ding * \return BMP_OK on success
1021*803618f4SDamon Ding * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly;
1022*803618f4SDamon Ding * in this case, the image may be partially viewable
1023*803618f4SDamon Ding */
1024*803618f4SDamon Ding static bmp_result
bmp_decode_rle4(bmp_image * bmp,uint8_t * data,int bytes)1025*803618f4SDamon Ding bmp_decode_rle4(bmp_image *bmp, uint8_t *data, int bytes)
1026*803618f4SDamon Ding {
1027*803618f4SDamon Ding uint8_t *top, *bottom, *end;
1028*803618f4SDamon Ding uint32_t *scanline;
1029*803618f4SDamon Ding uint32_t swidth;
1030*803618f4SDamon Ding uint32_t i, length, pixels_left;
1031*803618f4SDamon Ding uint32_t x = 0, y = 0, last_y = 0;
1032*803618f4SDamon Ding uint32_t pixel = 0, pixel2;
1033*803618f4SDamon Ding
1034*803618f4SDamon Ding if (bmp->ico)
1035*803618f4SDamon Ding return BMP_DATA_ERROR;
1036*803618f4SDamon Ding
1037*803618f4SDamon Ding swidth = sizeof(uint32_t) * bmp->width;
1038*803618f4SDamon Ding top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap);
1039*803618f4SDamon Ding if (!top)
1040*803618f4SDamon Ding return BMP_INSUFFICIENT_MEMORY;
1041*803618f4SDamon Ding bottom = top + (uint64_t)swidth * (bmp->height - 1);
1042*803618f4SDamon Ding end = data + bytes;
1043*803618f4SDamon Ding bmp->decoded = true;
1044*803618f4SDamon Ding
1045*803618f4SDamon Ding do {
1046*803618f4SDamon Ding if (data + 2 > end)
1047*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
1048*803618f4SDamon Ding length = *data++;
1049*803618f4SDamon Ding if (length == 0) {
1050*803618f4SDamon Ding length = *data++;
1051*803618f4SDamon Ding switch (length) {
1052*803618f4SDamon Ding case 0:
1053*803618f4SDamon Ding /* 00 - 00 means end of scanline */
1054*803618f4SDamon Ding x = 0;
1055*803618f4SDamon Ding if (last_y == y) {
1056*803618f4SDamon Ding y++;
1057*803618f4SDamon Ding if (y >= bmp->height)
1058*803618f4SDamon Ding return BMP_DATA_ERROR;
1059*803618f4SDamon Ding }
1060*803618f4SDamon Ding last_y = y;
1061*803618f4SDamon Ding break;
1062*803618f4SDamon Ding
1063*803618f4SDamon Ding case 1:
1064*803618f4SDamon Ding /* 00 - 01 means end of RLE data */
1065*803618f4SDamon Ding return BMP_OK;
1066*803618f4SDamon Ding
1067*803618f4SDamon Ding case 2:
1068*803618f4SDamon Ding /* 00 - 02 - XX - YY means move cursor */
1069*803618f4SDamon Ding if (data + 2 > end)
1070*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
1071*803618f4SDamon Ding x += *data++;
1072*803618f4SDamon Ding if (x >= bmp->width)
1073*803618f4SDamon Ding return BMP_DATA_ERROR;
1074*803618f4SDamon Ding y += *data++;
1075*803618f4SDamon Ding if (y >= bmp->height)
1076*803618f4SDamon Ding return BMP_DATA_ERROR;
1077*803618f4SDamon Ding break;
1078*803618f4SDamon Ding
1079*803618f4SDamon Ding default:
1080*803618f4SDamon Ding /* 00 - NN means escape NN pixels */
1081*803618f4SDamon Ding if (bmp->reversed) {
1082*803618f4SDamon Ding pixels_left = (bmp->height - y) * bmp->width - x;
1083*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
1084*803618f4SDamon Ding } else {
1085*803618f4SDamon Ding pixels_left = (y + 1) * bmp->width - x;
1086*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
1087*803618f4SDamon Ding }
1088*803618f4SDamon Ding if (length > pixels_left)
1089*803618f4SDamon Ding length = pixels_left;
1090*803618f4SDamon Ding if (data + ((length + 1) / 2) > end)
1091*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
1092*803618f4SDamon Ding
1093*803618f4SDamon Ding /* the following code could be easily optimised
1094*803618f4SDamon Ding * by simply checking the bounds on entry and
1095*803618f4SDamon Ding * using some simple copying routines
1096*803618f4SDamon Ding */
1097*803618f4SDamon Ding
1098*803618f4SDamon Ding for (i = 0; i < length; i++) {
1099*803618f4SDamon Ding if (x >= bmp->width) {
1100*803618f4SDamon Ding x = 0;
1101*803618f4SDamon Ding y++;
1102*803618f4SDamon Ding if (y >= bmp->height)
1103*803618f4SDamon Ding return BMP_DATA_ERROR;
1104*803618f4SDamon Ding if (bmp->reversed) {
1105*803618f4SDamon Ding scanline += bmp->width;
1106*803618f4SDamon Ding } else {
1107*803618f4SDamon Ding scanline -= bmp->width;
1108*803618f4SDamon Ding }
1109*803618f4SDamon Ding
1110*803618f4SDamon Ding }
1111*803618f4SDamon Ding if ((i & 1) == 0) {
1112*803618f4SDamon Ding pixel = *data++;
1113*803618f4SDamon Ding if ((pixel >> 4) >= bmp->colours)
1114*803618f4SDamon Ding return BMP_DATA_ERROR;
1115*803618f4SDamon Ding scanline[x++] = bmp->colour_table
1116*803618f4SDamon Ding [pixel >> 4];
1117*803618f4SDamon Ding } else {
1118*803618f4SDamon Ding if ((pixel & 0xf) >= bmp->colours)
1119*803618f4SDamon Ding return BMP_DATA_ERROR;
1120*803618f4SDamon Ding scanline[x++] = bmp->colour_table
1121*803618f4SDamon Ding [pixel & 0xf];
1122*803618f4SDamon Ding }
1123*803618f4SDamon Ding }
1124*803618f4SDamon Ding length = (length + 1) >> 1;
1125*803618f4SDamon Ding
1126*803618f4SDamon Ding if ((length & 1) && (*data++ != 0x00))
1127*803618f4SDamon Ding return BMP_DATA_ERROR;
1128*803618f4SDamon Ding
1129*803618f4SDamon Ding break;
1130*803618f4SDamon Ding }
1131*803618f4SDamon Ding } else {
1132*803618f4SDamon Ding /* NN means perform RLE for NN pixels */
1133*803618f4SDamon Ding if (bmp->reversed) {
1134*803618f4SDamon Ding pixels_left = (bmp->height - y) * bmp->width - x;
1135*803618f4SDamon Ding scanline = (void *)(top + (y * swidth));
1136*803618f4SDamon Ding } else {
1137*803618f4SDamon Ding pixels_left = (y + 1) * bmp->width - x;
1138*803618f4SDamon Ding scanline = (void *)(bottom - (y * swidth));
1139*803618f4SDamon Ding }
1140*803618f4SDamon Ding if (length > pixels_left)
1141*803618f4SDamon Ding length = pixels_left;
1142*803618f4SDamon Ding
1143*803618f4SDamon Ding /* boundary checking */
1144*803618f4SDamon Ding if (data + 1 > end)
1145*803618f4SDamon Ding return BMP_INSUFFICIENT_DATA;
1146*803618f4SDamon Ding
1147*803618f4SDamon Ding /* the following code could be easily optimised by
1148*803618f4SDamon Ding * simply checking the bounds on entry and using some
1149*803618f4SDamon Ding * simple copying routines
1150*803618f4SDamon Ding */
1151*803618f4SDamon Ding
1152*803618f4SDamon Ding pixel2 = *data++;
1153*803618f4SDamon Ding if ((pixel2 >> 4) >= bmp->colours ||
1154*803618f4SDamon Ding (pixel2 & 0xf) >= bmp->colours)
1155*803618f4SDamon Ding return BMP_DATA_ERROR;
1156*803618f4SDamon Ding pixel = bmp->colour_table[pixel2 >> 4];
1157*803618f4SDamon Ding pixel2 = bmp->colour_table[pixel2 & 0xf];
1158*803618f4SDamon Ding for (i = 0; i < length; i++) {
1159*803618f4SDamon Ding if (x >= bmp->width) {
1160*803618f4SDamon Ding x = 0;
1161*803618f4SDamon Ding y++;
1162*803618f4SDamon Ding if (y >= bmp->height)
1163*803618f4SDamon Ding return BMP_DATA_ERROR;
1164*803618f4SDamon Ding if (bmp->reversed) {
1165*803618f4SDamon Ding scanline += bmp->width;
1166*803618f4SDamon Ding } else {
1167*803618f4SDamon Ding scanline -= bmp->width;
1168*803618f4SDamon Ding }
1169*803618f4SDamon Ding }
1170*803618f4SDamon Ding if ((i & 1) == 0)
1171*803618f4SDamon Ding scanline[x++] = pixel;
1172*803618f4SDamon Ding else
1173*803618f4SDamon Ding scanline[x++] = pixel2;
1174*803618f4SDamon Ding }
1175*803618f4SDamon Ding
1176*803618f4SDamon Ding }
1177*803618f4SDamon Ding } while (data < end);
1178*803618f4SDamon Ding
1179*803618f4SDamon Ding return BMP_OK;
1180*803618f4SDamon Ding }
1181*803618f4SDamon Ding
1182*803618f4SDamon Ding
1183*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
1184*803618f4SDamon Ding bmp_result
bmp_create(bmp_image * bmp,bmp_bitmap_callback_vt * bitmap_callbacks)1185*803618f4SDamon Ding bmp_create(bmp_image *bmp,
1186*803618f4SDamon Ding bmp_bitmap_callback_vt *bitmap_callbacks)
1187*803618f4SDamon Ding {
1188*803618f4SDamon Ding memset(bmp, 0, sizeof(bmp_image));
1189*803618f4SDamon Ding bmp->bitmap_callbacks = *bitmap_callbacks;
1190*803618f4SDamon Ding
1191*803618f4SDamon Ding return BMP_OK;
1192*803618f4SDamon Ding }
1193*803618f4SDamon Ding
1194*803618f4SDamon Ding
1195*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
1196*803618f4SDamon Ding bmp_result
ico_collection_create(ico_collection * ico,bmp_bitmap_callback_vt * bitmap_callbacks)1197*803618f4SDamon Ding ico_collection_create(ico_collection *ico,
1198*803618f4SDamon Ding bmp_bitmap_callback_vt *bitmap_callbacks)
1199*803618f4SDamon Ding {
1200*803618f4SDamon Ding
1201*803618f4SDamon Ding memset(ico, 0, sizeof(ico_collection));
1202*803618f4SDamon Ding ico->bitmap_callbacks = *bitmap_callbacks;
1203*803618f4SDamon Ding
1204*803618f4SDamon Ding return BMP_OK;
1205*803618f4SDamon Ding }
1206*803618f4SDamon Ding
1207*803618f4SDamon Ding
1208*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
bmp_analyse(bmp_image * bmp,size_t size,uint8_t * data)1209*803618f4SDamon Ding bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data)
1210*803618f4SDamon Ding {
1211*803618f4SDamon Ding bmp_result res;
1212*803618f4SDamon Ding
1213*803618f4SDamon Ding /* ensure we aren't already initialised */
1214*803618f4SDamon Ding if (bmp->bitmap) {
1215*803618f4SDamon Ding return BMP_OK;
1216*803618f4SDamon Ding }
1217*803618f4SDamon Ding
1218*803618f4SDamon Ding /* initialize source data values */
1219*803618f4SDamon Ding bmp->buffer_size = size;
1220*803618f4SDamon Ding bmp->bmp_data = data;
1221*803618f4SDamon Ding
1222*803618f4SDamon Ding res = bmp_file_header_parse(bmp, data);
1223*803618f4SDamon Ding if (res == BMP_OK) {
1224*803618f4SDamon Ding res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE);
1225*803618f4SDamon Ding }
1226*803618f4SDamon Ding return res;
1227*803618f4SDamon Ding }
1228*803618f4SDamon Ding
1229*803618f4SDamon Ding
1230*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
ico_analyse(ico_collection * ico,size_t size,uint8_t * data)1231*803618f4SDamon Ding bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data)
1232*803618f4SDamon Ding {
1233*803618f4SDamon Ding /* ensure we aren't already initialised */
1234*803618f4SDamon Ding if (ico->first)
1235*803618f4SDamon Ding return BMP_OK;
1236*803618f4SDamon Ding
1237*803618f4SDamon Ding /* initialize values */
1238*803618f4SDamon Ding ico->buffer_size = size;
1239*803618f4SDamon Ding ico->ico_data = data;
1240*803618f4SDamon Ding
1241*803618f4SDamon Ding return ico_header_parse(ico, data);
1242*803618f4SDamon Ding }
1243*803618f4SDamon Ding
1244*803618f4SDamon Ding
1245*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
bmp_decode(bmp_image * bmp)1246*803618f4SDamon Ding bmp_result bmp_decode(bmp_image *bmp)
1247*803618f4SDamon Ding {
1248*803618f4SDamon Ding uint8_t *data;
1249*803618f4SDamon Ding uint32_t bytes;
1250*803618f4SDamon Ding bmp_result result = BMP_OK;
1251*803618f4SDamon Ding
1252*803618f4SDamon Ding data = bmp->bmp_data + bmp->bitmap_offset;
1253*803618f4SDamon Ding bytes = bmp->buffer_size - bmp->bitmap_offset;
1254*803618f4SDamon Ding
1255*803618f4SDamon Ding switch (bmp->encoding) {
1256*803618f4SDamon Ding case BMP_ENCODING_RGB:
1257*803618f4SDamon Ding switch (bmp->bpp) {
1258*803618f4SDamon Ding case 32:
1259*803618f4SDamon Ding result = bmp_decode_rgb32(bmp, &data, bytes);
1260*803618f4SDamon Ding break;
1261*803618f4SDamon Ding
1262*803618f4SDamon Ding case 24:
1263*803618f4SDamon Ding result = bmp_decode_rgb24(bmp, &data, bytes);
1264*803618f4SDamon Ding break;
1265*803618f4SDamon Ding
1266*803618f4SDamon Ding case 16:
1267*803618f4SDamon Ding result = bmp_decode_rgb16(bmp, &data, bytes);
1268*803618f4SDamon Ding break;
1269*803618f4SDamon Ding
1270*803618f4SDamon Ding default:
1271*803618f4SDamon Ding result = bmp_decode_rgb(bmp, &data, bytes);
1272*803618f4SDamon Ding break;
1273*803618f4SDamon Ding }
1274*803618f4SDamon Ding break;
1275*803618f4SDamon Ding
1276*803618f4SDamon Ding case BMP_ENCODING_RLE8:
1277*803618f4SDamon Ding result = bmp_decode_rle8(bmp, data, bytes);
1278*803618f4SDamon Ding break;
1279*803618f4SDamon Ding
1280*803618f4SDamon Ding case BMP_ENCODING_RLE4:
1281*803618f4SDamon Ding result = bmp_decode_rle4(bmp, data, bytes);
1282*803618f4SDamon Ding break;
1283*803618f4SDamon Ding
1284*803618f4SDamon Ding case BMP_ENCODING_BITFIELDS:
1285*803618f4SDamon Ding switch (bmp->bpp) {
1286*803618f4SDamon Ding case 32:
1287*803618f4SDamon Ding result = bmp_decode_rgb32(bmp, &data, bytes);
1288*803618f4SDamon Ding break;
1289*803618f4SDamon Ding
1290*803618f4SDamon Ding case 16:
1291*803618f4SDamon Ding result = bmp_decode_rgb16(bmp, &data, bytes);
1292*803618f4SDamon Ding break;
1293*803618f4SDamon Ding
1294*803618f4SDamon Ding default:
1295*803618f4SDamon Ding result = BMP_DATA_ERROR;
1296*803618f4SDamon Ding break;
1297*803618f4SDamon Ding }
1298*803618f4SDamon Ding break;
1299*803618f4SDamon Ding }
1300*803618f4SDamon Ding
1301*803618f4SDamon Ding /* icons with less than 32bpp have a 1bpp alpha mask */
1302*803618f4SDamon Ding if ((result == BMP_OK) && (bmp->ico) && (bmp->bpp != 32)) {
1303*803618f4SDamon Ding bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data;
1304*803618f4SDamon Ding result = bmp_decode_mask(bmp, data, bytes);
1305*803618f4SDamon Ding }
1306*803618f4SDamon Ding return result;
1307*803618f4SDamon Ding }
1308*803618f4SDamon Ding
1309*803618f4SDamon Ding
1310*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
bmp_decode_trans(bmp_image * bmp,uint32_t colour)1311*803618f4SDamon Ding bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour)
1312*803618f4SDamon Ding {
1313*803618f4SDamon Ding bmp->limited_trans = true;
1314*803618f4SDamon Ding bmp->trans_colour = colour;
1315*803618f4SDamon Ding return bmp_decode(bmp);
1316*803618f4SDamon Ding }
1317*803618f4SDamon Ding
1318*803618f4SDamon Ding
1319*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
ico_find(ico_collection * ico,uint16_t width,uint16_t height)1320*803618f4SDamon Ding bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height)
1321*803618f4SDamon Ding {
1322*803618f4SDamon Ding bmp_image *bmp = NULL;
1323*803618f4SDamon Ding ico_image *image;
1324*803618f4SDamon Ding int x, y, cur, distance = (1 << 24);
1325*803618f4SDamon Ding
1326*803618f4SDamon Ding if (width == 0)
1327*803618f4SDamon Ding width = ico->width;
1328*803618f4SDamon Ding if (height == 0)
1329*803618f4SDamon Ding height = ico->height;
1330*803618f4SDamon Ding for (image = ico->first; image; image = image->next) {
1331*803618f4SDamon Ding if ((image->bmp.width == width) && (image->bmp.height == height))
1332*803618f4SDamon Ding return &image->bmp;
1333*803618f4SDamon Ding x = image->bmp.width - width;
1334*803618f4SDamon Ding y = image->bmp.height - height;
1335*803618f4SDamon Ding cur = (x * x) + (y * y);
1336*803618f4SDamon Ding if (cur < distance) {
1337*803618f4SDamon Ding distance = cur;
1338*803618f4SDamon Ding bmp = &image->bmp;
1339*803618f4SDamon Ding }
1340*803618f4SDamon Ding }
1341*803618f4SDamon Ding return bmp;
1342*803618f4SDamon Ding }
1343*803618f4SDamon Ding
1344*803618f4SDamon Ding
1345*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
bmp_finalise(bmp_image * bmp)1346*803618f4SDamon Ding void bmp_finalise(bmp_image *bmp)
1347*803618f4SDamon Ding {
1348*803618f4SDamon Ding if (bmp->bitmap)
1349*803618f4SDamon Ding bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap);
1350*803618f4SDamon Ding bmp->bitmap = NULL;
1351*803618f4SDamon Ding if (bmp->colour_table)
1352*803618f4SDamon Ding free(bmp->colour_table);
1353*803618f4SDamon Ding bmp->colour_table = NULL;
1354*803618f4SDamon Ding }
1355*803618f4SDamon Ding
1356*803618f4SDamon Ding
1357*803618f4SDamon Ding /* exported interface documented in libnsbmp.h */
ico_finalise(ico_collection * ico)1358*803618f4SDamon Ding void ico_finalise(ico_collection *ico)
1359*803618f4SDamon Ding {
1360*803618f4SDamon Ding ico_image *image;
1361*803618f4SDamon Ding
1362*803618f4SDamon Ding for (image = ico->first; image; image = image->next)
1363*803618f4SDamon Ding bmp_finalise(&image->bmp);
1364*803618f4SDamon Ding while (ico->first) {
1365*803618f4SDamon Ding image = ico->first;
1366*803618f4SDamon Ding ico->first = image->next;
1367*803618f4SDamon Ding free(image);
1368*803618f4SDamon Ding }
1369*803618f4SDamon Ding }
1370