xref: /rk3399_rockchip-uboot/drivers/video/drm/libnsbmp.c (revision 803618f46efadbc48d59bd07dce23c74b2a536bd)
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