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