Commit | Line | Data |
---|---|---|
261f91c5 | 1 | #include <stdarg.h> |
92bda632 | 2 | #include "imageri.h" |
261f91c5 | 3 | |
705fd961 TC |
4 | /* |
5 | =head1 NAME | |
261f91c5 | 6 | |
705fd961 TC |
7 | bmp.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 | ||
21 | Reads 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 | ||
8d14daab TC |
38 | #define SIGNBIT32 ((i_upacked_t)1U << 31) |
39 | #define SIGNBIT16 ((i_upacked_t)1U << 15) | |
40 | ||
41 | #define SIGNMAX32 ((1UL << 31) - 1) | |
42 | ||
705fd961 TC |
43 | static int read_packed(io_glue *ig, char *format, ...); |
44 | static int write_packed(io_glue *ig, char *format, ...); | |
45 | static int write_bmphead(io_glue *ig, i_img *im, int bit_count, | |
46 | int data_size); | |
47 | static int write_1bit_data(io_glue *ig, i_img *im); | |
48 | static int write_4bit_data(io_glue *ig, i_img *im); | |
49 | static int write_8bit_data(io_glue *ig, i_img *im); | |
50 | static int write_24bit_data(io_glue *ig, i_img *im); | |
51 | static int read_bmp_pal(io_glue *ig, i_img *im, int count); | |
662e3c02 | 52 | static i_img *read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, |
d87dc9a4 | 53 | int compression, long offbits, int allow_incomplete); |
705fd961 | 54 | static i_img *read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, |
d87dc9a4 | 55 | int compression, long offbits, int allow_incomplete); |
705fd961 | 56 | static i_img *read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, |
d87dc9a4 | 57 | int compression, long offbits, int allow_incomplete); |
705fd961 | 58 | static i_img *read_direct_bmp(io_glue *ig, int xsize, int ysize, |
403946c6 | 59 | int bit_count, int clr_used, int compression, |
d87dc9a4 | 60 | long offbits, int allow_incomplete); |
705fd961 | 61 | |
8d14daab TC |
62 | /* used for the read_packed() and write_packed() functions, an integer |
63 | * type */ | |
64 | typedef long i_packed_t; | |
65 | typedef unsigned long i_upacked_t; | |
66 | ||
705fd961 TC |
67 | /* |
68 | =item i_writebmp_wiol(im, io_glue) | |
69 | ||
70 | Writes the image as a BMP file. Uses 1-bit, 4-bit, 8-bit or 24-bit | |
71 | formats depending on the image. | |
72 | ||
73 | Never compresses the image. | |
74 | ||
75 | =cut | |
76 | */ | |
77 | int | |
78 | i_writebmp_wiol(i_img *im, io_glue *ig) { | |
705fd961 TC |
79 | i_clear_error(); |
80 | ||
81 | /* pick a format */ | |
82 | if (im->type == i_direct_type) { | |
83 | return write_24bit_data(ig, im); | |
84 | } | |
85 | else { | |
86 | int pal_size; | |
87 | ||
88 | /* must be paletted */ | |
89 | pal_size = i_colorcount(im); | |
90 | if (pal_size <= 2) { | |
91 | return write_1bit_data(ig, im); | |
92 | } | |
93 | else if (pal_size <= 16) { | |
94 | return write_4bit_data(ig, im); | |
95 | } | |
96 | else { | |
97 | return write_8bit_data(ig, im); | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | /* | |
103 | =item i_readbmp_wiol(ig) | |
104 | ||
105 | Reads a Windows format bitmap from the given file. | |
106 | ||
107 | Handles BI_RLE4 and BI_RLE8 compressed images. Attempts to handle | |
108 | BI_BITFIELDS images too, but I need a test image. | |
109 | ||
110 | =cut | |
111 | */ | |
112 | ||
113 | i_img * | |
d87dc9a4 | 114 | i_readbmp_wiol(io_glue *ig, int allow_incomplete) { |
8d14daab TC |
115 | i_packed_t b_magic, m_magic, filesize, res1, res2, infohead_size; |
116 | i_packed_t xsize, ysize, planes, bit_count, compression, size_image, xres, yres; | |
117 | i_packed_t clr_used, clr_important, offbits; | |
705fd961 | 118 | i_img *im; |
662e3c02 TC |
119 | |
120 | mm_log((1, "i_readbmp_wiol(ig %p)\n", ig)); | |
705fd961 | 121 | |
705fd961 TC |
122 | i_clear_error(); |
123 | ||
8d14daab | 124 | if (!read_packed(ig, "CCVvvVVV!V!vvVVVVVV", &b_magic, &m_magic, &filesize, |
662e3c02 | 125 | &res1, &res2, &offbits, &infohead_size, |
705fd961 TC |
126 | &xsize, &ysize, &planes, |
127 | &bit_count, &compression, &size_image, &xres, &yres, | |
128 | &clr_used, &clr_important)) { | |
d87dc9a4 | 129 | i_push_error(0, "file too short to be a BMP file"); |
705fd961 TC |
130 | return 0; |
131 | } | |
132 | if (b_magic != 'B' || m_magic != 'M' || infohead_size != INFOHEAD_SIZE | |
133 | || planes != 1) { | |
134 | i_push_error(0, "not a BMP file"); | |
135 | return 0; | |
136 | } | |
662e3c02 TC |
137 | |
138 | mm_log((1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d " | |
139 | "bit_count %d compression %d size %d xres %d yres %d clr_used %d " | |
8d14daab TC |
140 | "clr_important %d\n", (int)filesize, (int)offbits, (int)xsize, |
141 | (int)ysize, (int)planes, (int)bit_count, (int)compression, | |
142 | (int)size_image, (int)xres, (int)yres, (int)clr_used, | |
143 | (int)clr_important)); | |
77157728 | 144 | |
ae12796a | 145 | if (!i_int_check_image_file_limits(xsize, abs(ysize), 3, sizeof(i_sample_t))) { |
77157728 TC |
146 | mm_log((1, "i_readbmp_wiol: image size exceeds limits\n")); |
147 | return NULL; | |
148 | } | |
705fd961 TC |
149 | |
150 | switch (bit_count) { | |
151 | case 1: | |
9c106321 | 152 | im = read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, |
d87dc9a4 | 153 | allow_incomplete); |
705fd961 TC |
154 | break; |
155 | ||
156 | case 4: | |
9c106321 | 157 | im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, |
d87dc9a4 | 158 | allow_incomplete); |
705fd961 TC |
159 | break; |
160 | ||
161 | case 8: | |
9c106321 | 162 | im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, |
d87dc9a4 | 163 | allow_incomplete); |
705fd961 TC |
164 | break; |
165 | ||
166 | case 32: | |
167 | case 24: | |
168 | case 16: | |
403946c6 | 169 | im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression, |
d87dc9a4 | 170 | offbits, allow_incomplete); |
705fd961 | 171 | break; |
efdc2568 TC |
172 | |
173 | default: | |
8d14daab | 174 | i_push_errorf(0, "unknown bit count for BMP file (%d)", (int)bit_count); |
efdc2568 | 175 | return NULL; |
705fd961 TC |
176 | } |
177 | ||
662e3c02 TC |
178 | if (im) { |
179 | /* store the resolution */ | |
180 | if (xres && !yres) | |
181 | yres = xres; | |
182 | else if (yres && !xres) | |
183 | xres = yres; | |
184 | if (xres) { | |
2e41e30b TC |
185 | i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 4); |
186 | i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 4); | |
662e3c02 TC |
187 | } |
188 | i_tags_addn(&im->tags, "bmp_compression", 0, compression); | |
189 | i_tags_addn(&im->tags, "bmp_important_colors", 0, clr_important); | |
190 | i_tags_addn(&im->tags, "bmp_used_colors", 0, clr_used); | |
191 | i_tags_addn(&im->tags, "bmp_filesize", 0, filesize); | |
192 | i_tags_addn(&im->tags, "bmp_bit_count", 0, bit_count); | |
193 | i_tags_add(&im->tags, "i_format", 0, "bmp", 3, 0); | |
705fd961 | 194 | } |
705fd961 TC |
195 | |
196 | return im; | |
197 | } | |
198 | ||
199 | /* | |
200 | =back | |
201 | ||
202 | =head1 IMPLEMENTATION FUNCTIONS | |
203 | ||
204 | Internal functions used in the implementation. | |
205 | ||
206 | =over | |
207 | ||
208 | =item read_packed(ig, format, ...) | |
209 | ||
210 | Reads from the specified "file" the specified sizes. The format codes | |
211 | match those used by perl's pack() function, though only a few are | |
212 | implemented. In all cases the vararg arguement is an int *. | |
213 | ||
214 | Returns non-zero if all of the arguments were read. | |
215 | ||
216 | =cut | |
261f91c5 | 217 | */ |
8d14daab TC |
218 | static int |
219 | read_packed(io_glue *ig, char *format, ...) { | |
261f91c5 TC |
220 | unsigned char buf[4]; |
221 | va_list ap; | |
8d14daab TC |
222 | i_packed_t *p; |
223 | i_packed_t work; | |
224 | int code; | |
225 | int shrieking; /* format code has a ! flag */ | |
261f91c5 TC |
226 | |
227 | va_start(ap, format); | |
228 | ||
229 | while (*format) { | |
8d14daab | 230 | p = va_arg(ap, i_packed_t *); |
261f91c5 | 231 | |
8d14daab TC |
232 | code = *format++; |
233 | shrieking = *format == '!'; | |
234 | if (shrieking) ++format; | |
235 | ||
236 | switch (code) { | |
261f91c5 | 237 | case 'v': |
6d5c85a2 | 238 | if (i_io_read(ig, buf, 2) != 2) |
261f91c5 | 239 | return 0; |
8d14daab TC |
240 | work = buf[0] + ((i_packed_t)buf[1] << 8); |
241 | if (shrieking) | |
242 | *p = (work ^ SIGNBIT16) - SIGNBIT16; | |
243 | else | |
244 | *p = work; | |
261f91c5 TC |
245 | break; |
246 | ||
247 | case 'V': | |
6d5c85a2 | 248 | if (i_io_read(ig, buf, 4) != 4) |
261f91c5 | 249 | return 0; |
8d14daab TC |
250 | work = buf[0] + (buf[1] << 8) + ((i_packed_t)buf[2] << 16) + ((i_packed_t)buf[3] << 24); |
251 | if (shrieking) | |
252 | *p = (work ^ SIGNBIT32) - SIGNBIT32; | |
253 | else | |
254 | *p = work; | |
261f91c5 TC |
255 | break; |
256 | ||
257 | case 'C': | |
6d5c85a2 | 258 | if (i_io_read(ig, buf, 1) != 1) |
261f91c5 TC |
259 | return 0; |
260 | *p = buf[0]; | |
261 | break; | |
262 | ||
263 | case 'c': | |
6d5c85a2 | 264 | if (i_io_read(ig, buf, 1) != 1) |
261f91c5 TC |
265 | return 0; |
266 | *p = (char)buf[0]; | |
267 | break; | |
268 | ||
705fd961 | 269 | case '3': /* extension - 24-bit number */ |
6d5c85a2 | 270 | if (i_io_read(ig, buf, 3) != 3) |
705fd961 | 271 | return 0; |
8d14daab | 272 | *p = buf[0] + (buf[1] << 8) + ((i_packed_t)buf[2] << 16); |
705fd961 TC |
273 | break; |
274 | ||
261f91c5 | 275 | default: |
8d14daab | 276 | i_fatal(1, "Unknown read_packed format code 0x%02x", code); |
261f91c5 | 277 | } |
261f91c5 TC |
278 | } |
279 | return 1; | |
280 | } | |
281 | ||
705fd961 TC |
282 | /* |
283 | =item write_packed(ig, format, ...) | |
284 | ||
285 | Writes packed data to the specified io_glue. | |
286 | ||
287 | Returns non-zero on success. | |
288 | ||
289 | =cut | |
290 | */ | |
291 | ||
261f91c5 TC |
292 | static int |
293 | write_packed(io_glue *ig, char *format, ...) { | |
294 | unsigned char buf[4]; | |
295 | va_list ap; | |
296 | int i; | |
297 | ||
298 | va_start(ap, format); | |
299 | ||
300 | while (*format) { | |
8d14daab | 301 | i = va_arg(ap, i_upacked_t); |
261f91c5 TC |
302 | |
303 | switch (*format) { | |
304 | case 'v': | |
305 | buf[0] = i & 255; | |
306 | buf[1] = i / 256; | |
6d5c85a2 | 307 | if (i_io_write(ig, buf, 2) == -1) |
261f91c5 TC |
308 | return 0; |
309 | break; | |
310 | ||
311 | case 'V': | |
312 | buf[0] = i & 0xFF; | |
313 | buf[1] = (i >> 8) & 0xFF; | |
314 | buf[2] = (i >> 16) & 0xFF; | |
315 | buf[3] = (i >> 24) & 0xFF; | |
6d5c85a2 | 316 | if (i_io_write(ig, buf, 4) == -1) |
261f91c5 TC |
317 | return 0; |
318 | break; | |
319 | ||
320 | case 'C': | |
321 | case 'c': | |
322 | buf[0] = i & 0xFF; | |
6d5c85a2 | 323 | if (i_io_write(ig, buf, 1) == -1) |
261f91c5 TC |
324 | return 0; |
325 | break; | |
326 | ||
327 | default: | |
b1e96952 | 328 | i_fatal(1, "Unknown write_packed format code 0x%02x", *format); |
261f91c5 TC |
329 | } |
330 | ++format; | |
331 | } | |
332 | va_end(ap); | |
333 | ||
334 | return 1; | |
335 | } | |
336 | ||
705fd961 TC |
337 | /* |
338 | =item write_bmphead(ig, im, bit_count, data_size) | |
339 | ||
340 | Writes a Windows BMP header to the file. | |
341 | ||
342 | Returns non-zero on success. | |
343 | ||
344 | =cut | |
345 | */ | |
261f91c5 TC |
346 | |
347 | static | |
348 | int write_bmphead(io_glue *ig, i_img *im, int bit_count, int data_size) { | |
349 | double xres, yres; | |
350 | int got_xres, got_yres, aspect_only; | |
351 | int colors_used = 0; | |
352 | int offset = FILEHEAD_SIZE + INFOHEAD_SIZE; | |
353 | ||
8d14daab TC |
354 | if (im->xsize > SIGNMAX32 || im->ysize > SIGNMAX32) { |
355 | i_push_error(0, "image too large to write to BMP"); | |
356 | return 0; | |
357 | } | |
358 | ||
261f91c5 TC |
359 | got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres); |
360 | got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres); | |
361 | if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only)) | |
362 | aspect_only = 0; | |
363 | if (!got_xres) { | |
364 | if (!got_yres) | |
365 | xres = yres = 72; | |
366 | else | |
367 | xres = yres; | |
368 | } | |
369 | else { | |
370 | if (!got_yres) | |
371 | yres = xres; | |
372 | } | |
373 | if (xres <= 0 || yres <= 0) | |
374 | xres = yres = 72; | |
375 | if (aspect_only) { | |
376 | /* scale so the smaller value is 72 */ | |
377 | double ratio; | |
378 | if (xres < yres) { | |
379 | ratio = 72.0 / xres; | |
380 | } | |
381 | else { | |
382 | ratio = 72.0 / yres; | |
383 | } | |
384 | xres *= ratio; | |
385 | yres *= ratio; | |
386 | } | |
387 | /* now to pels/meter */ | |
388 | xres *= 100.0/2.54; | |
389 | yres *= 100.0/2.54; | |
390 | ||
391 | if (im->type == i_palette_type) { | |
392 | colors_used = i_colorcount(im); | |
393 | offset += 4 * colors_used; | |
394 | } | |
395 | ||
8d14daab TC |
396 | if (!write_packed(ig, "CCVvvVVVVvvVVVVVV", 'B', 'M', |
397 | (i_upacked_t)(data_size+offset), | |
398 | (i_upacked_t)0, (i_upacked_t)0, (i_upacked_t)offset, | |
399 | (i_upacked_t)INFOHEAD_SIZE, (i_upacked_t)im->xsize, | |
400 | (i_upacked_t)im->ysize, (i_upacked_t)1, | |
401 | (i_upacked_t)bit_count, (i_upacked_t)BI_RGB, | |
402 | (i_upacked_t)data_size, | |
403 | (i_upacked_t)(xres+0.5), (i_upacked_t)(yres+0.5), | |
404 | (i_upacked_t)colors_used, (i_upacked_t)colors_used)){ | |
261f91c5 TC |
405 | i_push_error(0, "cannot write bmp header"); |
406 | return 0; | |
407 | } | |
408 | if (im->type == i_palette_type) { | |
409 | int i; | |
410 | i_color c; | |
411 | ||
412 | for (i = 0; i < colors_used; ++i) { | |
413 | i_getcolors(im, i, &c, 1); | |
414 | if (im->channels >= 3) { | |
8d14daab TC |
415 | if (!write_packed(ig, "CCCC", (i_upacked_t)(c.channel[2]), |
416 | (i_upacked_t)(c.channel[1]), | |
417 | (i_upacked_t)(c.channel[0]), (i_upacked_t)0)) { | |
261f91c5 TC |
418 | i_push_error(0, "cannot write palette entry"); |
419 | return 0; | |
420 | } | |
421 | } | |
422 | else { | |
8d14daab TC |
423 | i_upacked_t v = c.channel[0]; |
424 | if (!write_packed(ig, "CCCC", v, v, v, 0)) { | |
261f91c5 TC |
425 | i_push_error(0, "cannot write palette entry"); |
426 | return 0; | |
427 | } | |
428 | } | |
429 | } | |
430 | } | |
431 | ||
432 | return 1; | |
433 | } | |
434 | ||
705fd961 TC |
435 | /* |
436 | =item write_1bit_data(ig, im) | |
437 | ||
438 | Writes the image data as a 1-bit/pixel image. | |
439 | ||
440 | Returns non-zero on success. | |
441 | ||
442 | =cut | |
443 | */ | |
261f91c5 TC |
444 | static int |
445 | write_1bit_data(io_glue *ig, i_img *im) { | |
446 | i_palidx *line; | |
447 | unsigned char *packed; | |
448 | int byte; | |
449 | int mask; | |
450 | unsigned char *out; | |
451 | int line_size = (im->xsize+7) / 8; | |
452 | int x, y; | |
f0960b14 | 453 | int unpacked_size; |
261f91c5 TC |
454 | |
455 | /* round up to nearest multiple of four */ | |
456 | line_size = (line_size + 3) / 4 * 4; | |
457 | ||
458 | if (!write_bmphead(ig, im, 1, line_size * im->ysize)) | |
459 | return 0; | |
460 | ||
f0960b14 TC |
461 | /* this shouldn't be an issue, but let's be careful */ |
462 | unpacked_size = im->xsize + 8; | |
463 | if (unpacked_size < im->xsize) { | |
464 | i_push_error(0, "integer overflow during memory allocation"); | |
465 | return 0; | |
466 | } | |
467 | line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ | |
261f91c5 | 468 | memset(line + im->xsize, 0, 8); |
f0960b14 TC |
469 | |
470 | /* size allocated here is always much smaller than xsize, hence | |
471 | can't overflow int */ | |
472 | packed = mymalloc(line_size); /* checked 29jun05 tonyc */ | |
261f91c5 TC |
473 | memset(packed, 0, line_size); |
474 | ||
475 | for (y = im->ysize-1; y >= 0; --y) { | |
476 | i_gpal(im, 0, im->xsize, y, line); | |
477 | mask = 0x80; | |
478 | byte = 0; | |
479 | out = packed; | |
480 | for (x = 0; x < im->xsize; ++x) { | |
481 | if (line[x]) | |
482 | byte |= mask; | |
483 | if ((mask >>= 1) == 0) { | |
484 | *out++ = byte; | |
485 | byte = 0; | |
486 | mask = 0x80; | |
487 | } | |
488 | } | |
489 | if (mask != 0x80) { | |
490 | *out++ = byte; | |
491 | } | |
6d5c85a2 | 492 | if (i_io_write(ig, packed, line_size) < 0) { |
261f91c5 TC |
493 | myfree(packed); |
494 | myfree(line); | |
495 | i_push_error(0, "writing 1 bit/pixel packed data"); | |
496 | return 0; | |
497 | } | |
498 | } | |
499 | myfree(packed); | |
500 | myfree(line); | |
501 | ||
6d5c85a2 TC |
502 | if (i_io_close(ig)) |
503 | return 0; | |
10461f9a | 504 | |
261f91c5 TC |
505 | return 1; |
506 | } | |
507 | ||
705fd961 TC |
508 | /* |
509 | =item write_4bit_data(ig, im) | |
510 | ||
511 | Writes the image data as a 4-bit/pixel image. | |
512 | ||
513 | Returns non-zero on success. | |
514 | ||
515 | =cut | |
516 | */ | |
261f91c5 TC |
517 | static int |
518 | write_4bit_data(io_glue *ig, i_img *im) { | |
519 | i_palidx *line; | |
520 | unsigned char *packed; | |
521 | unsigned char *out; | |
522 | int line_size = (im->xsize+1) / 2; | |
523 | int x, y; | |
f0960b14 | 524 | int unpacked_size; |
261f91c5 TC |
525 | |
526 | /* round up to nearest multiple of four */ | |
527 | line_size = (line_size + 3) / 4 * 4; | |
528 | ||
529 | if (!write_bmphead(ig, im, 4, line_size * im->ysize)) | |
530 | return 0; | |
531 | ||
f0960b14 TC |
532 | /* this shouldn't be an issue, but let's be careful */ |
533 | unpacked_size = im->xsize + 2; | |
534 | if (unpacked_size < im->xsize) { | |
535 | i_push_error(0, "integer overflow during memory allocation"); | |
536 | return 0; | |
537 | } | |
538 | line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ | |
261f91c5 TC |
539 | memset(line + im->xsize, 0, 2); |
540 | ||
f0960b14 TC |
541 | /* size allocated here is always much smaller than xsize, hence |
542 | can't overflow int */ | |
543 | packed = mymalloc(line_size); /* checked 29jun05 tonyc */ | |
261f91c5 TC |
544 | memset(packed, 0, line_size); |
545 | ||
546 | for (y = im->ysize-1; y >= 0; --y) { | |
547 | i_gpal(im, 0, im->xsize, y, line); | |
548 | out = packed; | |
549 | for (x = 0; x < im->xsize; x += 2) { | |
550 | *out++ = (line[x] << 4) + line[x+1]; | |
551 | } | |
6d5c85a2 | 552 | if (i_io_write(ig, packed, line_size) < 0) { |
261f91c5 TC |
553 | myfree(packed); |
554 | myfree(line); | |
555 | i_push_error(0, "writing 4 bit/pixel packed data"); | |
556 | return 0; | |
557 | } | |
558 | } | |
559 | myfree(packed); | |
560 | myfree(line); | |
561 | ||
6d5c85a2 TC |
562 | if (i_io_close(ig)) |
563 | return 0; | |
10461f9a | 564 | |
261f91c5 TC |
565 | return 1; |
566 | } | |
567 | ||
705fd961 TC |
568 | /* |
569 | =item write_8bit_data(ig, im) | |
570 | ||
571 | Writes the image data as a 8-bit/pixel image. | |
572 | ||
573 | Returns non-zero on success. | |
574 | ||
575 | =cut | |
576 | */ | |
261f91c5 TC |
577 | static int |
578 | write_8bit_data(io_glue *ig, i_img *im) { | |
579 | i_palidx *line; | |
580 | int line_size = im->xsize; | |
a659442a | 581 | int y; |
f0960b14 | 582 | int unpacked_size; |
261f91c5 TC |
583 | |
584 | /* round up to nearest multiple of four */ | |
585 | line_size = (line_size + 3) / 4 * 4; | |
586 | ||
587 | if (!write_bmphead(ig, im, 8, line_size * im->ysize)) | |
588 | return 0; | |
589 | ||
f0960b14 TC |
590 | /* this shouldn't be an issue, but let's be careful */ |
591 | unpacked_size = im->xsize + 4; | |
592 | if (unpacked_size < im->xsize) { | |
593 | i_push_error(0, "integer overflow during memory allocation"); | |
594 | return 0; | |
595 | } | |
596 | line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ | |
261f91c5 TC |
597 | memset(line + im->xsize, 0, 4); |
598 | ||
599 | for (y = im->ysize-1; y >= 0; --y) { | |
600 | i_gpal(im, 0, im->xsize, y, line); | |
6d5c85a2 | 601 | if (i_io_write(ig, line, line_size) < 0) { |
261f91c5 TC |
602 | myfree(line); |
603 | i_push_error(0, "writing 8 bit/pixel packed data"); | |
604 | return 0; | |
605 | } | |
606 | } | |
607 | myfree(line); | |
608 | ||
6d5c85a2 TC |
609 | if (i_io_close(ig)) |
610 | return 0; | |
10461f9a | 611 | |
261f91c5 TC |
612 | return 1; |
613 | } | |
614 | ||
705fd961 TC |
615 | /* |
616 | =item write_24bit_data(ig, im) | |
617 | ||
618 | Writes the image data as a 24-bit/pixel image. | |
619 | ||
620 | Returns non-zero on success. | |
621 | ||
622 | =cut | |
623 | */ | |
261f91c5 TC |
624 | static int |
625 | write_24bit_data(io_glue *ig, i_img *im) { | |
261f91c5 | 626 | unsigned char *samples; |
a659442a | 627 | int y; |
261f91c5 | 628 | int line_size = 3 * im->xsize; |
07d9c639 TC |
629 | i_color bg; |
630 | ||
631 | i_get_file_background(im, &bg); | |
f0960b14 TC |
632 | |
633 | /* just in case we implement a direct format with 2bytes/pixel | |
634 | (unlikely though) */ | |
635 | if (line_size / 3 != im->xsize) { | |
636 | i_push_error(0, "integer overflow during memory allocation"); | |
637 | return 0; | |
638 | } | |
261f91c5 TC |
639 | |
640 | line_size = (line_size + 3) / 4 * 4; | |
641 | ||
642 | if (!write_bmphead(ig, im, 24, line_size * im->ysize)) | |
643 | return 0; | |
07d9c639 | 644 | samples = mymalloc(4 * im->xsize); |
10461f9a | 645 | memset(samples, 0, line_size); |
261f91c5 | 646 | for (y = im->ysize-1; y >= 0; --y) { |
07d9c639 TC |
647 | unsigned char *samplep = samples; |
648 | int x; | |
649 | i_gsamp_bg(im, 0, im->xsize, y, samples, 3, &bg); | |
650 | for (x = 0; x < im->xsize; ++x) { | |
651 | unsigned char tmp = samplep[2]; | |
652 | samplep[2] = samplep[0]; | |
653 | samplep[0] = tmp; | |
654 | samplep += 3; | |
655 | } | |
6d5c85a2 | 656 | if (i_io_write(ig, samples, line_size) < 0) { |
261f91c5 TC |
657 | i_push_error(0, "writing image data"); |
658 | myfree(samples); | |
659 | return 0; | |
660 | } | |
661 | } | |
662 | myfree(samples); | |
663 | ||
6d5c85a2 TC |
664 | if (i_io_close(ig)) |
665 | return 0; | |
10461f9a | 666 | |
261f91c5 TC |
667 | return 1; |
668 | } | |
669 | ||
705fd961 TC |
670 | /* |
671 | =item read_bmp_pal(ig, im, count) | |
261f91c5 | 672 | |
705fd961 | 673 | Reads count palette entries from the file and add them to the image. |
261f91c5 | 674 | |
705fd961 | 675 | Returns non-zero on success. |
261f91c5 | 676 | |
705fd961 TC |
677 | =cut |
678 | */ | |
261f91c5 TC |
679 | static int |
680 | read_bmp_pal(io_glue *ig, i_img *im, int count) { | |
681 | int i; | |
8d14daab | 682 | i_packed_t r, g, b, x; |
261f91c5 TC |
683 | i_color c; |
684 | ||
685 | for (i = 0; i < count; ++i) { | |
686 | if (!read_packed(ig, "CCCC", &b, &g, &r, &x)) { | |
687 | i_push_error(0, "reading BMP palette"); | |
688 | return 0; | |
689 | } | |
690 | c.channel[0] = r; | |
691 | c.channel[1] = g; | |
692 | c.channel[2] = b; | |
662e3c02 TC |
693 | if (i_addcolors(im, &c, 1) < 0) { |
694 | i_push_error(0, "out of space in image palette"); | |
261f91c5 | 695 | return 0; |
662e3c02 | 696 | } |
261f91c5 TC |
697 | } |
698 | ||
699 | return 1; | |
700 | } | |
701 | ||
705fd961 | 702 | /* |
403946c6 | 703 | =item read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits) |
705fd961 TC |
704 | |
705 | Reads in the palette and image data for a 1-bit/pixel image. | |
706 | ||
707 | Returns the image or NULL. | |
708 | ||
709 | =cut | |
710 | */ | |
261f91c5 | 711 | static i_img * |
662e3c02 | 712 | read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, |
d87dc9a4 | 713 | int compression, long offbits, int allow_incomplete) { |
261f91c5 | 714 | i_img *im; |
9c106321 | 715 | int x, y, lasty, yinc, start_y; |
261f91c5 TC |
716 | i_palidx *line, *p; |
717 | unsigned char *packed; | |
718 | int line_size = (xsize + 7)/8; | |
a659442a | 719 | int bit; |
261f91c5 | 720 | unsigned char *in; |
403946c6 | 721 | long base_offset; |
261f91c5 | 722 | |
662e3c02 TC |
723 | if (compression != BI_RGB) { |
724 | i_push_errorf(0, "unknown 1-bit BMP compression (%d)", compression); | |
725 | return NULL; | |
726 | } | |
727 | ||
f0960b14 TC |
728 | if (xsize + 8 < xsize) { /* if there was overflow */ |
729 | /* we check with 8 because we allocate that much for the decoded | |
730 | line buffer */ | |
731 | i_push_error(0, "integer overflow during memory allocation"); | |
732 | return NULL; | |
733 | } | |
734 | ||
735 | /* if xsize+7 is ok then (xsize+7)/8 will be and the minor | |
736 | adjustments below won't make it overflow */ | |
261f91c5 TC |
737 | line_size = (line_size+3) / 4 * 4; |
738 | ||
739 | if (ysize > 0) { | |
9c106321 | 740 | start_y = ysize-1; |
261f91c5 TC |
741 | lasty = -1; |
742 | yinc = -1; | |
743 | } | |
744 | else { | |
745 | /* when ysize is -ve it's a top-down image */ | |
746 | ysize = -ysize; | |
9c106321 | 747 | start_y = 0; |
261f91c5 TC |
748 | lasty = ysize; |
749 | yinc = 1; | |
750 | } | |
9c106321 | 751 | y = start_y; |
705fd961 TC |
752 | if (!clr_used) |
753 | clr_used = 2; | |
662e3c02 TC |
754 | if (clr_used < 0 || clr_used > 2) { |
755 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
756 | return NULL; | |
757 | } | |
403946c6 TC |
758 | |
759 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; | |
760 | if (offbits < base_offset) { | |
761 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
762 | return NULL; | |
763 | } | |
764 | ||
662e3c02 TC |
765 | im = i_img_pal_new(xsize, ysize, 3, 256); |
766 | if (!im) | |
767 | return NULL; | |
261f91c5 TC |
768 | if (!read_bmp_pal(ig, im, clr_used)) { |
769 | i_img_destroy(im); | |
770 | return NULL; | |
771 | } | |
403946c6 TC |
772 | |
773 | if (offbits > base_offset) { | |
774 | /* this will be slow if the offset is large, but that should be | |
775 | rare */ | |
776 | char buffer; | |
777 | while (base_offset < offbits) { | |
6d5c85a2 | 778 | if (i_io_read(ig, &buffer, 1) != 1) { |
403946c6 TC |
779 | i_img_destroy(im); |
780 | i_push_error(0, "failed skipping to image data offset"); | |
781 | return NULL; | |
782 | } | |
783 | ++base_offset; | |
784 | } | |
785 | } | |
662e3c02 TC |
786 | |
787 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); | |
261f91c5 | 788 | |
f0960b14 TC |
789 | packed = mymalloc(line_size); /* checked 29jun05 tonyc */ |
790 | line = mymalloc(xsize+8); /* checked 29jun05 tonyc */ | |
261f91c5 | 791 | while (y != lasty) { |
6d5c85a2 | 792 | if (i_io_read(ig, packed, line_size) != line_size) { |
261f91c5 TC |
793 | myfree(packed); |
794 | myfree(line); | |
d87dc9a4 | 795 | if (allow_incomplete) { |
9c106321 TC |
796 | i_tags_setn(&im->tags, "i_incomplete", 1); |
797 | i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y)); | |
798 | return im; | |
799 | } | |
800 | else { | |
801 | i_push_error(0, "failed reading 1-bit bmp data"); | |
802 | i_img_destroy(im); | |
803 | return NULL; | |
804 | } | |
261f91c5 TC |
805 | } |
806 | in = packed; | |
807 | bit = 0x80; | |
808 | p = line; | |
809 | for (x = 0; x < xsize; ++x) { | |
810 | *p++ = (*in & bit) ? 1 : 0; | |
811 | bit >>= 1; | |
812 | if (!bit) { | |
813 | ++in; | |
814 | bit = 0x80; | |
815 | } | |
816 | } | |
817 | i_ppal(im, 0, xsize, y, line); | |
818 | y += yinc; | |
819 | } | |
820 | ||
4b0f812c AMH |
821 | myfree(packed); |
822 | myfree(line); | |
261f91c5 TC |
823 | return im; |
824 | } | |
261f91c5 | 825 | |
705fd961 TC |
826 | /* |
827 | =item read_4bit_bmp(ig, xsize, ysize, clr_used, compression) | |
828 | ||
829 | Reads in the palette and image data for a 4-bit/pixel image. | |
830 | ||
831 | Returns the image or NULL. | |
832 | ||
833 | Hopefully this will be combined with the following function at some | |
834 | point. | |
835 | ||
836 | =cut | |
837 | */ | |
261f91c5 | 838 | static i_img * |
705fd961 | 839 | read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, |
d87dc9a4 | 840 | int compression, long offbits, int allow_incomplete) { |
261f91c5 TC |
841 | i_img *im; |
842 | int x, y, lasty, yinc; | |
843 | i_palidx *line, *p; | |
844 | unsigned char *packed; | |
845 | int line_size = (xsize + 1)/2; | |
846 | unsigned char *in; | |
705fd961 | 847 | int size, i; |
403946c6 | 848 | long base_offset; |
9c106321 | 849 | int starty; |
261f91c5 | 850 | |
f0960b14 TC |
851 | /* line_size is going to be smaller than xsize in most cases (and |
852 | when it's not, xsize is itself small), and hence not overflow */ | |
261f91c5 TC |
853 | line_size = (line_size+3) / 4 * 4; |
854 | ||
855 | if (ysize > 0) { | |
9c106321 | 856 | starty = ysize-1; |
261f91c5 TC |
857 | lasty = -1; |
858 | yinc = -1; | |
859 | } | |
860 | else { | |
861 | /* when ysize is -ve it's a top-down image */ | |
862 | ysize = -ysize; | |
9c106321 | 863 | starty = 0; |
261f91c5 TC |
864 | lasty = ysize; |
865 | yinc = 1; | |
866 | } | |
9c106321 | 867 | y = starty; |
705fd961 TC |
868 | if (!clr_used) |
869 | clr_used = 16; | |
662e3c02 TC |
870 | |
871 | if (clr_used > 16 || clr_used < 0) { | |
872 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
873 | return NULL; | |
874 | } | |
875 | ||
403946c6 TC |
876 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; |
877 | if (offbits < base_offset) { | |
878 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
879 | return NULL; | |
880 | } | |
881 | ||
662e3c02 TC |
882 | im = i_img_pal_new(xsize, ysize, 3, 256); |
883 | if (!im) /* error should have been pushed already */ | |
884 | return NULL; | |
261f91c5 TC |
885 | if (!read_bmp_pal(ig, im, clr_used)) { |
886 | i_img_destroy(im); | |
887 | return NULL; | |
888 | } | |
889 | ||
403946c6 TC |
890 | if (offbits > base_offset) { |
891 | /* this will be slow if the offset is large, but that should be | |
892 | rare */ | |
893 | char buffer; | |
894 | while (base_offset < offbits) { | |
6d5c85a2 | 895 | if (i_io_read(ig, &buffer, 1) != 1) { |
403946c6 TC |
896 | i_img_destroy(im); |
897 | i_push_error(0, "failed skipping to image data offset"); | |
898 | return NULL; | |
899 | } | |
900 | ++base_offset; | |
901 | } | |
902 | } | |
903 | ||
705fd961 | 904 | if (line_size < 260) |
f0960b14 | 905 | packed = mymalloc(260); /* checked 29jun05 tonyc */ |
705fd961 | 906 | else |
f0960b14 TC |
907 | packed = mymalloc(line_size); /* checked 29jun05 tonyc */ |
908 | /* xsize won't approach MAXINT */ | |
909 | line = mymalloc(xsize+1); /* checked 29jun05 tonyc */ | |
261f91c5 | 910 | if (compression == BI_RGB) { |
662e3c02 | 911 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); |
261f91c5 | 912 | while (y != lasty) { |
6d5c85a2 | 913 | if (i_io_read(ig, packed, line_size) != line_size) { |
261f91c5 TC |
914 | myfree(packed); |
915 | myfree(line); | |
d87dc9a4 | 916 | if (allow_incomplete) { |
9c106321 TC |
917 | i_tags_setn(&im->tags, "i_incomplete", 1); |
918 | i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); | |
919 | return im; | |
920 | } | |
921 | else { | |
922 | i_push_error(0, "failed reading 4-bit bmp data"); | |
923 | i_img_destroy(im); | |
924 | return NULL; | |
925 | } | |
261f91c5 TC |
926 | } |
927 | in = packed; | |
928 | p = line; | |
929 | for (x = 0; x < xsize; x+=2) { | |
930 | *p++ = *in >> 4; | |
931 | *p++ = *in & 0x0F; | |
932 | ++in; | |
933 | } | |
934 | i_ppal(im, 0, xsize, y, line); | |
935 | y += yinc; | |
936 | } | |
4b0f812c AMH |
937 | myfree(packed); |
938 | myfree(line); | |
261f91c5 TC |
939 | } |
940 | else if (compression == BI_RLE4) { | |
705fd961 | 941 | int read_size; |
705fd961 TC |
942 | int count; |
943 | ||
662e3c02 | 944 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RLE4", -1, 0); |
705fd961 TC |
945 | x = 0; |
946 | while (1) { | |
947 | /* there's always at least 2 bytes in a sequence */ | |
6d5c85a2 | 948 | if (i_io_read(ig, packed, 2) != 2) { |
705fd961 TC |
949 | myfree(packed); |
950 | myfree(line); | |
d87dc9a4 | 951 | if (allow_incomplete) { |
9c106321 TC |
952 | i_tags_setn(&im->tags, "i_incomplete", 1); |
953 | i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); | |
954 | return im; | |
955 | } | |
956 | else { | |
957 | i_push_error(0, "missing data during decompression"); | |
958 | i_img_destroy(im); | |
959 | return NULL; | |
960 | } | |
705fd961 TC |
961 | } |
962 | else if (packed[0]) { | |
4be2df8a TC |
963 | int count = packed[0]; |
964 | if (x + count > xsize) { | |
bb5712de | 965 | /* this file is corrupt */ |
02576e8d | 966 | myfree(packed); |
bb5712de TC |
967 | myfree(line); |
968 | i_push_error(0, "invalid data during decompression"); | |
4be2df8a TC |
969 | mm_log((1, "read 4-bit: scanline overflow x %d + count %d vs xsize %d (y %d)\n", |
970 | (int)x, count, (int)xsize, (int)y)); | |
bb5712de TC |
971 | i_img_destroy(im); |
972 | return NULL; | |
973 | } | |
705fd961 TC |
974 | line[0] = packed[1] >> 4; |
975 | line[1] = packed[1] & 0x0F; | |
4be2df8a TC |
976 | for (i = 0; i < count; i += 2) { |
977 | if (i < count-1) | |
705fd961 TC |
978 | i_ppal(im, x, x+2, y, line); |
979 | else | |
4be2df8a | 980 | i_ppal(im, x, x+(count-i), y, line); |
705fd961 TC |
981 | x += 2; |
982 | } | |
983 | } else { | |
984 | switch (packed[1]) { | |
985 | case BMPRLE_ENDOFLINE: | |
986 | x = 0; | |
987 | y += yinc; | |
988 | break; | |
989 | ||
990 | case BMPRLE_ENDOFBMP: | |
4dfa5522 AMH |
991 | myfree(packed); |
992 | myfree(line); | |
705fd961 TC |
993 | return im; |
994 | ||
995 | case BMPRLE_DELTA: | |
6d5c85a2 | 996 | if (i_io_read(ig, packed, 2) != 2) { |
705fd961 TC |
997 | myfree(packed); |
998 | myfree(line); | |
d87dc9a4 | 999 | if (allow_incomplete) { |
9c106321 TC |
1000 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1001 | i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); | |
1002 | return im; | |
1003 | } | |
1004 | else { | |
1005 | i_push_error(0, "missing data during decompression"); | |
1006 | i_img_destroy(im); | |
1007 | return NULL; | |
1008 | } | |
705fd961 TC |
1009 | } |
1010 | x += packed[0]; | |
1011 | y += yinc * packed[1]; | |
1012 | break; | |
1013 | ||
1014 | default: | |
1015 | count = packed[1]; | |
bb5712de TC |
1016 | if (x + count > xsize) { |
1017 | /* this file is corrupt */ | |
02576e8d | 1018 | myfree(packed); |
bb5712de TC |
1019 | myfree(line); |
1020 | i_push_error(0, "invalid data during decompression"); | |
1021 | i_img_destroy(im); | |
1022 | return NULL; | |
1023 | } | |
705fd961 TC |
1024 | size = (count + 1) / 2; |
1025 | read_size = (size+1) / 2 * 2; | |
6d5c85a2 | 1026 | if (i_io_read(ig, packed, read_size) != read_size) { |
705fd961 TC |
1027 | myfree(packed); |
1028 | myfree(line); | |
d87dc9a4 | 1029 | if (allow_incomplete) { |
9c106321 TC |
1030 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1031 | i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); | |
1032 | return im; | |
1033 | } | |
1034 | else { | |
1035 | i_push_error(0, "missing data during decompression"); | |
1036 | i_img_destroy(im); | |
1037 | return NULL; | |
1038 | } | |
705fd961 TC |
1039 | } |
1040 | for (i = 0; i < size; ++i) { | |
1041 | line[0] = packed[i] >> 4; | |
1042 | line[1] = packed[i] & 0xF; | |
1043 | i_ppal(im, x, x+2, y, line); | |
1044 | x += 2; | |
1045 | } | |
1046 | break; | |
1047 | } | |
1048 | } | |
1049 | } | |
1050 | } | |
1051 | else { /*if (compression == BI_RLE4) {*/ | |
1052 | myfree(packed); | |
1053 | myfree(line); | |
662e3c02 | 1054 | i_push_errorf(0, "unknown 4-bit BMP compression (%d)", compression); |
705fd961 TC |
1055 | i_img_destroy(im); |
1056 | return NULL; | |
261f91c5 TC |
1057 | } |
1058 | ||
1059 | return im; | |
1060 | } | |
1061 | ||
705fd961 | 1062 | /* |
d87dc9a4 | 1063 | =item read_8bit_bmp(ig, xsize, ysize, clr_used, compression, allow_incomplete) |
261f91c5 | 1064 | |
705fd961 TC |
1065 | Reads in the palette and image data for a 8-bit/pixel image. |
1066 | ||
1067 | Returns the image or NULL. | |
1068 | ||
1069 | =cut | |
1070 | */ | |
1071 | static i_img * | |
1072 | read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, | |
d87dc9a4 | 1073 | int compression, long offbits, int allow_incomplete) { |
261f91c5 | 1074 | i_img *im; |
9c106321 | 1075 | int x, y, lasty, yinc, start_y; |
a659442a | 1076 | i_palidx *line; |
705fd961 | 1077 | int line_size = xsize; |
403946c6 | 1078 | long base_offset; |
261f91c5 | 1079 | |
705fd961 | 1080 | line_size = (line_size+3) / 4 * 4; |
f0960b14 TC |
1081 | if (line_size < xsize) { /* if it overflowed (unlikely, but check) */ |
1082 | i_push_error(0, "integer overflow during memory allocation"); | |
1083 | return NULL; | |
1084 | } | |
705fd961 TC |
1085 | |
1086 | if (ysize > 0) { | |
9c106321 | 1087 | start_y = ysize-1; |
705fd961 TC |
1088 | lasty = -1; |
1089 | yinc = -1; | |
261f91c5 | 1090 | } |
705fd961 TC |
1091 | else { |
1092 | /* when ysize is -ve it's a top-down image */ | |
1093 | ysize = -ysize; | |
9c106321 | 1094 | start_y = 0; |
705fd961 TC |
1095 | lasty = ysize; |
1096 | yinc = 1; | |
261f91c5 | 1097 | } |
9c106321 | 1098 | y = start_y; |
705fd961 TC |
1099 | if (!clr_used) |
1100 | clr_used = 256; | |
662e3c02 TC |
1101 | if (clr_used > 256 || clr_used < 0) { |
1102 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
1103 | return NULL; | |
1104 | } | |
1105 | ||
403946c6 TC |
1106 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; |
1107 | if (offbits < base_offset) { | |
1108 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
1109 | return NULL; | |
1110 | } | |
1111 | ||
662e3c02 TC |
1112 | im = i_img_pal_new(xsize, ysize, 3, 256); |
1113 | if (!im) | |
1114 | return NULL; | |
705fd961 TC |
1115 | if (!read_bmp_pal(ig, im, clr_used)) { |
1116 | i_img_destroy(im); | |
1117 | return NULL; | |
1118 | } | |
1119 | ||
403946c6 TC |
1120 | if (offbits > base_offset) { |
1121 | /* this will be slow if the offset is large, but that should be | |
1122 | rare */ | |
1123 | char buffer; | |
1124 | while (base_offset < offbits) { | |
6d5c85a2 | 1125 | if (i_io_read(ig, &buffer, 1) != 1) { |
403946c6 TC |
1126 | i_img_destroy(im); |
1127 | i_push_error(0, "failed skipping to image data offset"); | |
1128 | return NULL; | |
1129 | } | |
1130 | ++base_offset; | |
1131 | } | |
1132 | } | |
1133 | ||
f0960b14 | 1134 | line = mymalloc(line_size); /* checked 29jun05 tonyc */ |
705fd961 | 1135 | if (compression == BI_RGB) { |
662e3c02 | 1136 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); |
705fd961 | 1137 | while (y != lasty) { |
6d5c85a2 | 1138 | if (i_io_read(ig, line, line_size) != line_size) { |
705fd961 | 1139 | myfree(line); |
d87dc9a4 | 1140 | if (allow_incomplete) { |
9c106321 TC |
1141 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1142 | i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y)); | |
1143 | return im; | |
1144 | } | |
1145 | else { | |
1146 | i_push_error(0, "failed reading 8-bit bmp data"); | |
1147 | i_img_destroy(im); | |
1148 | return NULL; | |
1149 | } | |
705fd961 TC |
1150 | } |
1151 | i_ppal(im, 0, xsize, y, line); | |
1152 | y += yinc; | |
1153 | } | |
12d25826 | 1154 | myfree(line); |
705fd961 TC |
1155 | } |
1156 | else if (compression == BI_RLE8) { | |
1157 | int read_size; | |
705fd961 TC |
1158 | int count; |
1159 | unsigned char packed[2]; | |
1160 | ||
662e3c02 | 1161 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RLE8", -1, 0); |
705fd961 TC |
1162 | x = 0; |
1163 | while (1) { | |
1164 | /* there's always at least 2 bytes in a sequence */ | |
6d5c85a2 | 1165 | if (i_io_read(ig, packed, 2) != 2) { |
705fd961 | 1166 | myfree(line); |
d87dc9a4 | 1167 | if (allow_incomplete) { |
9c106321 TC |
1168 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1169 | i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); | |
1170 | return im; | |
1171 | } | |
1172 | else { | |
1173 | i_push_error(0, "missing data during decompression"); | |
1174 | i_img_destroy(im); | |
1175 | return NULL; | |
1176 | } | |
705fd961 TC |
1177 | } |
1178 | if (packed[0]) { | |
bb5712de TC |
1179 | if (x + packed[0] > xsize) { |
1180 | /* this file isn't incomplete, it's corrupt */ | |
1181 | myfree(line); | |
1182 | i_push_error(0, "invalid data during decompression"); | |
1183 | i_img_destroy(im); | |
1184 | return NULL; | |
1185 | } | |
705fd961 TC |
1186 | memset(line, packed[1], packed[0]); |
1187 | i_ppal(im, x, x+packed[0], y, line); | |
1188 | x += packed[0]; | |
1189 | } else { | |
1190 | switch (packed[1]) { | |
1191 | case BMPRLE_ENDOFLINE: | |
1192 | x = 0; | |
1193 | y += yinc; | |
1194 | break; | |
1195 | ||
1196 | case BMPRLE_ENDOFBMP: | |
4dfa5522 | 1197 | myfree(line); |
705fd961 TC |
1198 | return im; |
1199 | ||
1200 | case BMPRLE_DELTA: | |
6d5c85a2 | 1201 | if (i_io_read(ig, packed, 2) != 2) { |
705fd961 | 1202 | myfree(line); |
d87dc9a4 | 1203 | if (allow_incomplete) { |
9c106321 TC |
1204 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1205 | i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); | |
1206 | return im; | |
1207 | } | |
1208 | else { | |
1209 | i_push_error(0, "missing data during decompression"); | |
1210 | i_img_destroy(im); | |
1211 | return NULL; | |
1212 | } | |
705fd961 TC |
1213 | } |
1214 | x += packed[0]; | |
1215 | y += yinc * packed[1]; | |
1216 | break; | |
1217 | ||
1218 | default: | |
1219 | count = packed[1]; | |
bb5712de TC |
1220 | if (x + count > xsize) { |
1221 | /* runs shouldn't cross a line boundary */ | |
1222 | /* this file isn't incomplete, it's corrupt */ | |
1223 | myfree(line); | |
1224 | i_push_error(0, "invalid data during decompression"); | |
1225 | i_img_destroy(im); | |
1226 | return NULL; | |
1227 | } | |
705fd961 | 1228 | read_size = (count+1) / 2 * 2; |
6d5c85a2 | 1229 | if (i_io_read(ig, line, read_size) != read_size) { |
705fd961 | 1230 | myfree(line); |
d87dc9a4 | 1231 | if (allow_incomplete) { |
9c106321 TC |
1232 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1233 | i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); | |
1234 | return im; | |
1235 | } | |
1236 | else { | |
1237 | i_push_error(0, "missing data during decompression"); | |
1238 | i_img_destroy(im); | |
1239 | return NULL; | |
1240 | } | |
705fd961 TC |
1241 | } |
1242 | i_ppal(im, x, x+count, y, line); | |
1243 | x += count; | |
1244 | break; | |
1245 | } | |
1246 | } | |
1247 | } | |
1248 | } | |
1249 | else { | |
1250 | myfree(line); | |
662e3c02 | 1251 | i_push_errorf(0, "unknown 8-bit BMP compression (%d)", compression); |
705fd961 TC |
1252 | i_img_destroy(im); |
1253 | return NULL; | |
1254 | } | |
1255 | ||
1256 | return im; | |
1257 | } | |
1258 | ||
1259 | struct bm_masks { | |
1260 | unsigned masks[3]; | |
1261 | int shifts[3]; | |
1cd4eef3 | 1262 | int bits[3]; |
705fd961 TC |
1263 | }; |
1264 | static struct bm_masks std_masks[] = | |
1265 | { | |
1266 | { /* 16-bit */ | |
1cd4eef3 TC |
1267 | { 0076000, 00001740, 00000037, }, |
1268 | { 10, 5, 0, }, | |
1269 | { 5, 5, 5, } | |
705fd961 TC |
1270 | }, |
1271 | { /* 24-bit */ | |
1272 | { 0xFF0000, 0x00FF00, 0x0000FF, }, | |
1273 | { 16, 8, 0, }, | |
1cd4eef3 | 1274 | { 8, 8, 8, }, |
705fd961 TC |
1275 | }, |
1276 | { /* 32-bit */ | |
1277 | { 0xFF0000, 0x00FF00, 0x0000FF, }, | |
1278 | { 16, 8, 0, }, | |
1cd4eef3 | 1279 | { 8, 8, 8, }, |
705fd961 TC |
1280 | }, |
1281 | }; | |
1282 | ||
1cd4eef3 TC |
1283 | /* multiplier and shift for converting from N bits to 8 bits */ |
1284 | struct bm_sampconverts { | |
1285 | int mult; | |
1286 | int shift; | |
1287 | }; | |
1288 | static struct bm_sampconverts samp_converts[] = { | |
1289 | { 0xff, 0 }, /* 1 bit samples */ | |
1290 | { 0x55, 0 }, | |
1291 | { 0111, 1 }, | |
1292 | { 0x11, 0 }, | |
1293 | { 0x21, 2 }, | |
1294 | { 0x41, 4 }, | |
1295 | { 0x81, 6 } /* 7 bit samples */ | |
1296 | }; | |
1297 | ||
705fd961 | 1298 | /* |
d87dc9a4 | 1299 | =item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression, allow_incomplete) |
705fd961 TC |
1300 | |
1301 | Skips the palette and reads in the image data for a direct colour image. | |
1302 | ||
1303 | Returns the image or NULL. | |
1304 | ||
1305 | =cut | |
1306 | */ | |
1307 | static i_img * | |
1308 | read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, | |
9c106321 | 1309 | int clr_used, int compression, long offbits, |
d87dc9a4 | 1310 | int allow_incomplete) { |
705fd961 | 1311 | i_img *im; |
bea6bcd7 | 1312 | int x, y, starty, lasty, yinc; |
705fd961 | 1313 | i_color *line, *p; |
705fd961 TC |
1314 | int pix_size = bit_count / 8; |
1315 | int line_size = xsize * pix_size; | |
1316 | struct bm_masks masks; | |
1317 | char unpack_code[2] = ""; | |
1318 | int i; | |
1319 | int extras; | |
1320 | char junk[4]; | |
662e3c02 TC |
1321 | const char *compression_name; |
1322 | int bytes; | |
403946c6 | 1323 | long base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE; |
261f91c5 | 1324 | |
705fd961 TC |
1325 | unpack_code[0] = *("v3V"+pix_size-2); |
1326 | unpack_code[1] = '\0'; | |
261f91c5 | 1327 | |
705fd961 TC |
1328 | line_size = (line_size+3) / 4 * 4; |
1329 | extras = line_size - xsize * pix_size; | |
261f91c5 | 1330 | |
705fd961 | 1331 | if (ysize > 0) { |
bea6bcd7 | 1332 | starty = ysize-1; |
705fd961 TC |
1333 | lasty = -1; |
1334 | yinc = -1; | |
1335 | } | |
1336 | else { | |
1337 | /* when ysize is -ve it's a top-down image */ | |
1338 | ysize = -ysize; | |
bea6bcd7 | 1339 | starty = 0; |
705fd961 TC |
1340 | lasty = ysize; |
1341 | yinc = 1; | |
1342 | } | |
bea6bcd7 | 1343 | y = starty; |
705fd961 | 1344 | if (compression == BI_RGB) { |
662e3c02 | 1345 | compression_name = "BI_RGB"; |
705fd961 TC |
1346 | masks = std_masks[pix_size-2]; |
1347 | ||
1348 | /* there's a potential "palette" after the header */ | |
1349 | for (i = 0; i < clr_used; ++clr_used) { | |
1350 | char buf[4]; | |
6d5c85a2 | 1351 | if (i_io_read(ig, buf, 4) != 4) { |
705fd961 TC |
1352 | i_push_error(0, "skipping colors"); |
1353 | return 0; | |
1354 | } | |
403946c6 | 1355 | base_offset += 4; |
705fd961 TC |
1356 | } |
1357 | } | |
1358 | else if (compression == BI_BITFIELDS) { | |
1cd4eef3 TC |
1359 | int pos; |
1360 | unsigned bits; | |
662e3c02 TC |
1361 | compression_name = "BI_BITFIELDS"; |
1362 | ||
705fd961 | 1363 | for (i = 0; i < 3; ++i) { |
8d14daab TC |
1364 | i_packed_t rmask; |
1365 | if (!read_packed(ig, "V", &rmask)) { | |
705fd961 TC |
1366 | i_push_error(0, "reading pixel masks"); |
1367 | return 0; | |
1368 | } | |
1cd4eef3 TC |
1369 | if (rmask == 0) { |
1370 | i_push_errorf(0, "Zero mask for channel %d", i); | |
1371 | return NULL; | |
1372 | } | |
8d14daab | 1373 | masks.masks[i] = rmask; |
705fd961 TC |
1374 | /* work out a shift for the mask */ |
1375 | pos = 0; | |
1cd4eef3 TC |
1376 | bits = masks.masks[i]; |
1377 | while (!(bits & 1)) { | |
705fd961 | 1378 | ++pos; |
1cd4eef3 | 1379 | bits >>= 1; |
705fd961 | 1380 | } |
1cd4eef3 TC |
1381 | masks.shifts[i] = pos; |
1382 | pos = 0; | |
1383 | while (bits & 1) { | |
1384 | ++pos; | |
1385 | bits >>= 1; | |
1386 | } | |
1387 | masks.bits[i] = pos; | |
1388 | /*fprintf(stderr, "%d: mask %08x shift %d bits %d\n", i, masks.masks[i], masks.shifts[i], masks.bits[i]);*/ | |
705fd961 | 1389 | } |
1cd4eef3 TC |
1390 | /* account for the masks */ |
1391 | base_offset += 3 * 4; | |
705fd961 | 1392 | } |
662e3c02 TC |
1393 | else { |
1394 | i_push_errorf(0, "unknown 24-bit BMP compression (%d)", compression); | |
1395 | return NULL; | |
1396 | } | |
261f91c5 | 1397 | |
ae12796a TC |
1398 | if (offbits < base_offset) { |
1399 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
1400 | return NULL; | |
1401 | } | |
1402 | ||
403946c6 TC |
1403 | if (offbits > base_offset) { |
1404 | /* this will be slow if the offset is large, but that should be | |
1405 | rare */ | |
1406 | char buffer; | |
1407 | while (base_offset < offbits) { | |
6d5c85a2 | 1408 | if (i_io_read(ig, &buffer, 1) != 1) { |
403946c6 TC |
1409 | i_push_error(0, "failed skipping to image data offset"); |
1410 | return NULL; | |
1411 | } | |
1412 | ++base_offset; | |
1413 | } | |
1414 | } | |
1415 | ||
705fd961 | 1416 | im = i_img_empty(NULL, xsize, ysize); |
662e3c02 TC |
1417 | if (!im) |
1418 | return NULL; | |
705fd961 | 1419 | |
662e3c02 TC |
1420 | i_tags_add(&im->tags, "bmp_compression_name", 0, compression_name, -1, 0); |
1421 | ||
1422 | /* I wasn't able to make this overflow in testing, but better to be | |
1423 | safe */ | |
1424 | bytes = sizeof(i_color) * xsize; | |
1425 | if (bytes / sizeof(i_color) != xsize) { | |
1426 | i_img_destroy(im); | |
1427 | i_push_error(0, "integer overflow calculating buffer size"); | |
1428 | return NULL; | |
1429 | } | |
f0960b14 | 1430 | line = mymalloc(bytes); /* checked 29jun05 tonyc */ |
705fd961 TC |
1431 | while (y != lasty) { |
1432 | p = line; | |
1433 | for (x = 0; x < xsize; ++x) { | |
8d14daab | 1434 | i_packed_t pixel; |
705fd961 | 1435 | if (!read_packed(ig, unpack_code, &pixel)) { |
705fd961 | 1436 | myfree(line); |
d87dc9a4 | 1437 | if (allow_incomplete) { |
9c106321 | 1438 | i_tags_setn(&im->tags, "i_incomplete", 1); |
bea6bcd7 | 1439 | i_tags_setn(&im->tags, "i_lines_read", abs(starty - y)); |
9c106321 TC |
1440 | return im; |
1441 | } | |
1442 | else { | |
1443 | i_push_error(0, "failed reading image data"); | |
1444 | i_img_destroy(im); | |
1445 | return NULL; | |
1446 | } | |
705fd961 TC |
1447 | } |
1448 | for (i = 0; i < 3; ++i) { | |
1cd4eef3 TC |
1449 | int sample = (pixel & masks.masks[i]) >> masks.shifts[i]; |
1450 | int bits = masks.bits[i]; | |
1451 | if (bits < 8) { | |
1452 | sample = (sample * samp_converts[bits-1].mult) >> samp_converts[bits-1].shift; | |
1453 | } | |
1454 | else if (bits) { | |
1455 | sample >>= bits - 8; | |
1456 | } | |
1457 | p->channel[i] = sample; | |
705fd961 TC |
1458 | } |
1459 | ++p; | |
1460 | } | |
1461 | i_plin(im, 0, xsize, y, line); | |
1462 | if (extras) | |
6d5c85a2 | 1463 | i_io_read(ig, junk, extras); |
705fd961 | 1464 | y += yinc; |
261f91c5 | 1465 | } |
705fd961 TC |
1466 | myfree(line); |
1467 | ||
1468 | return im; | |
261f91c5 | 1469 | } |
705fd961 TC |
1470 | |
1471 | /* | |
1472 | =head1 SEE ALSO | |
1473 | ||
1474 | Imager(3) | |
1475 | ||
1476 | =head1 AUTHOR | |
1477 | ||
1478 | Tony Cook <tony@develop-help.com> | |
1479 | ||
1480 | =head1 RESTRICTIONS | |
1481 | ||
1482 | Cannot save as compressed BMP. | |
1483 | ||
1484 | =head1 BUGS | |
1485 | ||
1486 | Doesn't handle OS/2 bitmaps. | |
1487 | ||
1488 | 16-bit/pixel images haven't been tested. (I need an image). | |
1489 | ||
1490 | BI_BITFIELDS compression hasn't been tested (I need an image). | |
1491 | ||
403946c6 TC |
1492 | The header handling for paletted images needs to be refactored |
1493 | ||
705fd961 TC |
1494 | =cut |
1495 | */ |