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