FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / utils / wpseditor / screenshot / gd_bmp.c
blob8fe737da03607de5d11b0af8855b2180e688ef5c
1 /*
2 Stolen from http://cvs.php.net/viewcvs.cgi/gd/playground/gdbmp/
3 */
5 /*
6 gd_bmp.c
8 Bitmap format support for libgd
10 * Written 2007, Scott MacVicar
11 ---------------------------------------------------------------------------
13 Todo:
15 Bitfield encoding
17 ----------------------------------------------------------------------------
19 /* $Id$ */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <stdio.h>
25 #include <math.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include "gd.h"
29 #include "bmp.h"
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);
49 #endif
51 #define BMP_DEBUG(s)
53 gdImagePtr gdImageCreateFromBmpCtx(gdIOCtxPtr infile);
55 gdImagePtr gdImageCreateFromBmp(FILE * inFile)
57 gdImagePtr im = 0;
58 gdIOCtx *in = gdNewFileCtx(inFile);
59 im = gdImageCreateFromBmpCtx(in);
60 in->gd_free(in);
61 return im;
64 gdImagePtr gdImageCreateFromBmpPtr(int size, void *data)
66 gdImagePtr im;
67 gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
68 im = gdImageCreateFromBmpCtx(in);
69 in->gd_free(in);
70 return im;
73 gdImagePtr gdImageCreateFromBmpCtx(gdIOCtxPtr infile)
75 bmp_hdr_t *hdr;
76 bmp_info_t *info;
77 gdImagePtr im = NULL;
78 int error = 0;
80 if (!(hdr= (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) {
81 return NULL;
84 if (bmp_read_header(infile, hdr)) {
85 gdFree(hdr);
86 return NULL;
89 if (hdr->magic != 0x4d42) {
90 gdFree(hdr);
91 return NULL;
94 if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) {
95 gdFree(hdr);
96 return NULL;
99 if (bmp_read_info(infile, info)) {
100 gdFree(hdr);
101 gdFree(info);
102 return NULL;
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);
114 } else {
115 im = gdImageCreate(info->width, info->height);
118 if (!im) {
119 gdFree(hdr);
120 gdFree(info);
121 return NULL;
124 switch (info->depth) {
125 case 1:
126 BMP_DEBUG(printf("1-bit image\n"));
127 error = bmp_read_1bit(im, infile, info, hdr);
128 break;
129 case 4:
130 BMP_DEBUG(printf("4-bit image\n"));
131 error = bmp_read_4bit(im, infile, info, hdr);
132 break;
133 case 8:
134 BMP_DEBUG(printf("8-bit image\n"));
135 error = bmp_read_8bit(im, infile, info, hdr);
136 break;
137 case 16:
138 case 24:
139 case 32:
140 BMP_DEBUG(printf("Direct BMP image\n"));
141 error = bmp_read_direct(im, infile, info, hdr);
142 break;
143 default:
144 BMP_DEBUG(printf("Unknown bit count\n"));
145 error = 1;
148 gdFree(hdr);
149 gdFree(info);
151 if (error) {
152 gdImageDestroy(im);
153 return NULL;
156 return im;
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)
168 return 1;
170 return 0;
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)) {
177 return 1;
180 switch (info->len) {
181 /* For now treat Windows v4 + v5 as v3 */
182 case BMP_WINDOWS_V3:
183 case BMP_WINDOWS_V4:
184 case BMP_WINDOWS_V5:
185 BMP_DEBUG(printf("Reading Windows Header\n"));
186 if (bmp_read_windows_v3_info(infile, info)) {
187 return 1;
189 break;
190 case BMP_OS2_V1:
191 if (bmp_read_os2_v1_info(infile, info)) {
192 return 1;
194 break;
195 case BMP_OS2_V2:
196 if (bmp_read_os2_v2_info(infile, info)) {
197 return 1;
199 break;
200 default:
201 BMP_DEBUG(printf("Unhandled bitmap\n"));
202 return 1;
204 return 0;
207 static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info)
209 if (
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)
221 return 1;
224 if (info->height < 0) {
225 info->topdown = 1;
226 info->height = -info->height;
227 } else {
228 info->topdown = 0;
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) {
235 return 1;
238 return 0;
241 static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info)
243 if (
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)
249 return 1;
252 /* OS2 v1 doesn't support topdown */
253 info->topdown = 0;
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) {
260 return 1;
263 return 0;
266 static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info)
268 char useless_bytes[24];
269 if (
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)
281 return 1;
284 /* Lets seek the next 24 pointless bytes, we don't care too much about it */
285 if (!gdGetBuf(useless_bytes, 24, infile)) {
286 return 1;
289 if (info->height < 0) {
290 info->topdown = 1;
291 info->height = -info->height;
292 } else {
293 info->topdown = 0;
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) {
300 return 1;
304 return 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) {
314 case BMP_BI_RGB:
315 /* no-op */
316 break;
318 case BMP_BI_BITFIELDS:
319 if (info->depth == 24) {
320 BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
321 return 1;
323 BMP_DEBUG(printf("Currently no bitfield support\n"));
324 return 1;
325 break;
327 case BMP_BI_RLE8:
328 if (info->depth != 8) {
329 BMP_DEBUG(printf("RLE is only valid for 8-bit images\n"));
330 return 1;
332 case BMP_BI_RLE4:
333 if (info->depth != 4) {
334 BMP_DEBUG(printf("RLE is only valid for 4-bit images\n"));
335 return 1;
337 case BMP_BI_JPEG:
338 case BMP_BI_PNG:
339 default:
340 BMP_DEBUG(printf("Unsupported BMP compression format\n"));
341 return 1;
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;
352 if (padding) {
353 padding = 4 - padding;
357 for (ypos = 0; ypos < info->height; ++ypos) {
358 if (info->topdown) {
359 row = ypos;
360 } else {
361 row = info->height - ypos - 1;
364 for (xpos = 0; xpos < info->width; xpos++) {
365 if (info->depth == 16) {
366 if (!gdGetWordLSB(&data, infile)) {
367 return 1;
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)) {
376 return 1;
378 } else {
379 if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile) || !gdGetByte(&alpha, infile)) {
380 return 1;
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)) {
388 return 1;
393 return 0;
396 static int bmp_read_palette(gdImagePtr im, gdIOCtxPtr infile, int count, int read_four)
398 int i;
399 int r, g, b, z;
401 for (i = 0; i < count; i++) {
402 if (
403 !gdGetByte(&r, infile) ||
404 !gdGetByte(&g, infile) ||
405 !gdGetByte(&b, infile) ||
406 (read_four && !gdGetByte(&z, infile))
408 return 1;
410 im->red[i] = r;
411 im->green[i] = g;
412 im->blue[i] = b;
413 im->open[i] = 1;
415 return 0;
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) {
424 return 1;
427 if (!info->numcolors) {
428 info->numcolors = 2;
429 } else if (info->numcolors < 0 || info->numcolors > 2) {
430 return 1;
433 if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
434 return 1;
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;
447 if (padding) {
448 padding = 4 - padding;
451 for (ypos = 0; ypos < info->height; ++ypos) {
452 if (info->topdown) {
453 row = ypos;
454 } else {
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(&current_byte, infile)) {
461 return 1;
464 for (bit = 0; bit < 8; bit++) {
465 index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00);
466 if (im->open[index]) {
467 im->open[index] = 0;
469 gdImageSetPixel(im, xpos + bit, row, index);
470 /* No need to read anything extra */
471 if ((xpos + bit) >= info->width) {
472 break;
477 for (xpos = padding; xpos > 0; --xpos) {
478 if (!gdGetByte(&index, infile)) {
479 return 1;
483 return 0;
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) {
492 return 1;
495 if (!info->numcolors) {
496 info->numcolors = 16;
497 } else if (info->numcolors < 0 || info->numcolors > 16) {
498 return 1;
501 if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
502 return 1;
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;
515 if (padding) {
516 padding = 4 - padding;
519 switch (info->enctype) {
520 case BMP_BI_RGB:
521 for (ypos = 0; ypos < info->height; ++ypos) {
522 if (info->topdown) {
523 row = ypos;
524 } else {
525 row = info->height - ypos - 1;
528 for (xpos = 0; xpos < info->width; xpos += 2) {
529 if (!gdGetByte(&current_byte, infile)) {
530 return 1;
533 index = (current_byte >> 4) & 0x0f;
534 if (im->open[index]) {
535 im->open[index] = 0;
537 gdImageSetPixel(im, xpos, row, index);
539 /* This condition may get called often, potential optimsations */
540 if (xpos >= info->width) {
541 break;
544 index = current_byte & 0x0f;
545 if (im->open[index]) {
546 im->open[index] = 0;
548 gdImageSetPixel(im, xpos + 1, row, index);
551 for (xpos = padding; xpos > 0; --xpos) {
552 if (!gdGetByte(&index, infile)) {
553 return 1;
557 break;
559 case BMP_BI_RLE4:
560 if (bmp_read_rle(im, infile, info)) {
561 return 1;
563 break;
565 default:
566 return 1;
568 return 0;
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;
574 int padding = 0;
576 if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) {
577 return 1;
580 if (!info->numcolors) {
581 info->numcolors = 256;
582 } else if (info->numcolors < 0 || info->numcolors > 256) {
583 return 1;
586 if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) {
587 return 1;
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;
600 if (padding) {
601 padding = 4 - padding;
604 switch (info->enctype) {
605 case BMP_BI_RGB:
606 for (ypos = 0; ypos < info->height; ++ypos) {
607 if (info->topdown) {
608 row = ypos;
609 } else {
610 row = info->height - ypos - 1;
613 for (xpos = 0; xpos < info->width; ++xpos) {
614 if (!gdGetByte(&index, infile)) {
615 return 1;
618 if (im->open[index]) {
619 im->open[index] = 0;
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)) {
626 return 1;
630 break;
632 case BMP_BI_RLE8:
633 if (bmp_read_rle(im, infile, info)) {
634 return 1;
636 break;
638 default:
639 return 1;
641 return 0;
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;
648 int padding = 0;
649 int i = 0, j = 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)) {
654 return 1;
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]) {
667 im->open[index] = 0;
669 gdImageSetPixel(im, xpos, row, index);
672 } else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) {
673 /* Uncompressed RLE needs to be even */
674 padding = 0;
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)) {
679 return 1;
681 padding++;
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]) {
690 im->open[temp] = 0;
692 gdImageSetPixel(im, xpos, row, temp);
696 /* Make sure the bytes read are even */
697 if (padding % 2 && !gdGetByte(&index, infile)) {
698 return 1;
700 } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) {
701 /* Next Line */
702 xpos = 0;
703 ypos++;
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)) {
707 return 1;
709 xpos += rle_length;
710 ypos += rle_data;
711 } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) {
712 /* End of bitmap */
713 break;
716 return 0;
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);
724 if (low == EOF) {
725 return 0;
728 high = (ctx->getC) (ctx);
729 if (high == EOF) {
730 return 0;
733 if (result) {
734 *result = (high << 8) | low;
737 return 1;
740 static int gdGetIntLSB(signed int *result, gdIOCtx * ctx)
742 int c = 0;
743 unsigned int r = 0;
745 c = (ctx->getC) (ctx);
746 if (c == EOF) {
747 return 0;
749 r |= (c << 24);
750 r >>= 8;
752 c = (ctx->getC) (ctx);
753 if (c == EOF) {
754 return 0;
756 r |= (c << 24);
757 r >>= 8;
759 c = (ctx->getC) (ctx);
760 if (c == EOF) {
761 return 0;
763 r |= (c << 24);
764 r >>= 8;
766 c = (ctx->getC) (ctx);
767 if (c == EOF) {
768 return 0;
770 r |= (c << 24);
772 if (result) {
773 *result = (signed int)r;
776 return 1;
778 #endif