2 Stolen from http://cvs.php.net/viewcvs.cgi/gd/playground/gdbmp/
8 Bitmap format support for libgd
10 * Written 2007, Scott MacVicar
11 ---------------------------------------------------------------------------
17 ----------------------------------------------------------------------------
31 extern void* gdCalloc (size_t nmemb
, size_t size
);
33 static int bmp_read_header(gdIOCtxPtr infile
, bmp_hdr_t
*hdr
);
34 static int bmp_read_info(gdIOCtxPtr infile
, bmp_info_t
*info
);
35 static int bmp_read_windows_v3_info(gdIOCtxPtr infile
, bmp_info_t
*info
);
36 static int bmp_read_os2_v1_info(gdIOCtxPtr infile
, bmp_info_t
*info
);
37 static int bmp_read_os2_v2_info(gdIOCtxPtr infile
, bmp_info_t
*info
);
39 static int bmp_read_direct(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
);
40 static int bmp_read_1bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
);
41 static int bmp_read_4bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
);
42 static int bmp_read_8bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
);
43 static int bmp_read_rle(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
);
45 #if GD_MAJOR_VERSION == 2 && GD_MINOR_VERSION < 1
46 /* Byte helper functions, since added to GD 2.1 */
47 static int gdGetIntLSB(signed int *result
, gdIOCtx
* ctx
);
48 static int gdGetWordLSB(signed short int *result
, gdIOCtx
* ctx
);
53 gdImagePtr
gdImageCreateFromBmpCtx(gdIOCtxPtr infile
);
55 gdImagePtr
gdImageCreateFromBmp(FILE * inFile
)
58 gdIOCtx
*in
= gdNewFileCtx(inFile
);
59 im
= gdImageCreateFromBmpCtx(in
);
64 gdImagePtr
gdImageCreateFromBmpPtr(int size
, void *data
)
67 gdIOCtx
*in
= gdNewDynamicCtxEx(size
, data
, 0);
68 im
= gdImageCreateFromBmpCtx(in
);
73 gdImagePtr
gdImageCreateFromBmpCtx(gdIOCtxPtr infile
)
80 if (!(hdr
= (bmp_hdr_t
*)gdCalloc(1, sizeof(bmp_hdr_t
)))) {
84 if (bmp_read_header(infile
, hdr
)) {
89 if (hdr
->magic
!= 0x4d42) {
94 if (!(info
= (bmp_info_t
*)gdCalloc(1, sizeof(bmp_info_t
)))) {
99 if (bmp_read_info(infile
, info
)) {
105 BMP_DEBUG(printf("Numcolours: %d\n", info
->numcolors
));
106 BMP_DEBUG(printf("Width: %d\n", info
->width
));
107 BMP_DEBUG(printf("Height: %d\n", info
->height
));
108 BMP_DEBUG(printf("Planes: %d\n", info
->numplanes
));
109 BMP_DEBUG(printf("Depth: %d\n", info
->depth
));
110 BMP_DEBUG(printf("Offset: %d\n", hdr
->off
));
112 if (info
->depth
>= 16) {
113 im
= gdImageCreateTrueColor(info
->width
, info
->height
);
115 im
= gdImageCreate(info
->width
, info
->height
);
124 switch (info
->depth
) {
126 BMP_DEBUG(printf("1-bit image\n"));
127 error
= bmp_read_1bit(im
, infile
, info
, hdr
);
130 BMP_DEBUG(printf("4-bit image\n"));
131 error
= bmp_read_4bit(im
, infile
, info
, hdr
);
134 BMP_DEBUG(printf("8-bit image\n"));
135 error
= bmp_read_8bit(im
, infile
, info
, hdr
);
140 BMP_DEBUG(printf("Direct BMP image\n"));
141 error
= bmp_read_direct(im
, infile
, info
, hdr
);
144 BMP_DEBUG(printf("Unknown bit count\n"));
159 static int bmp_read_header(gdIOCtx
*infile
, bmp_hdr_t
*hdr
)
162 !gdGetWordLSB(&hdr
->magic
, infile
) ||
163 !gdGetIntLSB(&hdr
->size
, infile
) ||
164 !gdGetWordLSB(&hdr
->reserved1
, infile
) ||
165 !gdGetWordLSB(&hdr
->reserved2
, infile
) ||
166 !gdGetIntLSB(&hdr
->off
, infile
)
173 static int bmp_read_info(gdIOCtx
*infile
, bmp_info_t
*info
)
175 /* read BMP length so we can work out the version */
176 if (!gdGetIntLSB(&info
->len
, infile
)) {
181 /* For now treat Windows v4 + v5 as v3 */
185 BMP_DEBUG(printf("Reading Windows Header\n"));
186 if (bmp_read_windows_v3_info(infile
, info
)) {
191 if (bmp_read_os2_v1_info(infile
, info
)) {
196 if (bmp_read_os2_v2_info(infile
, info
)) {
201 BMP_DEBUG(printf("Unhandled bitmap\n"));
207 static int bmp_read_windows_v3_info(gdIOCtxPtr infile
, bmp_info_t
*info
)
210 !gdGetIntLSB(&info
->width
, infile
) ||
211 !gdGetIntLSB(&info
->height
, infile
) ||
212 !gdGetWordLSB(&info
->numplanes
, infile
) ||
213 !gdGetWordLSB(&info
->depth
, infile
) ||
214 !gdGetIntLSB(&info
->enctype
, infile
) ||
215 !gdGetIntLSB(&info
->size
, infile
) ||
216 !gdGetIntLSB(&info
->hres
, infile
) ||
217 !gdGetIntLSB(&info
->vres
, infile
) ||
218 !gdGetIntLSB(&info
->numcolors
, infile
) ||
219 !gdGetIntLSB(&info
->mincolors
, infile
)
224 if (info
->height
< 0) {
226 info
->height
= -info
->height
;
231 info
->type
= BMP_PALETTE_4
;
233 if (info
->width
<= 0 || info
->height
<= 0 || info
->numplanes
<= 0 ||
234 info
->depth
<= 0 || info
->numcolors
< 0 || info
->mincolors
< 0) {
241 static int bmp_read_os2_v1_info(gdIOCtxPtr infile
, bmp_info_t
*info
)
244 !gdGetWordLSB((signed short int *)&info
->width
, infile
) ||
245 !gdGetWordLSB((signed short int *)&info
->height
, infile
) ||
246 !gdGetWordLSB(&info
->numplanes
, infile
) ||
247 !gdGetWordLSB(&info
->depth
, infile
)
252 /* OS2 v1 doesn't support topdown */
255 info
->numcolors
= 1 << info
->depth
;
256 info
->type
= BMP_PALETTE_3
;
258 if (info
->width
<= 0 || info
->height
<= 0 || info
->numplanes
<= 0 ||
259 info
->depth
<= 0 || info
->numcolors
< 0) {
266 static int bmp_read_os2_v2_info(gdIOCtxPtr infile
, bmp_info_t
*info
)
268 char useless_bytes
[24];
270 !gdGetIntLSB(&info
->width
, infile
) ||
271 !gdGetIntLSB(&info
->height
, infile
) ||
272 !gdGetWordLSB(&info
->numplanes
, infile
) ||
273 !gdGetWordLSB(&info
->depth
, infile
) ||
274 !gdGetIntLSB(&info
->enctype
, infile
) ||
275 !gdGetIntLSB(&info
->size
, infile
) ||
276 !gdGetIntLSB(&info
->hres
, infile
) ||
277 !gdGetIntLSB(&info
->vres
, infile
) ||
278 !gdGetIntLSB(&info
->numcolors
, infile
) ||
279 !gdGetIntLSB(&info
->mincolors
, infile
)
284 /* Lets seek the next 24 pointless bytes, we don't care too much about it */
285 if (!gdGetBuf(useless_bytes
, 24, infile
)) {
289 if (info
->height
< 0) {
291 info
->height
= -info
->height
;
296 info
->type
= BMP_PALETTE_4
;
298 if (info
->width
<= 0 || info
->height
<= 0 || info
->numplanes
<= 0 ||
299 info
->depth
<= 0 || info
->numcolors
< 0 || info
->mincolors
< 0) {
307 static int bmp_read_direct(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
)
309 int ypos
= 0, xpos
= 0, row
= 0;
310 int padding
= 0, alpha
= 0, red
= 0, green
= 0, blue
= 0;
311 signed short int data
= 0;
313 switch(info
->enctype
) {
318 case BMP_BI_BITFIELDS
:
319 if (info
->depth
== 24) {
320 BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
323 BMP_DEBUG(printf("Currently no bitfield support\n"));
328 if (info
->depth
!= 8) {
329 BMP_DEBUG(printf("RLE is only valid for 8-bit images\n"));
333 if (info
->depth
!= 4) {
334 BMP_DEBUG(printf("RLE is only valid for 4-bit images\n"));
340 BMP_DEBUG(printf("Unsupported BMP compression format\n"));
344 /* There is a chance the data isn't until later, would be wierd but it is possible */
345 if (gdTell(infile
) != header
->off
) {
346 /* Should make sure we don't seek past the file size */
347 gdSeek(infile
, header
->off
);
350 /* The line must be divisible by 4, else its padded with NULLs */
351 padding
= ((int)(info
->depth
/ 8) * info
->width
) % 4;
353 padding
= 4 - padding
;
357 for (ypos
= 0; ypos
< info
->height
; ++ypos
) {
361 row
= info
->height
- ypos
- 1;
364 for (xpos
= 0; xpos
< info
->width
; xpos
++) {
365 if (info
->depth
== 16) {
366 if (!gdGetWordLSB(&data
, infile
)) {
369 BMP_DEBUG(printf("Data: %X\n", data
));
370 red
= ((data
& 0x7C00) >> 10) << 3;
371 green
= ((data
& 0x3E0) >> 5) << 3;
372 blue
= (data
& 0x1F) << 3;
373 BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red
, green
, blue
));
374 } else if (info
->depth
== 24) {
375 if (!gdGetByte(&blue
, infile
) || !gdGetByte(&green
, infile
) || !gdGetByte(&red
, infile
)) {
379 if (!gdGetByte(&blue
, infile
) || !gdGetByte(&green
, infile
) || !gdGetByte(&red
, infile
) || !gdGetByte(&alpha
, infile
)) {
383 /*alpha = gdAlphaMax - (alpha >> 1);*/
384 gdImageSetPixel(im
, xpos
, row
, gdTrueColor(red
, green
, blue
));
386 for (xpos
= padding
; xpos
> 0; --xpos
) {
387 if (!gdGetByte(&red
, infile
)) {
396 static int bmp_read_palette(gdImagePtr im
, gdIOCtxPtr infile
, int count
, int read_four
)
401 for (i
= 0; i
< count
; i
++) {
403 !gdGetByte(&r
, infile
) ||
404 !gdGetByte(&g
, infile
) ||
405 !gdGetByte(&b
, infile
) ||
406 (read_four
&& !gdGetByte(&z
, infile
))
418 static int bmp_read_1bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
)
420 int ypos
= 0, xpos
= 0, row
= 0, index
= 0;
421 int padding
= 0, current_byte
= 0, bit
= 0;
423 if (info
->enctype
!= BMP_BI_RGB
) {
427 if (!info
->numcolors
) {
429 } else if (info
->numcolors
< 0 || info
->numcolors
> 2) {
433 if (bmp_read_palette(im
, infile
, info
->numcolors
, (info
->type
== BMP_PALETTE_4
))) {
437 im
->colorsTotal
= info
->numcolors
;
439 /* There is a chance the data isn't until later, would be wierd but it is possible */
440 if (gdTell(infile
) != header
->off
) {
441 /* Should make sure we don't seek past the file size */
442 gdSeek(infile
, header
->off
);
445 /* The line must be divisible by 4, else its padded with NULLs */
446 padding
= ((int)ceill(0.1 * info
->width
)) % 4;
448 padding
= 4 - padding
;
451 for (ypos
= 0; ypos
< info
->height
; ++ypos
) {
455 row
= info
->height
- ypos
- 1;
458 for (xpos
= 0; xpos
< info
->width
; xpos
+= 8) {
459 /* Bitmaps are always aligned in bytes so we'll never overflow */
460 if (!gdGetByte(¤t_byte
, infile
)) {
464 for (bit
= 0; bit
< 8; bit
++) {
465 index
= ((current_byte
& (0x80 >> bit
)) != 0 ? 0x01 : 0x00);
466 if (im
->open
[index
]) {
469 gdImageSetPixel(im
, xpos
+ bit
, row
, index
);
470 /* No need to read anything extra */
471 if ((xpos
+ bit
) >= info
->width
) {
477 for (xpos
= padding
; xpos
> 0; --xpos
) {
478 if (!gdGetByte(&index
, infile
)) {
486 static int bmp_read_4bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
)
488 int ypos
= 0, xpos
= 0, row
= 0, index
= 0;
489 int padding
= 0, current_byte
= 0;
491 if (info
->enctype
!= BMP_BI_RGB
&& info
->enctype
!= BMP_BI_RLE4
) {
495 if (!info
->numcolors
) {
496 info
->numcolors
= 16;
497 } else if (info
->numcolors
< 0 || info
->numcolors
> 16) {
501 if (bmp_read_palette(im
, infile
, info
->numcolors
, (info
->type
== BMP_PALETTE_4
))) {
505 im
->colorsTotal
= info
->numcolors
;
507 /* There is a chance the data isn't until later, would be wierd but it is possible */
508 if (gdTell(infile
) != header
->off
) {
509 /* Should make sure we don't seek past the file size */
510 gdSeek(infile
, header
->off
);
513 /* The line must be divisible by 4, else its padded with NULLs */
514 padding
= ((int)ceil(0.5 * info
->width
)) % 4;
516 padding
= 4 - padding
;
519 switch (info
->enctype
) {
521 for (ypos
= 0; ypos
< info
->height
; ++ypos
) {
525 row
= info
->height
- ypos
- 1;
528 for (xpos
= 0; xpos
< info
->width
; xpos
+= 2) {
529 if (!gdGetByte(¤t_byte
, infile
)) {
533 index
= (current_byte
>> 4) & 0x0f;
534 if (im
->open
[index
]) {
537 gdImageSetPixel(im
, xpos
, row
, index
);
539 /* This condition may get called often, potential optimsations */
540 if (xpos
>= info
->width
) {
544 index
= current_byte
& 0x0f;
545 if (im
->open
[index
]) {
548 gdImageSetPixel(im
, xpos
+ 1, row
, index
);
551 for (xpos
= padding
; xpos
> 0; --xpos
) {
552 if (!gdGetByte(&index
, infile
)) {
560 if (bmp_read_rle(im
, infile
, info
)) {
571 static int bmp_read_8bit(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
, bmp_hdr_t
*header
)
573 int ypos
= 0, xpos
= 0, row
= 0, index
= 0;
576 if (info
->enctype
!= BMP_BI_RGB
&& info
->enctype
!= BMP_BI_RLE8
) {
580 if (!info
->numcolors
) {
581 info
->numcolors
= 256;
582 } else if (info
->numcolors
< 0 || info
->numcolors
> 256) {
586 if (bmp_read_palette(im
, infile
, info
->numcolors
, (info
->type
== BMP_PALETTE_4
))) {
590 im
->colorsTotal
= info
->numcolors
;
592 /* There is a chance the data isn't until later, would be wierd but it is possible */
593 if (gdTell(infile
) != header
->off
) {
594 /* Should make sure we don't seek past the file size */
595 gdSeek(infile
, header
->off
);
598 /* The line must be divisible by 4, else its padded with NULLs */
599 padding
= (1 * info
->width
) % 4;
601 padding
= 4 - padding
;
604 switch (info
->enctype
) {
606 for (ypos
= 0; ypos
< info
->height
; ++ypos
) {
610 row
= info
->height
- ypos
- 1;
613 for (xpos
= 0; xpos
< info
->width
; ++xpos
) {
614 if (!gdGetByte(&index
, infile
)) {
618 if (im
->open
[index
]) {
621 gdImageSetPixel(im
, xpos
, row
, index
);
623 /* Could create a new variable, but it isn't really worth it */
624 for (xpos
= padding
; xpos
> 0; --xpos
) {
625 if (!gdGetByte(&index
, infile
)) {
633 if (bmp_read_rle(im
, infile
, info
)) {
644 static int bmp_read_rle(gdImagePtr im
, gdIOCtxPtr infile
, bmp_info_t
*info
)
646 int ypos
= 0, xpos
= 0, row
= 0, index
= 0;
647 int rle_length
= 0, rle_data
= 0;
650 int pixels_per_byte
= 8 / info
->depth
;
652 for (ypos
= 0; ypos
< info
->height
&& xpos
<= info
->width
;) {
653 if (!gdGetByte(&rle_length
, infile
) || !gdGetByte(&rle_data
, infile
)) {
656 row
= info
->height
- ypos
- 1;
658 if (rle_length
!= BMP_RLE_COMMAND
) {
659 if (im
->open
[rle_data
]) {
660 im
->open
[rle_data
] = 0;
663 for (i
= 0; (i
< rle_length
) && (xpos
< info
->width
);) {
664 for (j
= 1; (j
<= pixels_per_byte
) && (xpos
< info
->width
) && (i
< rle_length
); j
++, xpos
++, i
++) {
665 index
= (rle_data
& (((1 << info
->depth
) - 1) << (8 - (j
* info
->depth
)))) >> (8 - (j
* info
->depth
));
666 if (im
->open
[index
]) {
669 gdImageSetPixel(im
, xpos
, row
, index
);
672 } else if (rle_length
== BMP_RLE_COMMAND
&& rle_data
> 2) {
673 /* Uncompressed RLE needs to be even */
675 for (i
= 0; (i
< rle_data
) && (xpos
< info
->width
); i
+= pixels_per_byte
) {
676 int max_pixels
= pixels_per_byte
;
678 if (!gdGetByte(&index
, infile
)) {
683 if (rle_data
- i
< max_pixels
) {
684 max_pixels
= rle_data
- i
;
687 for (j
= 1; (j
<= max_pixels
) && (xpos
< info
->width
); j
++, xpos
++) {
688 int temp
= (index
>> (8 - (j
* info
->depth
))) & ((1 << info
->depth
) - 1);
689 if (im
->open
[temp
]) {
692 gdImageSetPixel(im
, xpos
, row
, temp
);
696 /* Make sure the bytes read are even */
697 if (padding
% 2 && !gdGetByte(&index
, infile
)) {
700 } else if (rle_length
== BMP_RLE_COMMAND
&& rle_data
== BMP_RLE_ENDOFLINE
) {
704 } else if (rle_length
== BMP_RLE_COMMAND
&& rle_data
== BMP_RLE_DELTA
) {
705 /* Delta Record, used for bmp files that contain other data*/
706 if (!gdGetByte(&rle_length
, infile
) || !gdGetByte(&rle_data
, infile
)) {
711 } else if (rle_length
== BMP_RLE_COMMAND
&& rle_data
== BMP_RLE_ENDOFBITMAP
) {
719 #if GD_MAJOR_VERSION == 2 && GD_MINOR_VERSION < 1
720 static int gdGetWordLSB(signed short int *result
, gdIOCtx
* ctx
)
722 unsigned int high
= 0, low
= 0;
723 low
= (ctx
->getC
) (ctx
);
728 high
= (ctx
->getC
) (ctx
);
734 *result
= (high
<< 8) | low
;
740 static int gdGetIntLSB(signed int *result
, gdIOCtx
* ctx
)
745 c
= (ctx
->getC
) (ctx
);
752 c
= (ctx
->getC
) (ctx
);
759 c
= (ctx
->getC
) (ctx
);
766 c
= (ctx
->getC
) (ctx
);
773 *result
= (signed int)r
;