Added io_buffer for reading from scalars. Also added test cases. Added
[imager.git] / bmp.c
CommitLineData
261f91c5
TC
1#include "image.h"
2#include <stdarg.h>
3
705fd961
TC
4/*
5=head1 NAME
261f91c5 6
705fd961
TC
7bmp.c - read and write windows BMP files
8
9=head1 SYNOPSIS
10
11 i_img *im;
12 io_glue *ig;
13
14 if (!i_writebmp_wiol(im, ig)) {
15 ... error ...
16 }
17 im = i_readbmp(ig);
18
19=head1 DESCRIPTION
20
21Reads and writes Windows BMP files.
22
23=over
24
25=cut
26*/
27
28#define FILEHEAD_SIZE 14
29#define INFOHEAD_SIZE 40
30#define BI_RGB 0
31#define BI_RLE8 1
32#define BI_RLE4 2
33#define BI_BITFIELDS 3
34#define BMPRLE_ENDOFLINE 0
35#define BMPRLE_ENDOFBMP 1
36#define BMPRLE_DELTA 2
37
38static int read_packed(io_glue *ig, char *format, ...);
39static int write_packed(io_glue *ig, char *format, ...);
40static int write_bmphead(io_glue *ig, i_img *im, int bit_count,
41 int data_size);
42static int write_1bit_data(io_glue *ig, i_img *im);
43static int write_4bit_data(io_glue *ig, i_img *im);
44static int write_8bit_data(io_glue *ig, i_img *im);
45static int write_24bit_data(io_glue *ig, i_img *im);
46static int read_bmp_pal(io_glue *ig, i_img *im, int count);
47static i_img *read_1bit_bmp(io_glue *ig, int xsize, int ysize,
48 int clr_used);
49static i_img *read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
50 int compression);
51static i_img *read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
52 int compression);
53static i_img *read_direct_bmp(io_glue *ig, int xsize, int ysize,
54 int bit_count, int clr_used, int compression);
55
56/*
57=item i_writebmp_wiol(im, io_glue)
58
59Writes the image as a BMP file. Uses 1-bit, 4-bit, 8-bit or 24-bit
60formats depending on the image.
61
62Never compresses the image.
63
64=cut
65*/
66int
67i_writebmp_wiol(i_img *im, io_glue *ig) {
68 io_glue_commit_types(ig);
69 i_clear_error();
70
71 /* pick a format */
72 if (im->type == i_direct_type) {
73 return write_24bit_data(ig, im);
74 }
75 else {
76 int pal_size;
77
78 /* must be paletted */
79 pal_size = i_colorcount(im);
80 if (pal_size <= 2) {
81 return write_1bit_data(ig, im);
82 }
83 else if (pal_size <= 16) {
84 return write_4bit_data(ig, im);
85 }
86 else {
87 return write_8bit_data(ig, im);
88 }
89 }
90}
91
92/*
93=item i_readbmp_wiol(ig)
94
95Reads a Windows format bitmap from the given file.
96
97Handles BI_RLE4 and BI_RLE8 compressed images. Attempts to handle
98BI_BITFIELDS images too, but I need a test image.
99
100=cut
101*/
102
103i_img *
104i_readbmp_wiol(io_glue *ig) {
105 int b_magic, m_magic, filesize, dummy, infohead_size;
106 int xsize, ysize, planes, bit_count, compression, size_image, xres, yres;
107 int clr_used, clr_important, offbits;
108 i_img *im;
109
110 io_glue_commit_types(ig);
111 i_clear_error();
112
113 if (!read_packed(ig, "CCVvvVVVVvvVVVVVV", &b_magic, &m_magic, &filesize,
114 &dummy, &dummy, &offbits, &infohead_size,
115 &xsize, &ysize, &planes,
116 &bit_count, &compression, &size_image, &xres, &yres,
117 &clr_used, &clr_important)) {
118 i_push_error(0, "file too short");
119 return 0;
120 }
121 if (b_magic != 'B' || m_magic != 'M' || infohead_size != INFOHEAD_SIZE
122 || planes != 1) {
123 i_push_error(0, "not a BMP file");
124 return 0;
125 }
126
127 switch (bit_count) {
128 case 1:
129 im = read_1bit_bmp(ig, xsize, ysize, clr_used);
130 break;
131
132 case 4:
133 im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression);
134 break;
135
136 case 8:
137 im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression);
138 break;
139
140 case 32:
141 case 24:
142 case 16:
143 im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression);
144 break;
efdc2568
TC
145
146 default:
147 i_push_errorf(0, "unknown bit count for BMP file (%d)", bit_count);
148 return NULL;
705fd961
TC
149 }
150
151 /* store the resolution */
152 if (xres && !yres)
153 yres = xres;
154 else if (yres && !xres)
155 xres = yres;
156 if (xres) {
157 i_tags_set_float(&im->tags, "i_xres", 0, xres * 0.0254);
158 i_tags_set_float(&im->tags, "i_yres", 0, yres * 0.0254);
159 }
160 i_tags_addn(&im->tags, "bmp_compression", 0, compression);
161 i_tags_addn(&im->tags, "bmp_important_colors", 0, clr_important);
162
163 return im;
164}
165
166/*
167=back
168
169=head1 IMPLEMENTATION FUNCTIONS
170
171Internal functions used in the implementation.
172
173=over
174
175=item read_packed(ig, format, ...)
176
177Reads from the specified "file" the specified sizes. The format codes
178match those used by perl's pack() function, though only a few are
179implemented. In all cases the vararg arguement is an int *.
180
181Returns non-zero if all of the arguments were read.
182
183=cut
261f91c5
TC
184*/
185static
186int read_packed(io_glue *ig, char *format, ...) {
187 unsigned char buf[4];
188 va_list ap;
189 int *p;
190
191 va_start(ap, format);
192
193 while (*format) {
194 p = va_arg(ap, int *);
195
196 switch (*format) {
197 case 'v':
198 if (ig->readcb(ig, buf, 2) == -1)
199 return 0;
200 *p = buf[0] + (buf[1] << 8);
201 break;
202
203 case 'V':
204 if (ig->readcb(ig, buf, 4) == -1)
205 return 0;
206 *p = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
207 break;
208
209 case 'C':
210 if (ig->readcb(ig, buf, 1) == -1)
211 return 0;
212 *p = buf[0];
213 break;
214
215 case 'c':
216 if (ig->readcb(ig, buf, 1) == -1)
217 return 0;
218 *p = (char)buf[0];
219 break;
220
705fd961
TC
221 case '3': /* extension - 24-bit number */
222 if (ig->readcb(ig, buf, 3) == -1)
223 return 0;
224 *p = buf[0] + (buf[1] << 8) + (buf[2] << 16);
225 break;
226
261f91c5
TC
227 default:
228 m_fatal(1, "Unknown read_packed format code 0x%02x", *format);
229 }
230 ++format;
231 }
232 return 1;
233}
234
705fd961
TC
235/*
236=item write_packed(ig, format, ...)
237
238Writes packed data to the specified io_glue.
239
240Returns non-zero on success.
241
242=cut
243*/
244
261f91c5
TC
245static int
246write_packed(io_glue *ig, char *format, ...) {
247 unsigned char buf[4];
248 va_list ap;
249 int i;
250
251 va_start(ap, format);
252
253 while (*format) {
254 i = va_arg(ap, unsigned int);
255
256 switch (*format) {
257 case 'v':
258 buf[0] = i & 255;
259 buf[1] = i / 256;
260 if (ig->writecb(ig, buf, 2) == -1)
261 return 0;
262 break;
263
264 case 'V':
265 buf[0] = i & 0xFF;
266 buf[1] = (i >> 8) & 0xFF;
267 buf[2] = (i >> 16) & 0xFF;
268 buf[3] = (i >> 24) & 0xFF;
269 if (ig->writecb(ig, buf, 4) == -1)
270 return 0;
271 break;
272
273 case 'C':
274 case 'c':
275 buf[0] = i & 0xFF;
276 if (ig->writecb(ig, buf, 1) == -1)
277 return 0;
278 break;
279
280 default:
281 m_fatal(1, "Unknown read_packed format code 0x%02x", *format);
282 }
283 ++format;
284 }
285 va_end(ap);
286
287 return 1;
288}
289
705fd961
TC
290/*
291=item write_bmphead(ig, im, bit_count, data_size)
292
293Writes a Windows BMP header to the file.
294
295Returns non-zero on success.
296
297=cut
298*/
261f91c5
TC
299
300static
301int write_bmphead(io_glue *ig, i_img *im, int bit_count, int data_size) {
302 double xres, yres;
303 int got_xres, got_yres, aspect_only;
304 int colors_used = 0;
305 int offset = FILEHEAD_SIZE + INFOHEAD_SIZE;
306
307 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
308 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
309 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
310 aspect_only = 0;
311 if (!got_xres) {
312 if (!got_yres)
313 xres = yres = 72;
314 else
315 xres = yres;
316 }
317 else {
318 if (!got_yres)
319 yres = xres;
320 }
321 if (xres <= 0 || yres <= 0)
322 xres = yres = 72;
323 if (aspect_only) {
324 /* scale so the smaller value is 72 */
325 double ratio;
326 if (xres < yres) {
327 ratio = 72.0 / xres;
328 }
329 else {
330 ratio = 72.0 / yres;
331 }
332 xres *= ratio;
333 yres *= ratio;
334 }
335 /* now to pels/meter */
336 xres *= 100.0/2.54;
337 yres *= 100.0/2.54;
338
339 if (im->type == i_palette_type) {
340 colors_used = i_colorcount(im);
341 offset += 4 * colors_used;
342 }
343
344 if (!write_packed(ig, "CCVvvVVVVvvVVVVVV", 'B', 'M', data_size+offset,
345 0, 0, offset, INFOHEAD_SIZE, im->xsize, im->ysize, 1,
705fd961
TC
346 bit_count, BI_RGB, 0, (int)(xres+0.5), (int)(yres+0.5),
347 colors_used, colors_used)){
261f91c5
TC
348 i_push_error(0, "cannot write bmp header");
349 return 0;
350 }
351 if (im->type == i_palette_type) {
352 int i;
353 i_color c;
354
355 for (i = 0; i < colors_used; ++i) {
356 i_getcolors(im, i, &c, 1);
357 if (im->channels >= 3) {
358 if (!write_packed(ig, "CCCC", c.channel[2], c.channel[1],
359 c.channel[0], 0)) {
360 i_push_error(0, "cannot write palette entry");
361 return 0;
362 }
363 }
364 else {
365 if (!write_packed(ig, "CCCC", c.channel[0], c.channel[0],
366 c.channel[0], 0)) {
367 i_push_error(0, "cannot write palette entry");
368 return 0;
369 }
370 }
371 }
372 }
373
374 return 1;
375}
376
705fd961
TC
377/*
378=item write_1bit_data(ig, im)
379
380Writes the image data as a 1-bit/pixel image.
381
382Returns non-zero on success.
383
384=cut
385*/
261f91c5
TC
386static int
387write_1bit_data(io_glue *ig, i_img *im) {
388 i_palidx *line;
389 unsigned char *packed;
390 int byte;
391 int mask;
392 unsigned char *out;
393 int line_size = (im->xsize+7) / 8;
394 int x, y;
395
396 /* round up to nearest multiple of four */
397 line_size = (line_size + 3) / 4 * 4;
398
399 if (!write_bmphead(ig, im, 1, line_size * im->ysize))
400 return 0;
401
402 line = mymalloc(im->xsize + 8);
403 memset(line + im->xsize, 0, 8);
404
405 packed = mymalloc(line_size);
406 memset(packed, 0, line_size);
407
408 for (y = im->ysize-1; y >= 0; --y) {
409 i_gpal(im, 0, im->xsize, y, line);
410 mask = 0x80;
411 byte = 0;
412 out = packed;
413 for (x = 0; x < im->xsize; ++x) {
414 if (line[x])
415 byte |= mask;
416 if ((mask >>= 1) == 0) {
417 *out++ = byte;
418 byte = 0;
419 mask = 0x80;
420 }
421 }
422 if (mask != 0x80) {
423 *out++ = byte;
424 }
425 if (ig->writecb(ig, packed, line_size) < 0) {
426 myfree(packed);
427 myfree(line);
428 i_push_error(0, "writing 1 bit/pixel packed data");
429 return 0;
430 }
431 }
432 myfree(packed);
433 myfree(line);
434
435 return 1;
436}
437
705fd961
TC
438/*
439=item write_4bit_data(ig, im)
440
441Writes the image data as a 4-bit/pixel image.
442
443Returns non-zero on success.
444
445=cut
446*/
261f91c5
TC
447static int
448write_4bit_data(io_glue *ig, i_img *im) {
449 i_palidx *line;
450 unsigned char *packed;
451 unsigned char *out;
452 int line_size = (im->xsize+1) / 2;
453 int x, y;
454
455 /* round up to nearest multiple of four */
456 line_size = (line_size + 3) / 4 * 4;
457
458 if (!write_bmphead(ig, im, 4, line_size * im->ysize))
459 return 0;
460
461 line = mymalloc(im->xsize + 2);
462 memset(line + im->xsize, 0, 2);
463
464 packed = mymalloc(line_size);
465 memset(packed, 0, line_size);
466
467 for (y = im->ysize-1; y >= 0; --y) {
468 i_gpal(im, 0, im->xsize, y, line);
469 out = packed;
470 for (x = 0; x < im->xsize; x += 2) {
471 *out++ = (line[x] << 4) + line[x+1];
472 }
473 if (ig->writecb(ig, packed, line_size) < 0) {
474 myfree(packed);
475 myfree(line);
476 i_push_error(0, "writing 4 bit/pixel packed data");
477 return 0;
478 }
479 }
480 myfree(packed);
481 myfree(line);
482
483 return 1;
484}
485
705fd961
TC
486/*
487=item write_8bit_data(ig, im)
488
489Writes the image data as a 8-bit/pixel image.
490
491Returns non-zero on success.
492
493=cut
494*/
261f91c5
TC
495static int
496write_8bit_data(io_glue *ig, i_img *im) {
497 i_palidx *line;
498 int line_size = im->xsize;
499 int x, y;
500
501 /* round up to nearest multiple of four */
502 line_size = (line_size + 3) / 4 * 4;
503
504 if (!write_bmphead(ig, im, 8, line_size * im->ysize))
505 return 0;
506
507 line = mymalloc(im->xsize + 4);
508 memset(line + im->xsize, 0, 4);
509
510 for (y = im->ysize-1; y >= 0; --y) {
511 i_gpal(im, 0, im->xsize, y, line);
512 if (ig->writecb(ig, line, line_size) < 0) {
513 myfree(line);
514 i_push_error(0, "writing 8 bit/pixel packed data");
515 return 0;
516 }
517 }
518 myfree(line);
519
520 return 1;
521}
522
523static int bgr_chans[] = { 2, 1, 0, };
524static int grey_chans[] = { 0, 0, 0, };
525
705fd961
TC
526/*
527=item write_24bit_data(ig, im)
528
529Writes the image data as a 24-bit/pixel image.
530
531Returns non-zero on success.
532
533=cut
534*/
261f91c5
TC
535static int
536write_24bit_data(io_glue *ig, i_img *im) {
537 int *chans;
538 unsigned char *samples;
539 int x, y;
540 int line_size = 3 * im->xsize;
541
542 line_size = (line_size + 3) / 4 * 4;
543
544 if (!write_bmphead(ig, im, 24, line_size * im->ysize))
545 return 0;
546 chans = im->channels >= 3 ? bgr_chans : grey_chans;
547 samples = mymalloc(line_size);
548 for (y = im->ysize-1; y >= 0; --y) {
549 i_gsamp(im, 0, im->xsize, y, samples, chans, 3);
550 if (ig->writecb(ig, samples, line_size) < 0) {
551 i_push_error(0, "writing image data");
552 myfree(samples);
553 return 0;
554 }
555 }
556 myfree(samples);
557
558 return 1;
559}
560
705fd961
TC
561/*
562=item read_bmp_pal(ig, im, count)
261f91c5 563
705fd961 564Reads count palette entries from the file and add them to the image.
261f91c5 565
705fd961 566Returns non-zero on success.
261f91c5 567
705fd961
TC
568=cut
569*/
261f91c5
TC
570static int
571read_bmp_pal(io_glue *ig, i_img *im, int count) {
572 int i;
573 int r, g, b, x;
574 i_color c;
575
576 for (i = 0; i < count; ++i) {
577 if (!read_packed(ig, "CCCC", &b, &g, &r, &x)) {
578 i_push_error(0, "reading BMP palette");
579 return 0;
580 }
581 c.channel[0] = r;
582 c.channel[1] = g;
583 c.channel[2] = b;
584 if (i_addcolors(im, &c, 1) < 0)
585 return 0;
586 }
587
588 return 1;
589}
590
705fd961
TC
591/*
592=item read_1bit_bmp(ig, xsize, ysize, clr_used)
593
594Reads in the palette and image data for a 1-bit/pixel image.
595
596Returns the image or NULL.
597
598=cut
599*/
261f91c5
TC
600static i_img *
601read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used) {
602 i_img *im;
603 int x, y, lasty, yinc;
604 i_palidx *line, *p;
605 unsigned char *packed;
606 int line_size = (xsize + 7)/8;
607 int byte, bit;
608 unsigned char *in;
609
610 line_size = (line_size+3) / 4 * 4;
611
612 if (ysize > 0) {
613 y = ysize-1;
614 lasty = -1;
615 yinc = -1;
616 }
617 else {
618 /* when ysize is -ve it's a top-down image */
619 ysize = -ysize;
620 y = 0;
621 lasty = ysize;
622 yinc = 1;
623 }
624 im = i_img_pal_new(xsize, ysize, 3, 256);
705fd961
TC
625 if (!clr_used)
626 clr_used = 2;
261f91c5
TC
627 if (!read_bmp_pal(ig, im, clr_used)) {
628 i_img_destroy(im);
629 return NULL;
630 }
631
632 packed = mymalloc(line_size);
633 line = mymalloc(xsize+8);
634 while (y != lasty) {
635 if (ig->readcb(ig, packed, line_size) != line_size) {
636 myfree(packed);
637 myfree(line);
638 i_push_error(0, "reading 1-bit bmp data");
639 i_img_destroy(im);
640 return NULL;
641 }
642 in = packed;
643 bit = 0x80;
644 p = line;
645 for (x = 0; x < xsize; ++x) {
646 *p++ = (*in & bit) ? 1 : 0;
647 bit >>= 1;
648 if (!bit) {
649 ++in;
650 bit = 0x80;
651 }
652 }
653 i_ppal(im, 0, xsize, y, line);
654 y += yinc;
655 }
656
657 return im;
658}
261f91c5 659
705fd961
TC
660/*
661=item read_4bit_bmp(ig, xsize, ysize, clr_used, compression)
662
663Reads in the palette and image data for a 4-bit/pixel image.
664
665Returns the image or NULL.
666
667Hopefully this will be combined with the following function at some
668point.
669
670=cut
671*/
261f91c5 672static i_img *
705fd961
TC
673read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
674 int compression) {
261f91c5
TC
675 i_img *im;
676 int x, y, lasty, yinc;
677 i_palidx *line, *p;
678 unsigned char *packed;
679 int line_size = (xsize + 1)/2;
680 unsigned char *in;
705fd961 681 int size, i;
261f91c5
TC
682
683 line_size = (line_size+3) / 4 * 4;
684
685 if (ysize > 0) {
686 y = ysize-1;
687 lasty = -1;
688 yinc = -1;
689 }
690 else {
691 /* when ysize is -ve it's a top-down image */
692 ysize = -ysize;
693 y = 0;
694 lasty = ysize;
695 yinc = 1;
696 }
697 im = i_img_pal_new(xsize, ysize, 3, 256);
705fd961
TC
698 if (!clr_used)
699 clr_used = 16;
261f91c5
TC
700 if (!read_bmp_pal(ig, im, clr_used)) {
701 i_img_destroy(im);
702 return NULL;
703 }
704
705fd961
TC
705 if (line_size < 260)
706 packed = mymalloc(260);
707 else
708 packed = mymalloc(line_size);
261f91c5
TC
709 line = mymalloc(xsize+1);
710 if (compression == BI_RGB) {
711 while (y != lasty) {
712 if (ig->readcb(ig, packed, line_size) != line_size) {
713 myfree(packed);
714 myfree(line);
715 i_push_error(0, "reading 4-bit bmp data");
716 i_img_destroy(im);
717 return NULL;
718 }
719 in = packed;
720 p = line;
721 for (x = 0; x < xsize; x+=2) {
722 *p++ = *in >> 4;
723 *p++ = *in & 0x0F;
724 ++in;
725 }
726 i_ppal(im, 0, xsize, y, line);
727 y += yinc;
728 }
729 }
730 else if (compression == BI_RLE4) {
705fd961
TC
731 int read_size;
732 int want_high;
733 int count;
734
735 x = 0;
736 while (1) {
737 /* there's always at least 2 bytes in a sequence */
738 if (ig->readcb(ig, packed, 2) != 2) {
739 myfree(packed);
740 myfree(line);
741 i_push_error(0, "missing data during decompression");
742 i_img_destroy(im);
743 return NULL;
744 }
745 else if (packed[0]) {
746 line[0] = packed[1] >> 4;
747 line[1] = packed[1] & 0x0F;
748 for (i = 0; i < packed[0]; i += 2) {
749 if (i < packed[0]-1)
750 i_ppal(im, x, x+2, y, line);
751 else
752 i_ppal(im, x, x+(packed[0]-i), y, line);
753 x += 2;
754 }
755 } else {
756 switch (packed[1]) {
757 case BMPRLE_ENDOFLINE:
758 x = 0;
759 y += yinc;
760 break;
761
762 case BMPRLE_ENDOFBMP:
4dfa5522
AMH
763 myfree(packed);
764 myfree(line);
705fd961
TC
765 return im;
766
767 case BMPRLE_DELTA:
768 if (ig->readcb(ig, packed, 2) != 2) {
769 myfree(packed);
770 myfree(line);
771 i_push_error(0, "missing data during decompression");
772 i_img_destroy(im);
773 return NULL;
774 }
775 x += packed[0];
776 y += yinc * packed[1];
777 break;
778
779 default:
780 count = packed[1];
781 size = (count + 1) / 2;
782 read_size = (size+1) / 2 * 2;
783 if (ig->readcb(ig, packed, read_size) != read_size) {
784 myfree(packed);
785 myfree(line);
786 i_push_error(0, "missing data during decompression");
787 /*i_img_destroy(im);*/
788 return im;
789 }
790 for (i = 0; i < size; ++i) {
791 line[0] = packed[i] >> 4;
792 line[1] = packed[i] & 0xF;
793 i_ppal(im, x, x+2, y, line);
794 x += 2;
795 }
796 break;
797 }
798 }
799 }
800 }
801 else { /*if (compression == BI_RLE4) {*/
802 myfree(packed);
803 myfree(line);
804 i_push_error(0, "bad compression for 4-bit image");
805 i_img_destroy(im);
806 return NULL;
261f91c5
TC
807 }
808
809 return im;
810}
811
705fd961
TC
812/*
813=item read_8bit_bmp(ig, xsize, ysize, clr_used, compression)
261f91c5 814
705fd961
TC
815Reads in the palette and image data for a 8-bit/pixel image.
816
817Returns the image or NULL.
818
819=cut
820*/
821static i_img *
822read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
823 int compression) {
261f91c5 824 i_img *im;
705fd961
TC
825 int x, y, lasty, yinc;
826 i_palidx *line, *p;
827 int line_size = xsize;
828 unsigned char *in;
261f91c5 829
705fd961
TC
830 line_size = (line_size+3) / 4 * 4;
831
832 if (ysize > 0) {
833 y = ysize-1;
834 lasty = -1;
835 yinc = -1;
261f91c5 836 }
705fd961
TC
837 else {
838 /* when ysize is -ve it's a top-down image */
839 ysize = -ysize;
840 y = 0;
841 lasty = ysize;
842 yinc = 1;
261f91c5 843 }
705fd961
TC
844 im = i_img_pal_new(xsize, ysize, 3, 256);
845 if (!clr_used)
846 clr_used = 256;
847 if (!read_bmp_pal(ig, im, clr_used)) {
848 i_img_destroy(im);
849 return NULL;
850 }
851
852 line = mymalloc(line_size);
853 if (compression == BI_RGB) {
854 while (y != lasty) {
855 if (ig->readcb(ig, line, line_size) != line_size) {
856 myfree(line);
857 i_push_error(0, "reading 8-bit bmp data");
858 i_img_destroy(im);
859 return NULL;
860 }
861 i_ppal(im, 0, xsize, y, line);
862 y += yinc;
863 }
864 }
865 else if (compression == BI_RLE8) {
866 int read_size;
867 int want_high;
868 int count;
869 unsigned char packed[2];
870
871 x = 0;
872 while (1) {
873 /* there's always at least 2 bytes in a sequence */
874 if (ig->readcb(ig, packed, 2) != 2) {
875 myfree(line);
876 i_push_error(0, "missing data during decompression");
877 i_img_destroy(im);
878 return NULL;
879 }
880 if (packed[0]) {
881 memset(line, packed[1], packed[0]);
882 i_ppal(im, x, x+packed[0], y, line);
883 x += packed[0];
884 } else {
885 switch (packed[1]) {
886 case BMPRLE_ENDOFLINE:
887 x = 0;
888 y += yinc;
889 break;
890
891 case BMPRLE_ENDOFBMP:
4dfa5522 892 myfree(line);
705fd961
TC
893 return im;
894
895 case BMPRLE_DELTA:
896 if (ig->readcb(ig, packed, 2) != 2) {
897 myfree(line);
898 i_push_error(0, "missing data during decompression");
899 i_img_destroy(im);
900 return NULL;
901 }
902 x += packed[0];
903 y += yinc * packed[1];
904 break;
905
906 default:
907 count = packed[1];
908 read_size = (count+1) / 2 * 2;
909 if (ig->readcb(ig, line, read_size) != read_size) {
910 myfree(line);
911 i_push_error(0, "missing data during decompression");
912 i_img_destroy(im);
913 return NULL;
914 }
915 i_ppal(im, x, x+count, y, line);
916 x += count;
917 break;
918 }
919 }
920 }
921 }
922 else {
923 myfree(line);
924 i_push_errorf(0, "unknown 8-bit BMP compression %d", compression);
925 i_img_destroy(im);
926 return NULL;
927 }
928
929 return im;
930}
931
932struct bm_masks {
933 unsigned masks[3];
934 int shifts[3];
935};
936static struct bm_masks std_masks[] =
937{
938 { /* 16-bit */
939 { 0770000, 00007700, 00000077, },
940 { 10, 4, -2, },
941 },
942 { /* 24-bit */
943 { 0xFF0000, 0x00FF00, 0x0000FF, },
944 { 16, 8, 0, },
945 },
946 { /* 32-bit */
947 { 0xFF0000, 0x00FF00, 0x0000FF, },
948 { 16, 8, 0, },
949 },
950};
951
952/*
953=item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression)
954
955Skips the palette and reads in the image data for a direct colour image.
956
957Returns the image or NULL.
958
959=cut
960*/
961static i_img *
962read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
963 int clr_used, int compression) {
964 i_img *im;
965 int x, y, lasty, yinc;
966 i_color *line, *p;
967 unsigned char *in;
968 int pix_size = bit_count / 8;
969 int line_size = xsize * pix_size;
970 struct bm_masks masks;
971 char unpack_code[2] = "";
972 int i;
973 int extras;
974 char junk[4];
261f91c5 975
705fd961
TC
976 unpack_code[0] = *("v3V"+pix_size-2);
977 unpack_code[1] = '\0';
261f91c5 978
705fd961
TC
979 line_size = (line_size+3) / 4 * 4;
980 extras = line_size - xsize * pix_size;
261f91c5 981
705fd961
TC
982 if (ysize > 0) {
983 y = ysize-1;
984 lasty = -1;
985 yinc = -1;
986 }
987 else {
988 /* when ysize is -ve it's a top-down image */
989 ysize = -ysize;
990 y = 0;
991 lasty = ysize;
992 yinc = 1;
993 }
994 line = mymalloc(line_size);
995 if (compression == BI_RGB) {
996 masks = std_masks[pix_size-2];
997
998 /* there's a potential "palette" after the header */
999 for (i = 0; i < clr_used; ++clr_used) {
1000 char buf[4];
1001 if (ig->readcb(ig, buf, 4) != 4) {
1002 i_push_error(0, "skipping colors");
1003 return 0;
1004 }
1005 }
1006 }
1007 else if (compression == BI_BITFIELDS) {
1008 int pos, bit;
1009 for (i = 0; i < 3; ++i) {
1010 if (!read_packed(ig, "V", masks.masks+i)) {
1011 i_push_error(0, "reading pixel masks");
1012 return 0;
1013 }
1014 /* work out a shift for the mask */
1015 pos = 0;
1016 bit = masks.masks[i] & -masks.masks[i];
1017 while (bit) {
1018 ++pos;
1019 bit >>= 1;
1020 }
1021 masks.shifts[i] = pos - 8;
1022 }
1023 }
261f91c5 1024
705fd961
TC
1025 im = i_img_empty(NULL, xsize, ysize);
1026
1027 line = mymalloc(sizeof(i_color) * xsize);
1028 while (y != lasty) {
1029 p = line;
1030 for (x = 0; x < xsize; ++x) {
1031 unsigned pixel;
1032 if (!read_packed(ig, unpack_code, &pixel)) {
1033 i_push_error(0, "reading image data");
1034 myfree(line);
1035 i_img_destroy(im);
1036 return NULL;
1037 }
1038 for (i = 0; i < 3; ++i) {
1039 if (masks.shifts[i] > 0)
1040 p->channel[i] = (pixel & masks.masks[i]) >> masks.shifts[i];
1041 else
1042 p->channel[i] = (pixel & masks.masks[i]) << -masks.shifts[i];
1043 }
1044 ++p;
1045 }
1046 i_plin(im, 0, xsize, y, line);
1047 if (extras)
1048 ig->readcb(ig, junk, extras);
1049 y += yinc;
261f91c5 1050 }
705fd961
TC
1051 myfree(line);
1052
1053 return im;
261f91c5 1054}
705fd961
TC
1055
1056/*
1057=head1 SEE ALSO
1058
1059Imager(3)
1060
1061=head1 AUTHOR
1062
1063Tony Cook <tony@develop-help.com>
1064
1065=head1 RESTRICTIONS
1066
1067Cannot save as compressed BMP.
1068
1069=head1 BUGS
1070
1071Doesn't handle OS/2 bitmaps.
1072
107316-bit/pixel images haven't been tested. (I need an image).
1074
1075BI_BITFIELDS compression hasn't been tested (I need an image).
1076
1077=cut
1078*/