xref: /PHP-8.2/ext/gd/libgd/gd_bmp.c (revision 6f663c3a)
1 /*
2 	gd_bmp.c
3 
4 	Bitmap format support for libgd
5 
6 	* Written 2007, Scott MacVicar
7 	---------------------------------------------------------------------------
8 
9 	Todo:
10 
11 	Bitfield encoding
12 
13 	----------------------------------------------------------------------------
14  */
15 /* $Id$ */
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19 
20 #include <stdio.h>
21 #include <math.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include "gd.h"
25 #include "gdhelpers.h"
26 #include "bmp.h"
27 
28 static int compress_row(unsigned char *uncompressed_row, int length);
29 static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data);
30 
31 static int bmp_read_header(gdIOCtxPtr infile, bmp_hdr_t *hdr);
32 static int bmp_read_info(gdIOCtxPtr infile, bmp_info_t *info);
33 static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info);
34 static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info);
35 static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info);
36 
37 static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
38 static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
39 static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
40 static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
41 static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info);
42 
43 #define BMP_DEBUG(s)
44 
gdBMPPutWord(gdIOCtx * out,int w)45 static int gdBMPPutWord(gdIOCtx *out, int w)
46 {
47 	/* Byte order is little-endian */
48 	gdPutC(w & 0xFF, out);
49 	gdPutC((w >> 8) & 0xFF, out);
50 	return 0;
51 }
52 
gdBMPPutInt(gdIOCtx * out,int w)53 static int gdBMPPutInt(gdIOCtx *out, int w)
54 {
55 	/* Byte order is little-endian */
56 	gdPutC(w & 0xFF, out);
57 	gdPutC((w >> 8) & 0xFF, out);
58 	gdPutC((w >> 16) & 0xFF, out);
59 	gdPutC((w >> 24) & 0xFF, out);
60 	return 0;
61 }
62 
63 /*
64 	Function: gdImageBmpPtr
65 */
gdImageBmpPtr(gdImagePtr im,int * size,int compression)66 void * gdImageBmpPtr(gdImagePtr im, int *size, int compression)
67 {
68 	void *rv;
69 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
70 	if (out == NULL) return NULL;
71 	gdImageBmpCtx(im, out, compression);
72 	rv = gdDPExtractData(out, size);
73 	out->gd_free(out);
74 	return rv;
75 }
76 
77 /*
78 	Function: gdImageBmp
79 */
gdImageBmp(gdImagePtr im,FILE * outFile,int compression)80 void gdImageBmp(gdImagePtr im, FILE *outFile, int compression)
81 {
82 	gdIOCtx *out = gdNewFileCtx(outFile);
83 	if (out == NULL) return;
84 	gdImageBmpCtx(im, out, compression);
85 	out->gd_free(out);
86 }
87 
88 /*
89 	Function: gdImageBmpCtx
90 */
gdImageBmpCtx(gdImagePtr im,gdIOCtxPtr out,int compression)91 void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
92 {
93 	int bitmap_size = 0, info_size, total_size, padding;
94 	int i, row, xpos, pixel;
95 	int error = 0;
96 	unsigned char *uncompressed_row = NULL, *uncompressed_row_start = NULL;
97 	FILE *tmpfile_for_compression = NULL;
98 	gdIOCtxPtr out_original = NULL;
99 
100 	/* No compression if its true colour or we don't support seek */
101 	if (im->trueColor) {
102 		compression = 0;
103 	}
104 
105 	if (compression == 1 && !out->seek) {
106 		/* Try to create a temp file where we can seek */
107 		if ((tmpfile_for_compression = tmpfile()) == NULL) {
108 			compression = 0;
109 		} else {
110 			out_original = out;
111 			if ((out = (gdIOCtxPtr)gdNewFileCtx(tmpfile_for_compression)) == NULL) {
112 				out = out_original;
113 				out_original = NULL;
114 				compression = 0;
115 			}
116 		}
117 	}
118 
119 	bitmap_size = ((im->sx * (im->trueColor ? 24 : 8)) / 8) * im->sy;
120 
121 	/* 40 byte Windows v3 header */
122 	info_size = BMP_WINDOWS_V3;
123 
124 	/* data for the palette */
125 	if (!im->trueColor) {
126 		info_size += im->colorsTotal * 4;
127 		if (compression) {
128 			bitmap_size = 0;
129 		}
130 	}
131 
132 	/* bitmap header + info header + data */
133 	total_size = 14 + info_size + bitmap_size;
134 
135 	/* write bmp header info */
136 	gdPutBuf("BM", 2, out);
137 	gdBMPPutInt(out, total_size);
138 	gdBMPPutWord(out, 0);
139 	gdBMPPutWord(out, 0);
140 	gdBMPPutInt(out, 14 + info_size);
141 
142 	/* write Windows v3 headers */
143 	gdBMPPutInt(out, BMP_WINDOWS_V3); /* header size */
144 	gdBMPPutInt(out, im->sx); /* width */
145 	gdBMPPutInt(out, im->sy); /* height */
146 	gdBMPPutWord(out, 1); /* colour planes */
147 	gdBMPPutWord(out, (im->trueColor ? 24 : 8)); /* bit count */
148 	gdBMPPutInt(out, (compression ? BMP_BI_RLE8 : BMP_BI_RGB)); /* compression */
149 	gdBMPPutInt(out, bitmap_size); /* image size */
150 	gdBMPPutInt(out, 0); /* H resolution */
151 	gdBMPPutInt(out, 0); /* V ressolution */
152 	gdBMPPutInt(out, im->colorsTotal); /* colours used */
153 	gdBMPPutInt(out, 0); /* important colours */
154 
155 	/* The line must be divisible by 4, else its padded with NULLs */
156 	padding = ((int)(im->trueColor ? 3 : 1) * im->sx) % 4;
157 	if (padding) {
158 		padding = 4 - padding;
159 	}
160 
161 	/* 8-bit colours */
162 	if (!im->trueColor) {
163 		for(i = 0; i< im->colorsTotal; ++i) {
164 			Putchar(gdImageBlue(im, i), out);
165 			Putchar(gdImageGreen(im, i), out);
166 			Putchar(gdImageRed(im, i), out);
167 			Putchar(0, out);
168 		}
169 
170 		if (compression) {
171 			/* Can potentially change this to X + ((X / 128) * 3) */
172 			uncompressed_row = uncompressed_row_start = (unsigned char *) gdCalloc(gdImageSX(im) * 2, sizeof(char));
173 			if (!uncompressed_row) {
174 				/* malloc failed */
175 				goto cleanup;
176 			}
177 		}
178 
179 		for (row = (im->sy - 1); row >= 0; row--) {
180 			if (compression) {
181 				memset (uncompressed_row, 0, gdImageSX(im));
182 			}
183 
184 			for (xpos = 0; xpos < im->sx; xpos++) {
185 				if (compression) {
186 					*uncompressed_row++ = (unsigned char)gdImageGetPixel(im, xpos, row);
187 				} else {
188 					Putchar(gdImageGetPixel(im, xpos, row), out);
189 				}
190 			}
191 
192 			if (!compression) {
193 				/* Add padding to make sure we have n mod 4 == 0 bytes per row */
194 				for (xpos = padding; xpos > 0; --xpos) {
195 					Putchar('\0', out);
196 				}
197 			} else {
198 				int compressed_size = 0;
199 				uncompressed_row = uncompressed_row_start;
200 				if ((compressed_size = compress_row(uncompressed_row, gdImageSX(im))) < 0) {
201 					error = 1;
202 					break;
203 				}
204 				bitmap_size += compressed_size;
205 
206 
207 				gdPutBuf(uncompressed_row, compressed_size, out);
208 				Putchar(BMP_RLE_COMMAND, out);
209 				Putchar(BMP_RLE_ENDOFLINE, out);
210 				bitmap_size += 2;
211 			}
212 		}
213 
214 		if (compression && uncompressed_row) {
215 			gdFree(uncompressed_row);
216 			if (error != 0) {
217 				goto cleanup;
218 			}
219 			/* Update filesize based on new values and set compression flag */
220 			Putchar(BMP_RLE_COMMAND, out);
221 			Putchar(BMP_RLE_ENDOFBITMAP, out);
222 			bitmap_size += 2;
223 
224 			/* Write new total bitmap size */
225 			gdSeek(out, 2);
226 			gdBMPPutInt(out, total_size + bitmap_size);
227 
228 			/* Total length of image data */
229 			gdSeek(out, 34);
230 			gdBMPPutInt(out, bitmap_size);
231 		}
232 
233 	} else {
234 		for (row = (im->sy - 1); row >= 0; row--) {
235 			for (xpos = 0; xpos < im->sx; xpos++) {
236 				pixel = gdImageGetPixel(im, xpos, row);
237 
238 				Putchar(gdTrueColorGetBlue(pixel), out);
239 				Putchar(gdTrueColorGetGreen(pixel), out);
240 				Putchar(gdTrueColorGetRed(pixel), out);
241 			}
242 
243 			/* Add padding to make sure we have n mod 4 == 0 bytes per row */
244 			for (xpos = padding; xpos > 0; --xpos) {
245 				Putchar('\0', out);
246 			}
247 		}
248 	}
249 
250 
251 	/* If we needed a tmpfile for compression copy it over to out_original */
252 	if (tmpfile_for_compression) {
253 		unsigned char* copy_buffer = NULL;
254 		int buffer_size = 0;
255 
256 		gdSeek(out, 0);
257 		copy_buffer = (unsigned char *) gdMalloc(1024 * sizeof(unsigned char));
258 		if (copy_buffer == NULL) {
259 			goto cleanup;
260 		}
261 
262 		while ((buffer_size = gdGetBuf(copy_buffer, 1024, out)) != EOF) {
263 			if (buffer_size == 0) {
264 				break;
265 			}
266 			gdPutBuf(copy_buffer , buffer_size, out_original);
267 		}
268 		gdFree(copy_buffer);
269 
270 		/* Replace the temp with the original which now has data */
271 		out->gd_free(out);
272 		out = out_original;
273 		out_original = NULL;
274 	}
275 
276 cleanup:
277 	if (tmpfile_for_compression) {
278 #ifdef _WIN32
279 		_rmtmp();
280 #else
281 		fclose(tmpfile_for_compression);
282 #endif
283 		tmpfile_for_compression = NULL;
284 	}
285 
286 	if (out_original) {
287 		out_original->gd_free(out_original);
288 	}
289 	return;
290 }
291 
compress_row(unsigned char * row,int length)292 static int compress_row(unsigned char *row, int length)
293 {
294 	int rle_type = 0;
295 	int compressed_length = 0;
296 	int pixel = 0, compressed_run = 0, rle_compression = 0;
297 	unsigned char *uncompressed_row = NULL, *uncompressed_rowp = NULL, *uncompressed_start = NULL;
298 
299 	uncompressed_row = (unsigned char *) gdMalloc(length);
300 	if (!uncompressed_row) {
301 		return -1;
302 	}
303 
304 	memcpy(uncompressed_row, row, length);
305 	uncompressed_start = uncompressed_rowp = uncompressed_row;
306 
307 	for (pixel = 0; pixel < length; pixel++) {
308 		if (compressed_run == 0) {
309 			uncompressed_row = uncompressed_rowp;
310 			compressed_run++;
311 			uncompressed_rowp++;
312 			rle_type = BMP_RLE_TYPE_RAW;
313 			continue;
314 		}
315 
316 		if (compressed_run == 1) {
317 			/* Compare next byte */
318 			if (memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
319 				rle_type = BMP_RLE_TYPE_RLE;
320 			}
321 		}
322 
323 		if (rle_type == BMP_RLE_TYPE_RLE) {
324 			if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) != 0) {
325 				/* more than what we can store in a single run or run is over due to non match, force write */
326 				rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
327 				row += rle_compression;
328 				compressed_length += rle_compression;
329 				compressed_run = 0;
330 				pixel--;
331 			} else {
332 				compressed_run++;
333 				uncompressed_rowp++;
334 			}
335 		} else {
336 			if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) {
337 				/* more than what we can store in a single run or run is over due to match, force write */
338 				rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
339 				row += rle_compression;
340 				compressed_length += rle_compression;
341 				compressed_run = 0;
342 				pixel--;
343 			} else {
344 				/* add this pixel to the row */
345 				compressed_run++;
346 				uncompressed_rowp++;
347 			}
348 
349 		}
350 	}
351 
352 	if (compressed_run) {
353 		if (rle_type == BMP_RLE_TYPE_RLE) {
354 			compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row);
355 		}
356 	}
357 
358 	gdFree(uncompressed_start);
359 
360 	return compressed_length;
361 }
362 
build_rle_packet(unsigned char * row,int packet_type,int length,unsigned char * data)363 static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data)
364 {
365 	int compressed_size = 0;
366 	if (length < 1 || length > 128) {
367 		return 0;
368 	}
369 
370 	/* Bitmap specific cases is that we can't have uncompressed rows of length 1 or 2 */
371 	if (packet_type == BMP_RLE_TYPE_RAW && length < 3) {
372 		int i = 0;
373 		for (i = 0; i < length; i++) {
374 			compressed_size += 2;
375 			memset(row, 1, 1);
376 			row++;
377 
378 			memcpy(row, data++, 1);
379 			row++;
380 		}
381 	} else if (packet_type == BMP_RLE_TYPE_RLE) {
382 		compressed_size = 2;
383 		memset(row, length, 1);
384 		row++;
385 
386 		memcpy(row, data, 1);
387 		row++;
388 	} else {
389 		compressed_size = 2 + length;
390 		memset(row, BMP_RLE_COMMAND, 1);
391 		row++;
392 
393 		memset(row, length, 1);
394 		row++;
395 
396 		memcpy(row, data, length);
397 		row += length;
398 
399 		/* Must be an even number for an uncompressed run */
400 		if (length % 2) {
401 			memset(row, 0, 1);
402 			row++;
403 			compressed_size++;
404 		}
405 	}
406 	return compressed_size;
407 }
408 
409 /*
410 	Function: gdImageCreateFromBmp
411 */
gdImageCreateFromBmp(FILE * inFile)412 gdImagePtr gdImageCreateFromBmp(FILE * inFile)
413 {
414 	gdImagePtr im = 0;
415 	gdIOCtx *in = gdNewFileCtx(inFile);
416 	if (in == NULL) return NULL;
417 	im = gdImageCreateFromBmpCtx(in);
418 	in->gd_free(in);
419 	return im;
420 }
421 
422 /*
423 	Function: gdImageCreateFromBmpPtr
424 */
gdImageCreateFromBmpPtr(int size,void * data)425 gdImagePtr gdImageCreateFromBmpPtr(int size, void *data)
426 {
427 	gdImagePtr im;
428 	gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
429 	if (in == NULL) return NULL;
430 	im = gdImageCreateFromBmpCtx(in);
431 	in->gd_free(in);
432 	return im;
433 }
434 
435 /*
436 	Function: gdImageCreateFromBmpCtx
437 */
gdImageCreateFromBmpCtx(gdIOCtxPtr infile)438 gdImagePtr gdImageCreateFromBmpCtx(gdIOCtxPtr infile)
439 {
440 	bmp_hdr_t *hdr;
441 	bmp_info_t *info;
442 	gdImagePtr im = NULL;
443 	int error = 0;
444 
445 	if (!(hdr= (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) {
446 		return NULL;
447 	}
448 
449 	if (bmp_read_header(infile, hdr)) {
450 		gdFree(hdr);
451 		return NULL;
452 	}
453 
454 	if (hdr->magic != 0x4d42) {
455 		gdFree(hdr);
456 		return NULL;
457 	}
458 
459 	if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) {
460 		gdFree(hdr);
461 		return NULL;
462 	}
463 
464 	if (bmp_read_info(infile, info)) {
465 		gdFree(hdr);
466 		gdFree(info);
467 		return NULL;
468 	}
469 
470 	BMP_DEBUG(printf("Numcolours: %d\n", info->numcolors));
471 	BMP_DEBUG(printf("Width: %d\n", info->width));
472 	BMP_DEBUG(printf("Height: %d\n", info->height));
473 	BMP_DEBUG(printf("Planes: %d\n", info->numplanes));
474 	BMP_DEBUG(printf("Depth: %d\n", info->depth));
475 	BMP_DEBUG(printf("Offset: %d\n", hdr->off));
476 
477 	if (info->depth >= 16) {
478 		im = gdImageCreateTrueColor(info->width, info->height);
479 	} else {
480 		im = gdImageCreate(info->width, info->height);
481 	}
482 
483 	if (!im) {
484 		gdFree(hdr);
485 		gdFree(info);
486 		return NULL;
487 	}
488 
489 	switch (info->depth) {
490 	case 1:
491 		BMP_DEBUG(printf("1-bit image\n"));
492 		error = bmp_read_1bit(im, infile, info, hdr);
493 		break;
494 	case 4:
495 		BMP_DEBUG(printf("4-bit image\n"));
496 		error = bmp_read_4bit(im, infile, info, hdr);
497 		break;
498 	case 8:
499 		BMP_DEBUG(printf("8-bit image\n"));
500 		error = bmp_read_8bit(im, infile, info, hdr);
501 		break;
502 	case 16:
503 	case 24:
504 	case 32:
505 		BMP_DEBUG(printf("Direct BMP image\n"));
506 		error = bmp_read_direct(im, infile, info, hdr);
507 		break;
508 	default:
509 		BMP_DEBUG(printf("Unknown bit count\n"));
510 		error = 1;
511 	}
512 
513 	gdFree(hdr);
514 	gdFree(info);
515 
516 	if (error) {
517 		gdImageDestroy(im);
518 		return NULL;
519 	}
520 
521 	return im;
522 }
523 
bmp_read_header(gdIOCtx * infile,bmp_hdr_t * hdr)524 static int bmp_read_header(gdIOCtx *infile, bmp_hdr_t *hdr)
525 {
526 	if(
527 	    !gdGetWordLSB(&hdr->magic, infile) ||
528 	    !gdGetIntLSB(&hdr->size, infile) ||
529 	    !gdGetWordLSB(&hdr->reserved1, infile) ||
530 	    !gdGetWordLSB(&hdr->reserved2 , infile) ||
531 	    !gdGetIntLSB(&hdr->off , infile)
532 	) {
533 		return 1;
534 	}
535 	return 0;
536 }
537 
bmp_read_info(gdIOCtx * infile,bmp_info_t * info)538 static int bmp_read_info(gdIOCtx *infile, bmp_info_t *info)
539 {
540 	/* read BMP length so we can work out the version */
541 	if (!gdGetIntLSB(&info->len, infile)) {
542 		return 1;
543 	}
544 
545 	switch (info->len) {
546 		/* For now treat Windows v4 + v5 as v3 */
547 	case BMP_WINDOWS_V3:
548 	case BMP_WINDOWS_V4:
549 	case BMP_WINDOWS_V5:
550 		BMP_DEBUG(printf("Reading Windows Header\n"));
551 		if (bmp_read_windows_v3_info(infile, info)) {
552 			return 1;
553 		}
554 		break;
555 	case BMP_OS2_V1:
556 		if (bmp_read_os2_v1_info(infile, info)) {
557 			return 1;
558 		}
559 		break;
560 	case BMP_OS2_V2:
561 		if (bmp_read_os2_v2_info(infile, info)) {
562 			return 1;
563 		}
564 		break;
565 	default:
566 		BMP_DEBUG(printf("Unhandled bitmap\n"));
567 		return 1;
568 	}
569 	return 0;
570 }
571 
bmp_read_windows_v3_info(gdIOCtxPtr infile,bmp_info_t * info)572 static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info)
573 {
574 	if (
575 	    !gdGetIntLSB(&info->width, infile) ||
576 	    !gdGetIntLSB(&info->height, infile) ||
577 	    !gdGetWordLSB(&info->numplanes, infile) ||
578 	    !gdGetWordLSB(&info->depth, infile) ||
579 	    !gdGetIntLSB(&info->enctype, infile) ||
580 	    !gdGetIntLSB(&info->size, infile) ||
581 	    !gdGetIntLSB(&info->hres, infile) ||
582 	    !gdGetIntLSB(&info->vres, infile) ||
583 	    !gdGetIntLSB(&info->numcolors, infile) ||
584 	    !gdGetIntLSB(&info->mincolors, infile)
585 	) {
586 		return 1;
587 	}
588 
589 	if (info->height < 0) {
590 		info->topdown = 1;
591 		info->height = -info->height;
592 	} else {
593 		info->topdown = 0;
594 	}
595 
596 	info->type = BMP_PALETTE_4;
597 
598 	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
599 	        info->depth <= 0  || info->numcolors < 0 || info->mincolors < 0) {
600 		return 1;
601 	}
602 
603 	return 0;
604 }
605 
bmp_read_os2_v1_info(gdIOCtxPtr infile,bmp_info_t * info)606 static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info)
607 {
608 	if (
609 	    !gdGetWordLSB((signed short int *)&info->width, infile) ||
610 	    !gdGetWordLSB((signed short int *)&info->height, infile) ||
611 	    !gdGetWordLSB(&info->numplanes, infile) ||
612 	    !gdGetWordLSB(&info->depth, infile)
613 	) {
614 		return 1;
615 	}
616 
617 	/* OS2 v1 doesn't support topdown */
618 	info->topdown = 0;
619 
620 	info->numcolors = 1 << info->depth;
621 	info->type = BMP_PALETTE_3;
622 
623 	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
624 	        info->depth <= 0 || info->numcolors < 0) {
625 		return 1;
626 	}
627 
628 	return 0;
629 }
630 
bmp_read_os2_v2_info(gdIOCtxPtr infile,bmp_info_t * info)631 static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info)
632 {
633 	char useless_bytes[24];
634 	if (
635 	    !gdGetIntLSB(&info->width, infile) ||
636 	    !gdGetIntLSB(&info->height, infile) ||
637 	    !gdGetWordLSB(&info->numplanes, infile) ||
638 	    !gdGetWordLSB(&info->depth, infile) ||
639 	    !gdGetIntLSB(&info->enctype, infile) ||
640 	    !gdGetIntLSB(&info->size, infile) ||
641 	    !gdGetIntLSB(&info->hres, infile) ||
642 	    !gdGetIntLSB(&info->vres, infile) ||
643 	    !gdGetIntLSB(&info->numcolors, infile) ||
644 	    !gdGetIntLSB(&info->mincolors, infile)
645 	) {
646 		return 1;
647 	}
648 
649 	/* Lets seek the next 24 pointless bytes, we don't care too much about it */
650 	if (!gdGetBuf(useless_bytes, 24, infile)) {
651 		return 1;
652 	}
653 
654 	if (info->height < 0) {
655 		info->topdown = 1;
656 		info->height = -info->height;
657 	} else {
658 		info->topdown = 0;
659 	}
660 
661 	info->type = BMP_PALETTE_4;
662 
663 	if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 ||
664 	        info->depth <= 0  || info->numcolors < 0 || info->mincolors < 0) {
665 		return 1;
666 	}
667 
668 
669 	return 0;
670 }
671 
bmp_read_direct(gdImagePtr im,gdIOCtxPtr infile,bmp_info_t * info,bmp_hdr_t * header)672 static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
673 {
674 	int ypos = 0, xpos = 0, row = 0;
675 	int padding = 0, alpha = 0, red = 0, green = 0, blue = 0;
676 	signed short int data = 0;
677 
678 	switch(info->enctype) {
679 	case BMP_BI_RGB:
680 		/* no-op */
681 		break;
682 
683 	case BMP_BI_BITFIELDS:
684 		if (info->depth == 24) {
685 			BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
686 			return 1;
687 		}
688 		BMP_DEBUG(printf("Currently no bitfield support\n"));
689 		return 1;
690 		break;
691 
692 	case BMP_BI_RLE8:
693 		if (info->depth != 8) {
694 			BMP_DEBUG(printf("RLE is only valid for 8-bit images\n"));
695 			return 1;
696 		}
697 		break;
698 	case BMP_BI_RLE4:
699 		if (info->depth != 4) {
700 			BMP_DEBUG(printf("RLE is only valid for 4-bit images\n"));
701 			return 1;
702 		}
703 		break;
704 	case BMP_BI_JPEG:
705 	case BMP_BI_PNG:
706 	default:
707 		BMP_DEBUG(printf("Unsupported BMP compression format\n"));
708 		return 1;
709 	}
710 
711 	/* There is a chance the data isn't until later, would be weird but it is possible */
712 	if (gdTell(infile) != header->off) {
713 		/* Should make sure we don't seek past the file size */
714 		if (!gdSeek(infile, header->off)) {
715 			return 1;
716 		}
717 	}
718 
719 	/* The line must be divisible by 4, else its padded with NULLs */
720 	padding = ((int)(info->depth / 8) * info->width) % 4;
721 	if (padding) {
722 		padding = 4 - padding;
723 	}
724 
725 
726 	for (ypos = 0; ypos < info->height; ++ypos) {
727 		if (info->topdown) {
728 			row = ypos;
729 		} else {
730 			row = info->height - ypos - 1;
731 		}
732 
733 		for (xpos = 0; xpos < info->width; xpos++) {
734 			if (info->depth == 16) {
735 				if (!gdGetWordLSB(&data, infile)) {
736 					return 1;
737 				}
738 				BMP_DEBUG(printf("Data: %X\n", data));
739 				red = ((data & 0x7C00) >> 10) << 3;
740 				green = ((data & 0x3E0) >> 5) << 3;
741 				blue = (data & 0x1F) << 3;
742 				BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red, green, blue));
743 			} else if (info->depth == 24) {
744 				if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile)) {
745 					return 1;
746 				}
747 			} else {
748 				if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile) || !gdGetByte(&alpha, infile)) {
749 					return 1;
750 				}
751 			}
752 			/*alpha = gdAlphaMax - (alpha >> 1);*/
753 			gdImageSetPixel(im, xpos, row, gdTrueColor(red, green, blue));
754 		}
755 		for (xpos = padding; xpos > 0; --xpos) {
756 			if (!gdGetByte(&red, infile)) {
757 				return 1;
758 			}
759 		}
760 	}
761 
762 	return 0;
763 }
764 
bmp_read_palette(gdImagePtr im,gdIOCtxPtr infile,int count,int read_four)765 static int bmp_read_palette(gdImagePtr im, gdIOCtxPtr infile, int count, int read_four)
766 {
767 	int i;
768 	int r, g, b, z;
769 
770 	for (i = 0; i < count; i++) {
771 		if (
772 		    !gdGetByte(&b, infile) ||
773 		    !gdGetByte(&g, infile) ||
774 		    !gdGetByte(&r, infile) ||
775 		    (read_four && !gdGetByte(&z, infile))
776 		) {
777 			return 1;
778 		}
779 		im->red[i] = r;
780 		im->green[i] = g;
781 		im->blue[i] = b;
782 		im->open[i] = 1;
783 	}
784 	return 0;
785 }
786 
bmp_read_1bit(gdImagePtr im,gdIOCtxPtr infile,bmp_info_t * info,bmp_hdr_t * header)787 static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
788 {
789 	int ypos = 0, xpos = 0, row = 0, index = 0;
790 	int padding = 0, current_byte = 0, bit = 0;
791 
792 	if (info->enctype != BMP_BI_RGB) {
793 		return 1;
794 	}
795 
796 	if (!info->numcolors) {
797 		info->numcolors = 2;
798 	} else if (info->numcolors < 0 || info->numcolors > 2) {
799 		return 1;
800 	}
801 
802 	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
803 		return 1;
804 	}
805 
806 	im->colorsTotal = info->numcolors;
807 
808 	/* There is a chance the data isn't until later, would be weird but it is possible */
809 	if (gdTell(infile) != header->off) {
810 		/* Should make sure we don't seek past the file size */
811 		if (!gdSeek(infile, header->off)) {
812 			return 1;
813 		}
814 	}
815 
816 	/* The line must be aligned on a 32 bits word, else it is padded with zeros */
817 	padding = (info->width + 7) / 8 % 4;
818 	if (padding) {
819 		padding = 4 - padding;
820 	}
821 
822 	for (ypos = 0; ypos < info->height; ++ypos) {
823 		if (info->topdown) {
824 			row = ypos;
825 		} else {
826 			row = info->height - ypos - 1;
827 		}
828 
829 		for (xpos = 0; xpos < info->width; xpos += 8) {
830 			/* Bitmaps are always aligned in bytes so we'll never overflow */
831 			if (!gdGetByte(&current_byte, infile)) {
832 				return 1;
833 			}
834 
835 			for (bit = 0; bit < 8; bit++) {
836 				index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
837 				if (im->open[index]) {
838 					im->open[index] = 0;
839 				}
840 				gdImageSetPixel(im, xpos + bit, row, index);
841 				/* No need to read anything extra */
842 				if ((xpos + bit) >= info->width) {
843 					break;
844 				}
845 			}
846 		}
847 
848 		for (xpos = padding; xpos > 0; --xpos) {
849 			if (!gdGetByte(&index, infile)) {
850 				return 1;
851 			}
852 		}
853 	}
854 	return 0;
855 }
856 
bmp_read_4bit(gdImagePtr im,gdIOCtxPtr infile,bmp_info_t * info,bmp_hdr_t * header)857 static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
858 {
859 	int ypos = 0, xpos = 0, row = 0, index = 0;
860 	int padding = 0, current_byte = 0;
861 
862 	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4) {
863 		return 1;
864 	}
865 
866 	if (!info->numcolors) {
867 		info->numcolors = 16;
868 	} else if (info->numcolors < 0 || info->numcolors > 16) {
869 		return 1;
870 	}
871 
872 	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
873 		return 1;
874 	}
875 
876 	im->colorsTotal = info->numcolors;
877 
878 	/* There is a chance the data isn't until later, would be weird but it is possible */
879 	if (gdTell(infile) != header->off) {
880 		/* Should make sure we don't seek past the file size */
881 		if (!gdSeek(infile, header->off)) {
882 			return 1;
883 		}
884 	}
885 
886 	/* The line must be divisible by 4, else its padded with NULLs */
887 	padding = ((int)ceil(0.5 * info->width)) % 4;
888 	if (padding) {
889 		padding = 4 - padding;
890 	}
891 
892 	switch (info->enctype) {
893 	case BMP_BI_RGB:
894 		for (ypos = 0; ypos < info->height; ++ypos) {
895 			if (info->topdown) {
896 				row = ypos;
897 			} else {
898 				row = info->height - ypos - 1;
899 			}
900 
901 			for (xpos = 0; xpos < info->width; xpos += 2) {
902 				if (!gdGetByte(&current_byte, infile)) {
903 					return 1;
904 				}
905 
906 				index = (current_byte >> 4) & 0x0f;
907 				if (im->open[index]) {
908 					im->open[index] = 0;
909 				}
910 				gdImageSetPixel(im, xpos, row, index);
911 
912 				/* This condition may get called often, potential optimsations */
913 				if (xpos >= info->width) {
914 					break;
915 				}
916 
917 				index = current_byte & 0x0f;
918 				if (im->open[index]) {
919 					im->open[index] = 0;
920 				}
921 				gdImageSetPixel(im, xpos + 1, row, index);
922 			}
923 
924 			for (xpos = padding; xpos > 0; --xpos) {
925 				if (!gdGetByte(&index, infile)) {
926 					return 1;
927 				}
928 			}
929 		}
930 		break;
931 
932 	case BMP_BI_RLE4:
933 		if (bmp_read_rle(im, infile, info)) {
934 			return 1;
935 		}
936 		break;
937 
938 	default:
939 		return 1;
940 	}
941 	return 0;
942 }
943 
bmp_read_8bit(gdImagePtr im,gdIOCtxPtr infile,bmp_info_t * info,bmp_hdr_t * header)944 static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header)
945 {
946 	int ypos = 0, xpos = 0, row = 0, index = 0;
947 	int padding = 0;
948 
949 	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) {
950 		return 1;
951 	}
952 
953 	if (!info->numcolors) {
954 		info->numcolors = 256;
955 	} else if (info->numcolors < 0 || info->numcolors > 256) {
956 		return 1;
957 	}
958 
959 	if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
960 		return 1;
961 	}
962 
963 	im->colorsTotal = info->numcolors;
964 
965 	/* There is a chance the data isn't until later, would be weird but it is possible */
966 	if (gdTell(infile) != header->off) {
967 		/* Should make sure we don't seek past the file size */
968 		if (!gdSeek(infile, header->off)) {
969 			return 1;
970 		}
971 	}
972 
973 	/* The line must be divisible by 4, else its padded with NULLs */
974 	padding = (1 * info->width) % 4;
975 	if (padding) {
976 		padding = 4 - padding;
977 	}
978 
979 	switch (info->enctype) {
980 	case BMP_BI_RGB:
981 		for (ypos = 0; ypos < info->height; ++ypos) {
982 			if (info->topdown) {
983 				row = ypos;
984 			} else {
985 				row = info->height - ypos - 1;
986 			}
987 
988 			for (xpos = 0; xpos < info->width; ++xpos) {
989 				if (!gdGetByte(&index, infile)) {
990 					return 1;
991 				}
992 
993 				if (im->open[index]) {
994 					im->open[index] = 0;
995 				}
996 				gdImageSetPixel(im, xpos, row, index);
997 			}
998 			/* Could create a new variable, but it isn't really worth it */
999 			for (xpos = padding; xpos > 0; --xpos) {
1000 				if (!gdGetByte(&index, infile)) {
1001 					return 1;
1002 				}
1003 			}
1004 		}
1005 		break;
1006 
1007 	case BMP_BI_RLE8:
1008 		if (bmp_read_rle(im, infile, info)) {
1009 			return 1;
1010 		}
1011 		break;
1012 
1013 	default:
1014 		return 1;
1015 	}
1016 	return 0;
1017 }
1018 
bmp_read_rle(gdImagePtr im,gdIOCtxPtr infile,bmp_info_t * info)1019 static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info)
1020 {
1021 	int ypos = 0, xpos = 0, row = 0, index = 0;
1022 	int rle_length = 0, rle_data = 0;
1023 	int padding = 0;
1024 	int i = 0, j = 0;
1025 	int pixels_per_byte = 8 / info->depth;
1026 
1027 	for (ypos = 0; ypos < info->height && xpos <= info->width;) {
1028 		if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
1029 			return 1;
1030 		}
1031 		row = info->height - ypos - 1;
1032 
1033 		if (rle_length != BMP_RLE_COMMAND) {
1034 			if (im->open[rle_data]) {
1035 				im->open[rle_data] = 0;
1036 			}
1037 
1038 			for (i = 0; (i < rle_length) && (xpos < info->width);) {
1039 				for (j = 1; (j <= pixels_per_byte) && (xpos < info->width) && (i < rle_length); j++, xpos++, i++) {
1040 					index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> (8 - (j * info->depth));
1041 					if (im->open[index]) {
1042 						im->open[index] = 0;
1043 					}
1044 					gdImageSetPixel(im, xpos, row, index);
1045 				}
1046 			}
1047 		} else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) {
1048 			/* Uncompressed RLE needs to be even */
1049 			padding = 0;
1050 			for (i = 0; (i < rle_data) && (xpos < info->width); i += pixels_per_byte) {
1051 				int max_pixels = pixels_per_byte;
1052 
1053 				if (!gdGetByte(&index, infile)) {
1054 					return 1;
1055 				}
1056 				padding++;
1057 
1058 				if (rle_data - i < max_pixels) {
1059 					max_pixels = rle_data - i;
1060 				}
1061 
1062 				for (j = 1; (j <= max_pixels)  && (xpos < info->width); j++, xpos++) {
1063 					int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1);
1064 					if (im->open[temp]) {
1065 						im->open[temp] = 0;
1066 					}
1067 					gdImageSetPixel(im, xpos, row, temp);
1068 				}
1069 			}
1070 
1071 			/* Make sure the bytes read are even */
1072 			if (padding % 2 && !gdGetByte(&index, infile)) {
1073 				return 1;
1074 			}
1075 		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) {
1076 			/* Next Line */
1077 			xpos = 0;
1078 			ypos++;
1079 		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA) {
1080 			/* Delta Record, used for bmp files that contain other data*/
1081 			if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) {
1082 				return 1;
1083 			}
1084 			xpos += rle_length;
1085 			ypos += rle_data;
1086 		} else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) {
1087 			/* End of bitmap */
1088 			break;
1089 		}
1090 	}
1091 	return 0;
1092 }
1093